|
| 1 | +# CLI Migration Plan |
| 2 | + |
| 3 | +Unify around a single `ohm` CLI shipped by `@ohm-js/compiler`, with `compile` |
| 4 | +(wasm-first batch compilation), `match` (wasm-first), and type generation as the |
| 5 | +stable surface. |
| 6 | + |
| 7 | +## End-State CLI Surface |
| 8 | + |
| 9 | +Binary name: `ohm` (from `@ohm-js/compiler`) |
| 10 | + |
| 11 | +### `ohm compile <patterns...>` |
| 12 | + |
| 13 | +Compiles `.ohm` sources to wasm artifacts in batch (glob-aware), plus optional |
| 14 | +type generation. This is the v18 successor to `generateBundles`. |
| 15 | + |
| 16 | +Flags: |
| 17 | + |
| 18 | +- `--cwd <dir>` — base directory for glob expansion |
| 19 | +- `-o, --outDir <dir>` — where to write outputs (default: alongside source) |
| 20 | +- `-t, --withTypes` — generate corresponding `.d.ts` |
| 21 | +- `-g, --grammarName <name>` — compile only one grammar from a multi-grammar file |
| 22 | + (default: compile all) |
| 23 | +- `-n, --dryRun` — print/plan without writing |
| 24 | +- `--quiet` / `--verbose` |
| 25 | + |
| 26 | +Output naming: |
| 27 | + |
| 28 | +- `path/to/foo.ohm` → `path/to/foo.ohm.wasm` |
| 29 | +- With `--withTypes`: `path/to/foo.ohm.d.ts` |
| 30 | + |
| 31 | +### `ohm match <inputPath>` |
| 32 | + |
| 33 | +Wasm-first matching. Accepts either a `.wasm` artifact or a source `.ohm` file |
| 34 | +(compile-then-match). |
| 35 | + |
| 36 | +Flags: |
| 37 | + |
| 38 | +- `-g, --grammar <path>` — `.ohm` (compile in-memory, then match) or `.wasm` |
| 39 | + (load directly) |
| 40 | +- `--grammarName <name>` — select grammar from multi-grammar file |
| 41 | +- `--startRule <rule>` — optional start rule selection |
| 42 | + |
| 43 | +Behavior: exit 0 on success, non-zero with failure message on mismatch. |
| 44 | + |
| 45 | +### `ohm types <patterns...>` (optional) |
| 46 | + |
| 47 | +Standalone type generation, useful when you want `.d.ts` without emitting wasm. |
| 48 | + |
| 49 | +Flags: `--cwd`, `--outDir`, `--grammarName`, `--dryRun` |
| 50 | + |
| 51 | +### Naming |
| 52 | + |
| 53 | +- Primary verb: `ohm compile` (not `build`), since "compile to wasm" is the core |
| 54 | + v18 story. |
| 55 | +- No default action — bare `ohm` shows help. |
| 56 | + |
| 57 | +## Migration Phases |
| 58 | + |
| 59 | +### Phase 0: Today |
| 60 | + |
| 61 | +- `@ohm-js/compiler` ships `ohm2wasm` (single-file, no globs/batch) |
| 62 | +- `@ohm-js/cli` ships `ohm` with `generateBundles` (v17 recipes) and `match` |
| 63 | + (v17 runtime) |
| 64 | + |
| 65 | +### Phase 1: Introduce unified `ohm` in the compiler (v18 alpha → early beta) |
| 66 | + |
| 67 | +Goal: make `@ohm-js/compiler` fully usable as the official CLI. |
| 68 | + |
| 69 | +1. Add `ohm` bin entry to `@ohm-js/compiler`, alongside `ohm2wasm`. |
| 70 | +2. Refactor `ohm2wasm` implementation into `ohm compile`: |
| 71 | + - Add glob support via `fast-glob` |
| 72 | + - Add `--outDir`, `--cwd`, `--withTypes`, batch compilation |
| 73 | + - If invoked as `ohm2wasm`, behave like `ohm compile <file>` with a |
| 74 | + deprecation warning. |
| 75 | +3. Implement `ohm match` wasm-first: |
| 76 | + - `--grammar` accepts `.wasm` (load directly) or `.ohm` (compile-then-match) |
| 77 | +4. Move `generateTypes` into `@ohm-js/compiler` (the compiler already bundles |
| 78 | + `ohm-js-legacy` internally via esbuild — use that for type generation without |
| 79 | + exposing it publicly). |
| 80 | +5. Update docs to recommend `pnpm add -D @ohm-js/compiler` and `ohm compile`. |
| 81 | + |
| 82 | +### Phase 2: Deprecate `@ohm-js/cli` (v18 beta) |
| 83 | + |
| 84 | +Goal: keep the old install path working while steering users to the compiler. |
| 85 | + |
| 86 | +1. Release a new major of `@ohm-js/cli` that: |
| 87 | + - Depends on `@ohm-js/compiler` |
| 88 | + - Forwards all args to the compiler's `ohm` CLI |
| 89 | + - Prints deprecation warning |
| 90 | +2. Compatibility mapping: |
| 91 | + - `ohm generateBundles …` → forwards to `ohm compile …` |
| 92 | + - `ohm match …` → forwards to compiler's `ohm match …` |
| 93 | +3. Deprecate the package on npm. |
| 94 | + |
| 95 | +### Phase 3: Compiler CLI is the only real CLI (v18 stable) |
| 96 | + |
| 97 | +1. `@ohm-js/compiler`'s `ohm` is the official CLI in all docs. |
| 98 | +2. `@ohm-js/cli` remains as deprecated wrapper only — no new features. |
| 99 | +3. `generateBundles` kept as deprecated alias for one stable cycle. |
| 100 | + |
| 101 | +### Phase 4: Clean up (v19 or later) |
| 102 | + |
| 103 | +- Stop publishing `@ohm-js/cli`, or keep as permanent thin wrapper. |
| 104 | +- Optionally remove `ohm2wasm` alias, or keep it (low maintenance cost). |
| 105 | + |
| 106 | +## Type Generation in v18 |
| 107 | + |
| 108 | +The compiler already bundles `ohm-js-legacy` via esbuild for internal use. Type |
| 109 | +generation can reuse this internal representation to produce `.d.ts` files |
| 110 | +without exposing legacy APIs publicly. |
| 111 | + |
| 112 | +- Primary: `ohm compile --withTypes` |
| 113 | +- Optional: `ohm types <patterns...>` for CI workflows that want types without |
| 114 | + wasm |
| 115 | + |
| 116 | +## Risks and Guardrails |
| 117 | + |
| 118 | +- **Artifact format churn**: lock down output naming (`.ohm.wasm`, `.ohm.d.ts`) |
| 119 | + early. |
| 120 | +- **Multi-grammar files**: default is compile all; `--grammarName` selects one. |
| 121 | +- **Dependencies**: anything needed at runtime by the CLI (`commander`, |
| 122 | + `fast-glob`) must be in `dependencies` of `@ohm-js/compiler`, not |
| 123 | + `devDependencies`. |
| 124 | +- **Node version**: `ohm match` with `.ohm` input requires Node 24 (same as the |
| 125 | + compiler). Document clearly. |
| 126 | + |
| 127 | +## Future Considerations (not now) |
| 128 | + |
| 129 | +- `--emit wasm+js` for generating JS loader modules (depends on v18 runtime |
| 130 | + loading API stabilizing) |
| 131 | +- `--watch` mode |
| 132 | +- Incremental compilation / caching via `--cacheDir` |
0 commit comments