You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+7-24Lines changed: 7 additions & 24 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -16,6 +16,7 @@ Highlights
16
16
- Configurable lowering modes: full syntax transforms or globals-only.
17
17
- Specifier tools: add extensions, add directory indexes, or map with a custom callback.
18
18
- Output control: write to disk (`out`/`inPlace`) or return the transformed string.
19
+
- CLI: `dub` for batch transforms, dry-run/list/summary, stdin/stdout, and colorized diagnostics. See [docs/cli.md](docs/cli.md).
19
20
20
21
> [!IMPORTANT]
21
22
> All parsing logic is applied under the assumption the code is in [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) which [modules run under by default](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#other_differences_between_modules_and_classic_scripts).
@@ -143,7 +144,7 @@ type ModuleOptions = {
143
144
### Behavior notes (defaults in parentheses)
144
145
145
146
- `target` (`commonjs`): output module system.
146
-
- `transformSyntax` (true): enable/disable the ESM↔CJS lowering pass; set to `'globals-only'` to rewrite module globals (`import.meta.*`, `__dirname`, `__filename`, `require.main` shims) while leaving import/export syntax untouched. In `'globals-only'`, no helpers are injected (e.g., `__requireResolve`), `require.resolve` rewrites to `import.meta.resolve`, and `idiomaticExports` is skipped. See [globals-only](#globals-only-scope).
147
+
- `transformSyntax` (`true`): enable/disable the ESM↔CJS lowering pass; set to `'globals-only'` to rewrite module globals (`import.meta.*`, `__dirname`, `__filename`, `require.main` shims) while leaving import/export syntax untouched. In `'globals-only'`, no helpers are injected (e.g., `__requireResolve`), `require.resolve` rewrites to `import.meta.resolve`, and `idiomaticExports` is skipped. See [globals-only](docs/globals-only.md).
147
148
- `liveBindings` (`strict`): getter-based live bindings, or snapshot (`loose`/`off`).
148
149
- `appendJsExtension` (`relative-only` when targeting ESM): append `.js` to relative specifiers; never touches bare specifiers.
149
150
- `appendDirectoryIndex` (`index.js`): when a relative specifier ends with a slash, append this index filename (set `false` to disable).
@@ -159,7 +160,7 @@ type ModuleOptions = {
159
160
- `requireSource` (`builtin`): whether `require` comes from Node or `createRequire`.
160
161
- `cjsDefault` (`auto`): bundler-style default interop vs direct `module.exports`.
161
162
- `idiomaticExports` (`safe`): when raising CJS to ESM, attempt to synthesize `export` statements directly when it is safe. `off` always uses the helper bag; `aggressive` currently matches `safe` heuristics.
162
-
- `out`/`inPlace`: write the transformed code to a file; otherwise the function returns the transformed string only.
163
+
- `out`/`inPlace`: choose output location. Default returns the transformed string (CLI emits to stdout). `out` writes to the provided path. `inPlace` overwrites the input files on disk and does not return/emit the code.
163
164
- `cwd` (`process.cwd()`): Base directory used to resolve relative `out` paths.
164
165
165
166
> [!NOTE]
@@ -170,13 +171,6 @@ See [docs/esm-to-cjs.md](docs/esm-to-cjs.md) for deeper notes on live bindings,
170
171
> [!NOTE]
171
172
> Known limitations: `with` and unshadowed `eval` are rejected when raising CJS to ESM because the rewrite would be unsound; bare specifiers are not rewritten—only relative specifiers participate in `rewriteSpecifier`.
172
173
173
-
### Globals-only scope
174
-
175
-
- Rewrites module globals (`import.meta.*`, `__dirname`, `__filename`, `require.main` shims) for the target side.
176
-
- Optional specifier rewrites still run (`rewriteSpecifier`, `appendJsExtension`, `appendDirectoryIndex`).
177
-
- Leaves imports/exports and interop untouched (no export bag, no idiomaticExports, no live-binding synthesis, no helpers like `__requireResolve`).
178
-
- CJS→ESM: `require.resolve` maps to `import.meta.resolve` (URL return, ESM resolver) and may differ from CJS resolution. ESM→CJS: `import.meta` maps to CJS globals; no import lowering.
179
-
180
174
### Diagnostics callback example
181
175
182
176
Pass a `diagnostics` callback to surface CJS→ESM edge cases (mixed `module.exports`/`exports`, top-level `return`, legacy `require.cache`/`require.extensions`, live-binding reassignments, string-literal export names):
This pre-`tsc` step removes the flagged globals inthe compiled orientation; runtime semantics still match the target build.
215
+
This pre-`tsc` step rewrites globals-only (keeps import/export syntax) so the TypeScript checker sees already-rewritten sources; runtime semantics still match the target build.
`transformSyntax: 'globals-only'` rewrites well-known module globals lexically without emitting any runtime helpers or preludes. Imports/exports and interop are left untouched; runtime correctness depends on the target environment providing the globals you opt into.
4
+
5
+
## Scope
6
+
7
+
- Pure identifier/member rewrites; no injected helpers, shims, or prelude imports.
8
+
- Works in both directions (CJS ➜ ESM, ESM ➜ CJS).
9
+
- Diagnostics still surface for legacy constructs (e.g., `require.extensions`, `module.parent`).
| ESM ➜ CJS | other `import.meta.*`|`module.*`| no extra objects created |
31
+
<!-- prettier-ignore-end -->
32
+
33
+
\* In globals-only, we do not inject the CJS-style `require.resolve` helper. The rewrite relies on the host's native `import.meta.resolve`, whose semantics differ (URL-based, parent handling). Use full transforms if you need the helper that preserves CJS resolution behavior.
34
+
35
+
## When to use it
36
+
37
+
- Pre-`tsc` mitigation for TypeScript’s [asymmetry on module globals](https://github.com/microsoft/TypeScript/issues/58658) (see `test/cli.ts`): rewrite globals so the checker sees the target-side shapes without altering import/export syntax.
38
+
- Tooling pipelines that only need syntax-level compatibility and will supply the required globals at runtime.
39
+
40
+
## When not to use it
41
+
42
+
- When you need runtime shims (e.g., `createRequire`, import.meta polyfills) or interop helpers: use `transformSyntax: true` instead.
43
+
44
+
## Caveats
45
+
46
+
- Behavior relies on the target runtime’s built-ins; no guarantees are made about availability or semantics of the rewritten globals.
47
+
- Legacy fields like `require.cache`, `require.extensions`, `module.parent`, and `module.children` only receive warnings/stubs—behavior may differ from Node.
Copy file name to clipboardExpand all lines: docs/roadmap.md
+4-5Lines changed: 4 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,15 +9,14 @@ Shipped: `idiomaticExports: 'safe'` is now the default for CJS → ESM, with fal
9
9
Next:
10
10
11
11
- Explore a true `'aggressive'` mode (mixed exports/module.exports, limited reassignments, identifier-safe computed keys) with guarded semantics and explicit diagnostics.
12
+
- In `'auto'`, allow idiomatic `module.exports = { foo, bar }` when the object literal is simple: single top-level assignment, no spreads/getters/computed/duplicate keys, only identifier keys, RHS values limited to safe literals/identifiers/function|class expressions, no `require()` inside RHS, and no `__proto__`/`prototype` keys. Add a shorthand-object fixture test and keep falling back to the helper for anything more complex.
12
13
- Consider a constrained ESM → CJS “pretty” path where live-binding and TLA semantics permit it.
13
14
14
15
## CLI
15
16
16
-
- Deliver a `knighted-module` CLI that wraps the core transform with parity to API options (targets, rewriteSpecifier, appendJsExtension/appendDirectoryIndex, detectCircularRequires, topLevelAwait, cjsDefault, diagnostics hooks, out/in-place).
17
-
- Input handling: accept file/glob lists plus stdin/stdout piping; respect `package.json``type` and `.cjs/.mjs` extensions; allow per-invocation overrides via flags and a config file.
18
-
- Output handling: in-place rewrite or out-dir mirroring with extension rewriting; emit diagnostics to stderr and machine-readable JSON when requested; non-zero exit on diagnostics of severity error.
19
-
- Performance ergonomics: batch parse/format where possible, optional concurrency flag, and a `--watch` mode that rebuilds on change with minimal restarts.
20
-
- DX: `--dry-run` to preview planned rewrites, `--list` to show which files would change, `--summary` to print counts of transformed specifiers/globals, and `--help`/`--version` aligned with package metadata.
17
+
- Shipped parity CLI wrapping the core transform (targets, rewriteSpecifier, appendJsExtension/appendDirectoryIndex, detectCircularRequires, topLevelAwait, cjsDefault, diagnostics hooks, out/in-place) with stdin/stdout support, JSON/summary, and list/dry-run paths.
18
+
- Next: optional concurrency flag, `--watch` mode with minimal restarts, and a tiny stream type surface to keep test stubs and embedding clean.
19
+
- DX polish: keep help/examples in sync with tests; retain single-fixture CLI coverage unless new CLI-specific behaviors emerge (e.g., multi-ext glob ordering, large-input streaming), since transform semantics are already exercised in module fixtures.
0 commit comments