Kế hoạch đầy đủ đưa dự án từ trạng thái hiện tại (parser ~70%, không có CLI, chưa chạy được trong project thật) đến v1.0 ổn định, dùng được trên browser / Node / Bun.
Tham chiếu trạng thái: compatibility.md.
Kiến trúc tokenizer được mô tả tại architecture/tokenizer.md.
Những quyết định này đã chốt trước khi bắt đầu để không phải quay lại bàn trong quá trình thực thi.
- Target runtime: JavaScript (browser + Node + Bun). Không tự viết runtime.
- Codegen: dùng
@babel/generatorđã có sẵn. Không thay thế. - AST: giữ chuẩn Babel-compatible.
- Không làm trong scope v1.0: type system, LSP, formatter, REPL tương tác, tối ưu bytecode. Để dành post-v1.
- File extension:
.vjs(đã dùng). Không đổi.
Tất cả keyword JS phải có bản tiếng Việt. Lần này mình chốt:
| JS | Tiếng Việt mới | Giữ alias tiếng Anh? |
|---|---|---|
let |
biến |
Có (backward compat) |
yield |
nhường |
Có |
this |
đây |
Có |
null |
rỗng |
Có |
instanceof |
là kiểu |
Có |
static |
tĩnh |
Có |
public |
công khai |
Có |
private |
riêng tư |
Có |
protected |
bảo vệ |
Có |
get |
lấy |
Có |
set |
gán |
Có |
of (for...of) |
của |
— |
in (for...in) |
trong |
— |
Infinity |
vô cực |
Có |
async generator * |
giữ * symbol |
— |
Alias tiếng Anh giữ lại vì một số chưa có bản tiếng Việt sạch, và để tránh phá code đã có. Khuyến khích dùng tiếng Việt trong docs/example.
Hiện tại: xin chào → _xin_ch224o (codepoint encoding, xấu, khó debug).
Đổi sang: giữ nguyên Unicode (JS hỗ trợ Unicode identifier từ ES2015), chỉ thay space bằng _:
xin chào → xin_chào
Con Mèo → Con_Mèo
số chân → số_chân
- Ưu: output đọc được, dễ debug, 1-1 mapping.
- Nhược: nếu user có sẵn
xin_chào(có underscore) vàxin chào(có space) thì collision. Xử lý: detect collision tại parse time → throw error rõ ràng. Chấp nhận constraint này, không cố tự resolve. - Fallback cho ký tự thật sự không hợp lệ JS (rất hiếm với tiếng Việt): encode
\u{xxxx}.
Mọi feature mới đều phải có 3 nhóm test trước khi merge:
- Parser positive — parse đúng, AST đúng shape.
- Parser negative — cú pháp sai → throw lỗi tiếng Việt với file:line.
- Codegen — AST → JS string chuẩn, dùng snapshot.
Coverage target: ≥85% statements trên packages/parser/src khi đóng v1.0.
Không làm trong v1.0, dù tiện tay cỡ nào:
- TypeScript type annotations
- JSX
- Decorators
- Pattern matching (proposal)
- Pipeline operator
Các thứ này đáng làm nhưng sẽ phình scope. Ghi vào post-v1 backlog.
9 giai đoạn, thứ tự tuyến tính (mỗi phase phụ thuộc phase trước):
| Phase | Nội dung | Ước lượng effort |
|---|---|---|
| 0 | Thiết lập nền & keyword migration | 3-4 ngày |
| 1 | Cú pháp P1 (arrow, destructuring, spread, template, for-of/in, default param) | 2-3 tuần |
| 2 | Cú pháp P2 (private, getter/setter, regex, logical assign, ...) | 2 tuần |
| 3 | Identifier mangling mới + collision detection | 3-5 ngày |
| 4 | Source map + error messages chất lượng | 1 tuần |
| 5 | CLI (vietscript run/build/watch) + Node loader |
1-2 tuần |
| 6 | Plugin Vite/Webpack chạy thật + Bun support | 1 tuần |
| 7 | Stdlib mỏng (wrappers tiếng Việt cho JS globals) | 3-5 ngày |
| 8 | Docs tái cấu trúc + 1 sample app chạy thật | 1 tuần |
| 9 | Release v1.0 (npm publish + announcement) | 2-3 ngày |
Tổng: ~10-14 tuần làm part-time (hobby). Chạy tuyến tính, không parallel — vì một mình làm.
Mục tiêu: có đủ tooling và quyết định để mọi phase sau chạy mượt.
- Thêm
@vitest/coverage-v8vào devDeps. - Cập nhật vitest.config.ts bật
coverage: { reporter: ['text','html','json-summary'], include: ['packages/*/src/**'], exclude: ['**/__test__/**'] }. - Script:
pnpm test:coverage. - Ghi baseline coverage vào compatibility.md.
- Tạo
.github/workflows/ci.yml. - Chạy:
pnpm install,pnpm lint,pnpm test:coverage,pnpm build. - Upload coverage report.
- Enforce: PR phải pass trước khi merge.
- Thêm các keyword mới ở keyword.enum.ts.
- Thêm mapping ở specs.ts.
- Tokenizer phải accept cả tên mới và alias tiếng Anh (không break code cũ).
- Test: mỗi keyword mới có 1 test parse minimal.
- Ghi
CONTRIBUTING.mdphần "thêm parser node": pattern chuẩn để implementParse()+Nodeclass + test 3 nhóm. - Template file để copy khi thêm node mới.
- Tự động sinh % hoàn thiện từ compatibility.md (parse markdown table, đếm ✅/🟡/❌).
- Hiển thị trong CI summary.
Acceptance:
-
pnpm test:coveragechạy được, ra số. - CI xanh trên PR mẫu.
- 11 keyword mới có trong enum + tokenize được.
-
CONTRIBUTING.mdcó hướng dẫn thêm node.
Mục tiêu: xóa bỏ cảm giác "ngôn ngữ thiếu". Mỗi feature = 1 PR.
Thứ tự đề xuất (ít phụ thuộc → nhiều phụ thuộc):
- File mới:
nodes/expressions/ArrowFunctionExpression.ts. - Parse:
(params) => expr,(params) => { ... },x => x,async () => .... - AST shape: Babel
ArrowFunctionExpressionvớiparams,body,async,expressionflag. - Tính: precedence đặt thấp để không va đụng ternary.
- Mở rộng
ParameterList: sau identifier, nếu gặp=→ wrapAssignmentPattern. - Áp dụng cho function, method, arrow, constructor.
- Trong
ParameterList: nếu bắt đầu bằng...→RestElement. - Chỉ cho phép ở vị trí cuối.
- Error tiếng Việt nếu sai vị trí.
- Trong call args:
f(...a)→SpreadElement. - Trong array:
[...a]→SpreadElement. - Trong object:
{...o}→SpreadElement(Babel shape khác object vs array).
- File mới:
nodes/patterns/ObjectPattern.ts. - Parse trong: variable declarator, function param, assignment target.
- Hỗ trợ: shorthand
{a}, rename{a: b}, default{a = 1}, rest{a, ...b}, nested{a: {b}}.
- File mới:
nodes/patterns/ArrayPattern.ts. - Hỗ trợ:
[a, b], hole[, b], default[a = 1], rest[a, ...b], nested[a, [b]].
- Sửa tokenizer: backtick string tách thành
TemplateLiteralvớiquasis+expressions. - Parse recursive:
${...}chứa expression (có thể chứa template literal khác). - Escape:
\``,${`.
- Sửa ForStatement.ts: sau
(decl, nếu gặp keywordcủa→ForOfStatement,trong→ForInStatement. - Xóa throw "not implemented".
- Hỗ trợ
for await (... của ...).
- Shorthand:
{a}parse thànhProperty { key: a, value: a, shorthand: true }. - Computed:
{[expr]: value}→Property { computed: true, key: expr }.
Acceptance mỗi feature:
- Parser positive test (≥3 case bao gồm edge).
- Parser negative test (cú pháp sai → error tiếng Việt).
- Codegen snapshot test.
- Cập nhật compatibility.md từ ❌ → ✅.
Acceptance Phase 1:
- Coverage tăng ít nhất +10% so với baseline.
- Demo: viết lại
sandbox/src/index.vjsdùng arrow + destructuring + template → parse + codegen ra JS chạy được vớinode --input-type=module -e "...".
Hoàn thiện phần dang dở.
- Tokenizer:
#identifierthành token riêng. - Parse trong class body: property + method.
- Chỉ cho truy cập trong class.
- Object literal:
{get x() {}, set x(v) {}}→Property { kind: 'get'|'set' }. - Class body:
lấy x() {},gán x(v) {}(dùng keyword tiếng Việt đã thêm Phase 0).
- Verify:
bất đồng bộ hàm* name() {}parse đúng vớiasync: true, generator: true. - Test:
for await (v của asyncGen()).
obj?.()— optional call.obj?.[x]— optional computed member.- Test chain:
a?.b?.c.
- Thêm tokens nếu thiếu:
||=,&&=,??=. - Parse như
AssignmentExpressionvới operator tương ứng.
- Regex tokenizer: hex
0x[0-9a-fA-F]+, octal0o[0-7]+, binary0b[01]+. - BigInt
123n(suffixn). - Numeric separator
1_000_000.
- Tokenizer:
/pattern/flags, phân biệt với division bằng context (sau operator/keyword = regex, sau expression = division). - AST:
RegExpLiteral { pattern, flags }.
\n,\t,\r,\\,\",\',\0,\xFF,\u00E1,\u{1F600}.- Test đầy đủ.
- Verify tokenizer skip
//và/* */. - Preserve comment trong AST (optional, cho future codegen giữ comment).
- Test: comment ở đủ vị trí (sau statement, giữa expression, đầu file).
nhãn: lặp (...) {}+phá vòng lặp nhãn/tiếp tục nhãn.- Test với nested loop.
- Rà soát: node nào có parser test nhưng chưa có codegen test → bổ sung.
- Đặc biệt: ForStatement, WhileStatement, DoWhileStatement, TryStatement, ThrowStatement, BreakStatement, ContinueStatement, LabelledStatement, WithStatement.
Acceptance:
- compatibility.md không còn 🟡 ở cột P2.
- Coverage ≥85% statements.
- Xóa logic codepoint encoding hiện tại.
- Quy tắc mới:
- Replace
(space) →_. - Giữ nguyên Unicode char.
- Nếu ký tự không hợp lệ JS identifier → encode
\u{xxxx}. - Nếu sau mangling trùng với identifier khác trong cùng scope → throw
VietScriptError: tên "xin chào" trùng với "xin_chào" sau khi chuyển đổi.
- Replace
- Trong mỗi scope (function, block, module), trước khi codegen: build set mangled names, detect trùng.
- Trả error với cả 2 vị trí nguồn.
- Nếu identifier sau mangling trùng JS reserved (
if,class, ...) → throw error. User phải đặt tên khác. - Lý do: an toàn hơn là auto-rename.
- Mọi identifier tiếng Việt trong test suite cũ phải giữ nguyên behavior (update snapshot output).
- Thêm test: collision, reserved word, multi-word với dấu.
- docs/basics/variables.md: thêm section "cách tên biến được dịch sang JS".
Acceptance:
- Output JS của sandbox đọc được, không còn
_ch224o. - Tất cả snapshot test cập nhật và pass.
- 3 test collision/reserved word mới.
Quan trọng cho DX khi chạy thật — stack trace phải trỏ về file .vjs gốc.
- Parser phải track
loc: { line, column }trên mọi AST node (verify có sẵn, bổ sung nếu thiếu). @babel/generatorcó optionsourceMaps: true— sử dụng.- Output:
file.vjs.mapv3 source map spec.
Chuẩn hóa format error tiếng Việt:
VietScriptError: <mô tả>
tại file.vjs:12:5
10 | hàm tính(a, b) {
11 | khai báo tổng = a + b
> 12 | trả về tổng +
| ^
13 | }
gợi ý: thiếu biểu thức sau dấu '+'
- Class
VietScriptErrorvớifile,line,column,snippet,hint. - Catch tại mọi
throw new Error(...)trong parser → replace vớiVietScriptError. - Rà hết parser để add
hintcho lỗi phổ biến.
- Khi chạy JS output có source map, Node 22+ / Bun tự đọc source map cho stack trace.
- Test:
throwtrong.vjs, stack trace phải hiệnfile.vjs:line.
Acceptance:
node --enable-source-maps output.js→ error trỏ về.vjs.- Syntax error trong
.vjsin ra format đẹp có snippet + hint. - Test: ≥10 error case khác nhau.
Đây là thứ biến dự án từ "demo" thành "dùng được".
- Binary name:
vietscript(ngắn:vjs). - Sub-command:
vietscript run file.vjs— parse + codegen + chạy ngay qua Node.vietscript build src/— build cả thư mục, output JS + source map.vietscript watch src/— watch mode, rebuild on change.vietscript check file.vjs— parse only, báo lỗi không chạy.
- Stack:
commandercho parsing args,chokidarcho watch.
- File:
packages/cli/loader.mjs. - Dùng Node loader hooks API (Node 20+).
- Khi
import "./file.vjs": loader parse → codegen → trả JS. - Cho phép:
node --loader @vietscript/cli/loader ./main.vjschạy thẳng.vjs.
- Bun có
Bun.plugin({ loader })API. - File:
packages/cli/bun-plugin.ts. - Usage:
bun run --preload @vietscript/cli/bun-plugin main.vjs.
- Tạo
vietscript.config.jsonvới default (entry, outDir, target). - Tạo
src/main.vjsmẫu.
- CLI catch
VietScriptError, in đẹp. - Exit code != 0 nếu có lỗi (cho CI).
Acceptance:
npm i -g @vietscript/cli && vietscript run examples/hello.vjsin ra "Xin chào".- Watch mode tự rebuild khi sửa file.
node --loader @vietscript/cli/loader main.vjschạy OK.bun main.vjs(sau preload) chạy OK.
Verify plugin đã có chạy với project thật, không chỉ unit test.
- Review packages/plugins/vite.
- Test với Vite 5 + React app.
- HMR: sửa
.vjs→ browser reload đúng.
- Review packages/plugins/webpack.
- Test với Webpack 5 + vanilla JS.
- Thư mục mới:
examples/todo-app/. - Nội dung: app todo nhỏ, viết 100% bằng VietScript, dùng Vite + React.
- Deploy lên Vercel/Netlify — có URL live trong README.
- Thư mục mới:
examples/node-server/. - Express app viết bằng VietScript, chạy qua
vietscript runhoặc loader.
Acceptance:
- 2 sample project chạy thật, có URL / screenshot trong README.
- README có badge "dùng trong production" (trong app demo của chính dự án).
Ngôn ngữ có "hồn" khi có thể viết hoàn toàn tiếng Việt. Stdlib là tập hàm wrap JS globals.
- Auto-import trong CLI run mode (
import * from '@vietscript/stdlib'tự động).
in(...args) → console.log
đọc(prompt) → prompt (browser) / readline (Node)
tự_số(s) → Number(s)
tự_chuỗi(x) → String(x)
loại_của(x) → typeof x
độ_dài(x) → x.length
- Không wrap hết, chỉ thêm alias cho method phổ biến:
mảng.thêm()→Array.prototype.pushmảng.bớt()→Array.prototype.popmảng.với_mỗi()→forEachmảng.lọc()→filtermảng.ánh_xạ()→map
Implement qua prototype extension (polyfill) hoặc wrapper function. Chọn wrapper function để không pollute global.
- Không làm: Promise wrapper, Date wrapper, Math wrapper. Để dành post-v1.
- Không làm: I/O framework riêng.
Acceptance:
- Viết được full sample: "hello world" 100% tiếng Việt, không có identifier tiếng Anh nào.
- Docs
docs/stdlib.mdliệt kê hết API.
Hiện tại docs ở docs/basics/ rời rạc. Cấu trúc mới:
docs/
├── index.md (landing)
├── introduction.md (giữ)
├── getting-started.md (cập nhật: cài CLI, chạy file đầu tiên)
├── language/ (ngôn ngữ)
│ ├── variables.md
│ ├── operators.md
│ ├── control-flow.md (if + switch + ternary)
│ ├── loops.md (for + while + for...of/in)
│ ├── functions.md (thường + arrow + async + generator)
│ ├── classes.md
│ ├── modules.md (import/export)
│ ├── error-handling.md (try/catch/throw)
│ ├── destructuring.md
│ └── template-literals.md
├── stdlib.md
├── cli.md
├── plugins.md (Vite + Webpack + Bun)
├── compatibility.md (giữ, đã có)
├── differences-from-js.md (mới — liệt kê minh bạch cái nào khác JS)
└── roadmap.md (file này)
- Giới thiệu 1 đoạn.
- Cú pháp VietScript + JS tương ứng đặt cạnh nhau (block code 2 cột hoặc 2 block liền kề).
- ≥2 ví dụ chạy được.
- Link đến playground với ví dụ đó pre-fill.
docs/migration-from-js.md: đoạn JS → VietScript tương đương (10-15 ví dụ).
- Update playground load các example mới trong
examples/. - Nút "Copy as JS" xem ngay output.
Acceptance:
- Mọi mục trong compatibility.md có ít nhất 1 link đến trang docs giải thích.
- 10+ ví dụ runnable trong playground.
- Set version
1.0.0cho tất cả package. CHANGELOG.md: liệt kê mọi thay đổi từ trạng thái hiện tại.
- Publish public:
@vietscript/parser,@vietscript/shared,@vietscript/cli,@vietscript/stdlib,@vietscript/vite-plugin,@vietscript/webpack-plugin. - Test: install vào project trống, chạy được.
- Rewrite: "VietScript là ngôn ngữ tiếng Việt transpile ra JS. Chạy trên browser/Node/Bun."
- Quick start 5 dòng.
- Link docs + playground + sample apps.
- Bài viết ngắn (vn + en) về dự án.
- Đăng: Twitter/X, Reddit r/programming, dev.to, HackerNews (nếu muốn).
- Nhắm mạnh cộng đồng dev Việt trước.
- Tạo issue template, PR template trên GitHub.
- Monitor issue đầu tiên trong 2 tuần, fix bug.
Acceptance:
- Package có trên npm, tải về chạy được.
- README có badge version, CI status, coverage.
- Sample app có URL live.
Ghi lại để không quên, nhưng không đụng trước khi xong v1:
- TypeScript annotation — syntax có sẵn trong specs nhưng parser chưa handle. Cần decide: transpile strip types, hay generate .d.ts?
- LSP server — autocomplete + go-to-definition trong VS Code.
- Formatter (
vietscript fmt). - REPL (
vietscript repl). - Decorators.
- Pattern matching (proposal stage).
- Native compilation via AssemblyScript / TinyGo (thử nghiệm).
- Translator JS → VietScript tự động (ngược chiều hiện tại).
- Stdlib mở rộng: Promise, Date, Math, Fetch wrapper.
- Mobile — React Native example.
| Rủi ro | Ảnh hưởng | Đối phó |
|---|---|---|
| Destructuring + pattern phức tạp hơn dự kiến, lan sang các node khác | Phase 1 kéo dài | Làm ObjectPattern/ArrayPattern trước, isolated test, rồi mới wire vào VariableDeclaration và ParameterList |
| Tokenizer regex xung đột khi thêm regex literal | Parse lỗi khắp nơi | Context-aware tokenizer: track "expect expression" state. Test regression cho mọi file test cũ |
| Keyword tiếng Việt mới xung đột với identifier có sẵn | Break code user | Giữ alias tiếng Anh. Thêm flag --strict-vietnamese bật ở v1.x sau |
Source map cần loc chi tiết mà hiện tại AST không track |
Phase 4 rework parser | Audit từng node ở cuối Phase 2, bổ sung loc trước khi vào Phase 4 |
@babel/generator output không khớp syntax VietScript cần |
Phải tự viết printer | Kiểm tra ngay đầu Phase 1 bằng test codegen cho mọi feature mới. Nếu có case không hỗ trợ, fork hoặc tự viết printer cho node đó |
| Scope phình | Không release được | Backlog post-v1 là cứng. Nếu thấy muốn làm, ghi vào backlog và đi tiếp |
| Solo developer burnout | Dự án stall | Scope nhỏ mỗi phase, release từng phase lên branch, có cảm giác tiến triển |
- Mỗi phase = 1 GitHub Milestone.
- Mỗi task trong phase = 1 Issue.
- Mỗi feature P1/P2 = 1 PR.
- compatibility.md update sau mỗi PR merge.
- Cuối mỗi phase: cập nhật
CHANGELOG.md(unreleased section).
- Cú pháp JS cover: 95%+ (xem compatibility matrix).
- Coverage: ≥85% statements.
- CLI: chạy được trên Node 20+, Bun 1.1+.
- Runtime: chạy trên Chrome/Firefox/Safari/Edge (≥2024), Node 20+, Bun 1.1+.
- Sample apps: ≥2 (todo web, node server) có URL live.
- Docs: 100% mục trong matrix có trang giải thích.
- NPM: 6 package publish với version 1.0.0.
- Tạo GitHub milestone cho Phase 0-9.
- Tạo issue cho Phase 0 (5 task con).
- Bắt đầu với Phase 0.1 (coverage reporter) — có số đo để theo dõi tiến độ.
Không làm gì khác trước khi bạn review roadmap này.