@knighted/module 1.0.0-beta.0 → 1.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -4
- package/dist/cjs/exports.d.cts +6 -0
- package/dist/cjs/format.cjs +315 -12
- package/dist/cjs/format.d.cts +3 -2
- package/dist/cjs/formatters/assignmentExpression.cjs +1 -1
- package/dist/cjs/formatters/identifier.cjs +4 -4
- package/dist/cjs/formatters/memberExpression.cjs +2 -2
- package/dist/cjs/formatters/metaProperty.cjs +19 -1
- package/dist/cjs/identifiers.d.cts +19 -0
- package/dist/cjs/lang.d.cts +4 -0
- package/dist/cjs/module.cjs +5 -4
- package/dist/cjs/types.d.cts +9 -0
- package/dist/cjs/url.d.cts +2 -0
- package/dist/cjs/utils/exports.cjs +227 -0
- package/dist/cjs/utils/identifiers.cjs +190 -0
- package/dist/cjs/utils/lang.cjs +25 -0
- package/dist/cjs/utils/url.cjs +16 -0
- package/dist/cjs/utils.cjs +71 -1
- package/dist/cjs/utils.d.cts +3 -2
- package/dist/exports.d.ts +6 -0
- package/dist/format.d.ts +3 -2
- package/dist/format.js +311 -9
- package/dist/formatters/assignmentExpression.js +1 -1
- package/dist/formatters/identifier.js +2 -2
- package/dist/formatters/memberExpression.js +1 -1
- package/dist/formatters/metaProperty.js +19 -1
- package/dist/identifiers.d.ts +19 -0
- package/dist/lang.d.ts +4 -0
- package/dist/module.js +4 -3
- package/dist/src/format.d.ts +9 -0
- package/dist/src/module.d.ts +3 -0
- package/dist/src/parse.d.ts +2 -0
- package/dist/src/types.d.ts +44 -0
- package/dist/src/utils/exports.d.ts +6 -0
- package/dist/src/utils/identifiers.d.ts +19 -0
- package/dist/src/utils/lang.d.ts +4 -0
- package/dist/src/utils/url.d.ts +2 -0
- package/dist/src/utils.d.ts +26 -0
- package/dist/src/walk.d.ts +20 -0
- package/dist/types.d.ts +9 -0
- package/dist/url.d.ts +2 -0
- package/dist/utils/exports.js +221 -0
- package/dist/utils/identifiers.js +183 -0
- package/dist/utils/lang.js +20 -0
- package/dist/utils/url.js +10 -0
- package/dist/utils.d.ts +3 -2
- package/dist/utils.js +70 -1
- package/package.json +4 -4
- /package/dist/{formatters → src/formatters}/assignmentExpression.d.ts +0 -0
- /package/dist/{formatters → src/formatters}/expressionStatement.d.ts +0 -0
- /package/dist/{formatters → src/formatters}/identifier.d.ts +0 -0
- /package/dist/{formatters → src/formatters}/memberExpression.d.ts +0 -0
- /package/dist/{formatters → src/formatters}/metaProperty.d.ts +0 -0
- /package/dist/{helpers → src/helpers}/identifier.d.ts +0 -0
- /package/dist/{helpers → src/helpers}/scope.d.ts +0 -0
package/README.md
CHANGED
|
@@ -9,6 +9,12 @@ Node.js utility for transforming a JavaScript or TypeScript file from an ES modu
|
|
|
9
9
|
- ES module ➡️ CommonJS
|
|
10
10
|
- CommonJS ➡️ ES module
|
|
11
11
|
|
|
12
|
+
Highlights
|
|
13
|
+
|
|
14
|
+
- Defaults to safe CommonJS output: strict live bindings, import.meta shims, and specifier preservation.
|
|
15
|
+
- Opt into stricter/looser behaviors: live binding enforcement, import.meta.main gating, and top-level await strategies.
|
|
16
|
+
- Can optionally rewrite relative specifiers and write transformed output to disk.
|
|
17
|
+
|
|
12
18
|
> [!IMPORTANT]
|
|
13
19
|
> 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).
|
|
14
20
|
|
|
@@ -18,9 +24,15 @@ By default `@knighted/module` transforms the one-to-one [differences between ES
|
|
|
18
24
|
|
|
19
25
|
- Node >= 20.11.0
|
|
20
26
|
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @knighted/module
|
|
31
|
+
```
|
|
32
|
+
|
|
21
33
|
## Example
|
|
22
34
|
|
|
23
|
-
Given an ES module
|
|
35
|
+
Given an ES module:
|
|
24
36
|
|
|
25
37
|
**file.js**
|
|
26
38
|
|
|
@@ -40,7 +52,7 @@ const detectCalledFromCli = async path => {
|
|
|
40
52
|
detectCalledFromCli(argv[1])
|
|
41
53
|
```
|
|
42
54
|
|
|
43
|
-
|
|
55
|
+
Transform it to CommonJS:
|
|
44
56
|
|
|
45
57
|
```js
|
|
46
58
|
import { transform } from '@knighted/module'
|
|
@@ -51,7 +63,7 @@ await transform('./file.js', {
|
|
|
51
63
|
})
|
|
52
64
|
```
|
|
53
65
|
|
|
54
|
-
Which produces
|
|
66
|
+
Which produces:
|
|
55
67
|
|
|
56
68
|
**file.cjs**
|
|
57
69
|
|
|
@@ -99,6 +111,7 @@ type ModuleOptions = {
|
|
|
99
111
|
| ((value: string) => string | null | undefined)
|
|
100
112
|
dirFilename?: 'inject' | 'preserve' | 'error'
|
|
101
113
|
importMeta?: 'preserve' | 'shim' | 'error'
|
|
114
|
+
importMetaMain?: 'shim' | 'warn' | 'error'
|
|
102
115
|
requireSource?: 'builtin' | 'create-require'
|
|
103
116
|
cjsDefault?: 'module-exports' | 'auto' | 'none'
|
|
104
117
|
topLevelAwait?: 'error' | 'wrap' | 'preserve'
|
|
@@ -107,7 +120,24 @@ type ModuleOptions = {
|
|
|
107
120
|
}
|
|
108
121
|
```
|
|
109
122
|
|
|
123
|
+
Behavior notes (defaults in parentheses)
|
|
124
|
+
|
|
125
|
+
- `target` (`commonjs`): output module system.
|
|
126
|
+
- `transformSyntax` (true): enable/disable the ESM↔CJS lowering pass.
|
|
127
|
+
- `liveBindings` (`strict`): getter-based live bindings, or snapshot (`loose`/`off`).
|
|
128
|
+
- `dirFilename` (`inject`): inject `__dirname`/`__filename`, preserve existing, or throw.
|
|
129
|
+
- `importMeta` (`shim`): rewrite `import.meta.*` to CommonJS equivalents.
|
|
130
|
+
- `importMetaMain` (`shim`): gate `import.meta.main` with shimming/warning/error when Node support is too old.
|
|
131
|
+
- `topLevelAwait` (`error`): throw, wrap, or preserve when TLA appears in CommonJS output.
|
|
132
|
+
- `rewriteSpecifier` (off): rewrite relative specifiers to a chosen extension or via a callback.
|
|
133
|
+
- `requireSource` (`builtin`): whether `require` comes from Node or `createRequire`.
|
|
134
|
+
- `cjsDefault` (`auto`): bundler-style default interop vs direct `module.exports`.
|
|
135
|
+
- `out`/`inPlace`: write the transformed code to a file; otherwise the function returns the transformed string only.
|
|
136
|
+
|
|
137
|
+
See [docs/esm-to-cjs.md](docs/esm-to-cjs.md) for deeper notes on live bindings, interop helpers, top-level await behavior, and `import.meta.main` handling.
|
|
138
|
+
|
|
110
139
|
## Roadmap
|
|
111
140
|
|
|
112
141
|
- Remove `@knighted/specifier` and avoid double parsing.
|
|
113
|
-
-
|
|
142
|
+
- Emit source maps and clearer diagnostics for transform choices.
|
|
143
|
+
- Broaden fixtures covering live-binding and top-level await edge cases across Node versions.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Node } from 'oxc-parser';
|
|
2
|
+
import type { CjsExport } from '../types.cjs';
|
|
3
|
+
declare const exportsRename = "__exports";
|
|
4
|
+
declare const requireMainRgx: RegExp;
|
|
5
|
+
declare const collectCjsExports: (ast: Node) => Promise<Map<string, CjsExport>>;
|
|
6
|
+
export { exportsRename, requireMainRgx, collectCjsExports };
|
package/dist/cjs/format.cjs
CHANGED
|
@@ -5,17 +5,254 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.format = void 0;
|
|
7
7
|
var _magicString = _interopRequireDefault(require("magic-string"));
|
|
8
|
-
var _identifier = require("
|
|
9
|
-
var _metaProperty = require("
|
|
10
|
-
var _memberExpression = require("
|
|
11
|
-
var _assignmentExpression = require("
|
|
12
|
-
var
|
|
13
|
-
var
|
|
14
|
-
var
|
|
8
|
+
var _identifier = require("#formatters/identifier.js");
|
|
9
|
+
var _metaProperty = require("#formatters/metaProperty.js");
|
|
10
|
+
var _memberExpression = require("#formatters/memberExpression.js");
|
|
11
|
+
var _assignmentExpression = require("#formatters/assignmentExpression.js");
|
|
12
|
+
var _url = require("#utils/url.js");
|
|
13
|
+
var _exports = require("#utils/exports.js");
|
|
14
|
+
var _identifiers = require("#utils/identifiers.js");
|
|
15
|
+
var _identifier2 = require("#helpers/identifier.js");
|
|
16
|
+
var _walk = require("#walk");
|
|
15
17
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
18
|
+
const isValidIdent = name => /^[$A-Z_a-z][$\w]*$/.test(name);
|
|
19
|
+
const exportAssignment = (name, expr, live) => {
|
|
20
|
+
const prop = isValidIdent(name) ? `.${name}` : `[${JSON.stringify(name)}]`;
|
|
21
|
+
if (live === 'strict') {
|
|
22
|
+
const key = JSON.stringify(name);
|
|
23
|
+
return `Object.defineProperty(exports, ${key}, { enumerable: true, get: () => ${expr} });`;
|
|
24
|
+
}
|
|
25
|
+
return `exports${prop} = ${expr};`;
|
|
26
|
+
};
|
|
27
|
+
const defaultInteropName = '__interopDefault';
|
|
28
|
+
const interopHelper = `const ${defaultInteropName} = mod => (mod && mod.__esModule ? mod.default : mod);\n`;
|
|
29
|
+
const hasTopLevelAwait = program => {
|
|
30
|
+
let found = false;
|
|
31
|
+
const walkNode = (node, inFunction) => {
|
|
32
|
+
if (found) return;
|
|
33
|
+
switch (node.type) {
|
|
34
|
+
case 'FunctionDeclaration':
|
|
35
|
+
case 'FunctionExpression':
|
|
36
|
+
case 'ArrowFunctionExpression':
|
|
37
|
+
case 'ClassDeclaration':
|
|
38
|
+
case 'ClassExpression':
|
|
39
|
+
inFunction = true;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
if (!inFunction && node.type === 'AwaitExpression') {
|
|
43
|
+
found = true;
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const keys = Object.keys(node);
|
|
47
|
+
for (const key of keys) {
|
|
48
|
+
const value = node[key];
|
|
49
|
+
if (!value) continue;
|
|
50
|
+
if (Array.isArray(value)) {
|
|
51
|
+
for (const item of value) {
|
|
52
|
+
if (item && typeof item === 'object') {
|
|
53
|
+
walkNode(item, inFunction);
|
|
54
|
+
if (found) return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
} else if (value && typeof value === 'object') {
|
|
58
|
+
walkNode(value, inFunction);
|
|
59
|
+
if (found) return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
walkNode(program, false);
|
|
64
|
+
return found;
|
|
65
|
+
};
|
|
66
|
+
const lowerEsmToCjs = (program, code, opts, containsTopLevelAwait) => {
|
|
67
|
+
const live = opts.liveBindings ?? 'strict';
|
|
68
|
+
const importTransforms = [];
|
|
69
|
+
const exportTransforms = [];
|
|
70
|
+
let needsInterop = false;
|
|
71
|
+
let importIndex = 0;
|
|
72
|
+
for (const node of program.body) {
|
|
73
|
+
if (node.type === 'ImportDeclaration') {
|
|
74
|
+
const srcLiteral = code.slice(node.source.start, node.source.end);
|
|
75
|
+
const specifiers = node.specifiers ?? [];
|
|
76
|
+
const defaultSpec = specifiers.find(s => s.type === 'ImportDefaultSpecifier');
|
|
77
|
+
const namespaceSpec = specifiers.find(s => s.type === 'ImportNamespaceSpecifier');
|
|
78
|
+
const namedSpecs = specifiers.filter(s => s.type === 'ImportSpecifier');
|
|
79
|
+
|
|
80
|
+
// Side-effect import
|
|
81
|
+
if (!specifiers.length) {
|
|
82
|
+
importTransforms.push({
|
|
83
|
+
start: node.start,
|
|
84
|
+
end: node.end,
|
|
85
|
+
code: `require(${srcLiteral});\n`,
|
|
86
|
+
needsInterop: false
|
|
87
|
+
});
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const modIdent = `__mod${importIndex++}`;
|
|
91
|
+
const lines = [];
|
|
92
|
+
lines.push(`const ${modIdent} = require(${srcLiteral});`);
|
|
93
|
+
if (namespaceSpec) {
|
|
94
|
+
lines.push(`const ${namespaceSpec.local.name} = ${modIdent};`);
|
|
95
|
+
}
|
|
96
|
+
if (defaultSpec) {
|
|
97
|
+
let init = modIdent;
|
|
98
|
+
switch (opts.cjsDefault) {
|
|
99
|
+
case 'module-exports':
|
|
100
|
+
init = modIdent;
|
|
101
|
+
break;
|
|
102
|
+
case 'none':
|
|
103
|
+
init = `${modIdent}.default`;
|
|
104
|
+
break;
|
|
105
|
+
case 'auto':
|
|
106
|
+
default:
|
|
107
|
+
init = `${defaultInteropName}(${modIdent})`;
|
|
108
|
+
needsInterop = true;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
lines.push(`const ${defaultSpec.local.name} = ${init};`);
|
|
112
|
+
}
|
|
113
|
+
if (namedSpecs.length) {
|
|
114
|
+
const pairs = namedSpecs.map(s => {
|
|
115
|
+
const imported = s.imported.name;
|
|
116
|
+
const local = s.local.name;
|
|
117
|
+
return imported === local ? imported : `${imported}: ${local}`;
|
|
118
|
+
});
|
|
119
|
+
lines.push(`const { ${pairs.join(', ')} } = ${modIdent};`);
|
|
120
|
+
}
|
|
121
|
+
importTransforms.push({
|
|
122
|
+
start: node.start,
|
|
123
|
+
end: node.end,
|
|
124
|
+
code: `${lines.join('\n')}\n`,
|
|
125
|
+
needsInterop
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
if (node.type === 'ExportNamedDeclaration') {
|
|
129
|
+
// Handle declaration exports
|
|
130
|
+
if (node.declaration) {
|
|
131
|
+
const decl = node.declaration;
|
|
132
|
+
const declSrc = code.slice(decl.start, decl.end);
|
|
133
|
+
const exportedNames = [];
|
|
134
|
+
if (decl.type === 'VariableDeclaration') {
|
|
135
|
+
for (const d of decl.declarations) {
|
|
136
|
+
if (d.id.type === 'Identifier') {
|
|
137
|
+
exportedNames.push(d.id.name);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} else if (decl.id?.type === 'Identifier') {
|
|
141
|
+
exportedNames.push(decl.id.name);
|
|
142
|
+
}
|
|
143
|
+
const exportLines = exportedNames.map(name => exportAssignment(name, name, live));
|
|
144
|
+
exportTransforms.push({
|
|
145
|
+
start: node.start,
|
|
146
|
+
end: node.end,
|
|
147
|
+
code: `${declSrc}\n${exportLines.join('\n')}\n`
|
|
148
|
+
});
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Handle re-export or local specifiers
|
|
153
|
+
if (node.specifiers?.length) {
|
|
154
|
+
if (node.source) {
|
|
155
|
+
const srcLiteral = code.slice(node.source.start, node.source.end);
|
|
156
|
+
const modIdent = `__mod${importIndex++}`;
|
|
157
|
+
const lines = [`const ${modIdent} = require(${srcLiteral});`];
|
|
158
|
+
for (const spec of node.specifiers) {
|
|
159
|
+
if (spec.type !== 'ExportSpecifier') continue;
|
|
160
|
+
const exported = spec.exported.name;
|
|
161
|
+
const imported = spec.local.name;
|
|
162
|
+
let rhs = `${modIdent}.${imported}`;
|
|
163
|
+
if (imported === 'default') {
|
|
164
|
+
rhs = `${defaultInteropName}(${modIdent})`;
|
|
165
|
+
needsInterop = true;
|
|
166
|
+
}
|
|
167
|
+
lines.push(exportAssignment(exported, rhs, live));
|
|
168
|
+
}
|
|
169
|
+
exportTransforms.push({
|
|
170
|
+
start: node.start,
|
|
171
|
+
end: node.end,
|
|
172
|
+
code: `${lines.join('\n')}\n`,
|
|
173
|
+
needsInterop
|
|
174
|
+
});
|
|
175
|
+
} else {
|
|
176
|
+
const lines = [];
|
|
177
|
+
for (const spec of node.specifiers) {
|
|
178
|
+
if (spec.type !== 'ExportSpecifier') continue;
|
|
179
|
+
const exported = spec.exported.name;
|
|
180
|
+
const local = spec.local.name;
|
|
181
|
+
lines.push(exportAssignment(exported, local, live));
|
|
182
|
+
}
|
|
183
|
+
exportTransforms.push({
|
|
184
|
+
start: node.start,
|
|
185
|
+
end: node.end,
|
|
186
|
+
code: `${lines.join('\n')}\n`
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (node.type === 'ExportDefaultDeclaration') {
|
|
192
|
+
const decl = node.declaration;
|
|
193
|
+
const useExportsObject = containsTopLevelAwait && opts.topLevelAwait !== 'error';
|
|
194
|
+
if (decl.type === 'FunctionDeclaration' || decl.type === 'ClassDeclaration') {
|
|
195
|
+
if (decl.id?.name) {
|
|
196
|
+
const declSrc = code.slice(decl.start, decl.end);
|
|
197
|
+
const assign = useExportsObject ? `exports.default = ${decl.id.name};` : `module.exports = ${decl.id.name};`;
|
|
198
|
+
exportTransforms.push({
|
|
199
|
+
start: node.start,
|
|
200
|
+
end: node.end,
|
|
201
|
+
code: `${declSrc}\n${assign}\n`
|
|
202
|
+
});
|
|
203
|
+
} else {
|
|
204
|
+
const declSrc = code.slice(decl.start, decl.end);
|
|
205
|
+
const assign = useExportsObject ? `exports.default = ${declSrc};` : `module.exports = ${declSrc};`;
|
|
206
|
+
exportTransforms.push({
|
|
207
|
+
start: node.start,
|
|
208
|
+
end: node.end,
|
|
209
|
+
code: `${assign}\n`
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
} else {
|
|
213
|
+
const exprSrc = code.slice(decl.start, decl.end);
|
|
214
|
+
const assign = useExportsObject ? `exports.default = ${exprSrc};` : `module.exports = ${exprSrc};`;
|
|
215
|
+
exportTransforms.push({
|
|
216
|
+
start: node.start,
|
|
217
|
+
end: node.end,
|
|
218
|
+
code: `${assign}\n`
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (node.type === 'ExportAllDeclaration') {
|
|
223
|
+
const srcLiteral = code.slice(node.source.start, node.source.end);
|
|
224
|
+
if (node.exported) {
|
|
225
|
+
const exported = node.exported.name;
|
|
226
|
+
const modIdent = `__mod${importIndex++}`;
|
|
227
|
+
const lines = [`const ${modIdent} = require(${srcLiteral});`, exportAssignment(exported, modIdent, live)];
|
|
228
|
+
exportTransforms.push({
|
|
229
|
+
start: node.start,
|
|
230
|
+
end: node.end,
|
|
231
|
+
code: `${lines.join('\n')}\n`
|
|
232
|
+
});
|
|
233
|
+
} else {
|
|
234
|
+
const modIdent = `__mod${importIndex++}`;
|
|
235
|
+
const lines = [`const ${modIdent} = require(${srcLiteral});`];
|
|
236
|
+
const loop = `for (const k in ${modIdent}) {\n if (k === 'default') continue;\n if (!Object.prototype.hasOwnProperty.call(${modIdent}, k)) continue;\n Object.defineProperty(exports, k, { enumerable: true, get: () => ${modIdent}[k] });\n}`;
|
|
237
|
+
lines.push(loop);
|
|
238
|
+
exportTransforms.push({
|
|
239
|
+
start: node.start,
|
|
240
|
+
end: node.end,
|
|
241
|
+
code: `${lines.join('\n')}\n`
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
importTransforms,
|
|
248
|
+
exportTransforms,
|
|
249
|
+
needsInterop
|
|
250
|
+
};
|
|
251
|
+
};
|
|
16
252
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
253
|
+
* Node added support for import.meta.main.
|
|
254
|
+
* Added in: v24.2.0, v22.18.0
|
|
255
|
+
* @see https://nodejs.org/api/esm.html#importmetamain
|
|
19
256
|
*/
|
|
20
257
|
const format = async (src, ast, opts) => {
|
|
21
258
|
const code = new _magicString.default(src);
|
|
@@ -25,14 +262,22 @@ const format = async (src, ast, opts) => {
|
|
|
25
262
|
hasDefaultExportBeenReassigned: false,
|
|
26
263
|
hasDefaultExportBeenAssigned: false
|
|
27
264
|
};
|
|
28
|
-
await (0,
|
|
265
|
+
const exportTable = opts.target === 'module' ? await (0, _exports.collectCjsExports)(ast.program) : null;
|
|
266
|
+
await (0, _identifiers.collectModuleIdentifiers)(ast.program);
|
|
267
|
+
const shouldCheckTopLevelAwait = opts.target === 'commonjs' && opts.transformSyntax;
|
|
268
|
+
const containsTopLevelAwait = shouldCheckTopLevelAwait ? hasTopLevelAwait(ast.program) : false;
|
|
269
|
+
const shouldLowerCjs = opts.target === 'commonjs' && opts.transformSyntax;
|
|
270
|
+
let pendingCjsTransforms = null;
|
|
271
|
+
if (shouldLowerCjs && opts.topLevelAwait === 'error' && containsTopLevelAwait) {
|
|
272
|
+
throw new Error('Top-level await is not supported when targeting CommonJS (set topLevelAwait to "wrap" or "preserve" to override).');
|
|
273
|
+
}
|
|
29
274
|
if (opts.target === 'module' && opts.transformSyntax) {
|
|
30
275
|
/**
|
|
31
276
|
* Prepare ESM output by renaming `exports` to `__exports` and seeding an
|
|
32
277
|
* `import.meta.filename` touch so import.meta is present even when the
|
|
33
278
|
* original source never referenced it.
|
|
34
279
|
*/
|
|
35
|
-
code.prepend(`let ${
|
|
280
|
+
code.prepend(`let ${_exports.exportsRename} = {};
|
|
36
281
|
void import.meta.filename;
|
|
37
282
|
`);
|
|
38
283
|
}
|
|
@@ -53,7 +298,7 @@ void import.meta.filename;
|
|
|
53
298
|
*/
|
|
54
299
|
if (node.type === 'AssignmentExpression' && node.left.type === 'MemberExpression' && node.left.object.type === 'MetaProperty' && node.left.property.type === 'Identifier' && node.left.property.name === 'url') {
|
|
55
300
|
if (node.right.type === 'Literal' && typeof node.right.value === 'string') {
|
|
56
|
-
if (!(0,
|
|
301
|
+
if (!(0, _url.isValidUrl)(node.right.value)) {
|
|
57
302
|
const rhs = code.snip(node.right.start, node.right.end).toString();
|
|
58
303
|
const assignment = code.snip(node.start, node.end).toString();
|
|
59
304
|
code.update(node.start, node.end, `/* Invalid assignment: ${rhs} is not a URL. ${assignment} */`);
|
|
@@ -110,6 +355,64 @@ void import.meta.filename;
|
|
|
110
355
|
}
|
|
111
356
|
}
|
|
112
357
|
});
|
|
358
|
+
if (shouldLowerCjs) {
|
|
359
|
+
const {
|
|
360
|
+
importTransforms,
|
|
361
|
+
exportTransforms,
|
|
362
|
+
needsInterop
|
|
363
|
+
} = lowerEsmToCjs(ast.program, code, opts, containsTopLevelAwait);
|
|
364
|
+
pendingCjsTransforms = {
|
|
365
|
+
transforms: [...importTransforms, ...exportTransforms].sort((a, b) => a.start - b.start),
|
|
366
|
+
needsInterop
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
if (pendingCjsTransforms) {
|
|
370
|
+
for (const t of pendingCjsTransforms.transforms) {
|
|
371
|
+
code.overwrite(t.start, t.end, t.code);
|
|
372
|
+
}
|
|
373
|
+
if (pendingCjsTransforms.needsInterop) {
|
|
374
|
+
code.prepend(`${interopHelper}exports.__esModule = true;\n`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (opts.target === 'module' && opts.transformSyntax && exportTable) {
|
|
378
|
+
const isValidExportName = name => /^[$A-Z_a-z][$\w]*$/.test(name);
|
|
379
|
+
const asExportName = name => isValidExportName(name) ? name : JSON.stringify(name);
|
|
380
|
+
const accessProp = name => isValidExportName(name) ? `${_exports.exportsRename}.${name}` : `${_exports.exportsRename}[${JSON.stringify(name)}]`;
|
|
381
|
+
const tempNameFor = name => {
|
|
382
|
+
const sanitized = name.replace(/[^$\w]/g, '_') || 'value';
|
|
383
|
+
const safe = /^[0-9]/.test(sanitized) ? `_${sanitized}` : sanitized;
|
|
384
|
+
return `__export_${safe}`;
|
|
385
|
+
};
|
|
386
|
+
const lines = [];
|
|
387
|
+
const defaultEntry = exportTable.get('default');
|
|
388
|
+
if (defaultEntry) {
|
|
389
|
+
const def = defaultEntry.fromIdentifier ?? _exports.exportsRename;
|
|
390
|
+
lines.push(`export default ${def};`);
|
|
391
|
+
}
|
|
392
|
+
for (const [key, entry] of exportTable) {
|
|
393
|
+
if (key === 'default') continue;
|
|
394
|
+
if (entry.fromIdentifier) {
|
|
395
|
+
lines.push(`export { ${entry.fromIdentifier} as ${asExportName(key)} };`);
|
|
396
|
+
} else {
|
|
397
|
+
const temp = tempNameFor(key);
|
|
398
|
+
lines.push(`const ${temp} = ${accessProp(key)};`);
|
|
399
|
+
lines.push(`export { ${temp} as ${asExportName(key)} };`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
if (lines.length) {
|
|
403
|
+
code.append(`\n${lines.join('\n')}\n`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (opts.target === 'commonjs' && opts.transformSyntax && containsTopLevelAwait) {
|
|
407
|
+
const body = code.toString();
|
|
408
|
+
if (opts.topLevelAwait === 'wrap') {
|
|
409
|
+
const tlaPromise = `const __tla = (async () => {\n${body}\nreturn module.exports;\n})();\n`;
|
|
410
|
+
const setPromise = `const __setTla = target => {\n if (!target) return;\n const type = typeof target;\n if (type !== 'object' && type !== 'function') return;\n target.__tla = __tla;\n};\n`;
|
|
411
|
+
const attach = `__setTla(module.exports);\n__tla.then(resolved => __setTla(resolved), err => { throw err; });\n`;
|
|
412
|
+
return `${tlaPromise}${setPromise}${attach}`;
|
|
413
|
+
}
|
|
414
|
+
return `;(async () => {\n${body}\n})();\n`;
|
|
415
|
+
}
|
|
113
416
|
return code.toString();
|
|
114
417
|
};
|
|
115
418
|
exports.format = format;
|
package/dist/cjs/format.d.cts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { ParseResult } from 'oxc-parser';
|
|
2
2
|
import type { FormatterOptions } from './types.cjs';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Node added support for import.meta.main.
|
|
5
|
+
* Added in: v24.2.0, v22.18.0
|
|
6
|
+
* @see https://nodejs.org/api/esm.html#importmetamain
|
|
6
7
|
*/
|
|
7
8
|
declare const format: (src: string, ast: ParseResult, opts: FormatterOptions) => Promise<string>;
|
|
8
9
|
export { format };
|
|
@@ -4,8 +4,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.identifier = void 0;
|
|
7
|
-
var
|
|
8
|
-
var _identifier = require("
|
|
7
|
+
var _exports = require("#utils/exports.js");
|
|
8
|
+
var _identifier = require("#helpers/identifier.js");
|
|
9
9
|
const identifier = ({
|
|
10
10
|
node,
|
|
11
11
|
ancestors,
|
|
@@ -34,10 +34,10 @@ const identifier = ({
|
|
|
34
34
|
// The code is reassigning `exports` to something else.
|
|
35
35
|
|
|
36
36
|
meta.hasExportsBeenReassigned = true;
|
|
37
|
-
code.update(parent.left.start, parent.left.end,
|
|
37
|
+
code.update(parent.left.start, parent.left.end, _exports.exportsRename);
|
|
38
38
|
}
|
|
39
39
|
if (_identifier.identifier.isModuleScope(ancestors) && !_identifier.identifier.isFunctionExpressionId(ancestors) && !_identifier.identifier.isExportSpecifierAlias(ancestors) && !_identifier.identifier.isClassPropertyKey(ancestors) && !_identifier.identifier.isMethodDefinitionKey(ancestors) && !_identifier.identifier.isMemberKey(ancestors) && !_identifier.identifier.isPropertyKey(ancestors) && !_identifier.identifier.isIife(ancestors)) {
|
|
40
|
-
code.update(start, end,
|
|
40
|
+
code.update(start, end, _exports.exportsRename);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -4,11 +4,11 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.memberExpression = void 0;
|
|
7
|
-
var
|
|
7
|
+
var _exports = require("#utils/exports.js");
|
|
8
8
|
const memberExpression = (node, parent, src, options) => {
|
|
9
9
|
if (options.target === 'module') {
|
|
10
10
|
if (node.object.type === 'Identifier' && node.property.type === 'Identifier' && node.object.name === 'module' && node.property.name === 'exports') {
|
|
11
|
-
src.update(node.start, node.end,
|
|
11
|
+
src.update(node.start, node.end, _exports.exportsRename);
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
if (node.object.type === 'Identifier' && node.property.type === 'Identifier' && node.object.name === 'require') {
|
|
@@ -4,6 +4,19 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.metaProperty = void 0;
|
|
7
|
+
const importMetaMainSupport = '(() => { const [__nmaj, __nmin] = process.versions.node.split(".").map(n => parseInt(n, 10) || 0); return (__nmaj > 24 || (__nmaj === 24 && __nmin >= 2) || (__nmaj === 22 && __nmin >= 18)); })()';
|
|
8
|
+
const importMetaMainShim = 'process.argv[1] === __filename';
|
|
9
|
+
const importMetaMainExpr = mode => {
|
|
10
|
+
switch (mode) {
|
|
11
|
+
case 'warn':
|
|
12
|
+
return `(${importMetaMainSupport} ? ${importMetaMainShim} : (console.warn("import.meta.main is not supported before Node 22.18/24.2; falling back to shim."), ${importMetaMainShim}))`;
|
|
13
|
+
case 'error':
|
|
14
|
+
return `(${importMetaMainSupport} ? ${importMetaMainShim} : (() => { throw new Error("import.meta.main is not supported before Node 22.18/24.2"); })())`;
|
|
15
|
+
case 'shim':
|
|
16
|
+
default:
|
|
17
|
+
return importMetaMainShim;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
7
20
|
const metaProperty = (node, parent, src, options) => {
|
|
8
21
|
if (options.target === 'commonjs') {
|
|
9
22
|
if (parent?.type !== 'MemberExpression') {
|
|
@@ -27,10 +40,15 @@ const metaProperty = (node, parent, src, options) => {
|
|
|
27
40
|
break;
|
|
28
41
|
case 'resolve':
|
|
29
42
|
/**
|
|
30
|
-
*
|
|
43
|
+
* Map to require.resolve intentionally: matches CJS resolution semantics.
|
|
44
|
+
* Wrapping in pathToFileURL(...) would change the return shape (URL string)
|
|
45
|
+
* without truly emulating ESM import.meta.resolve rules.
|
|
31
46
|
*/
|
|
32
47
|
src.update(parent.start, parent.end, 'require.resolve');
|
|
33
48
|
break;
|
|
49
|
+
case 'main':
|
|
50
|
+
src.update(parent.start, parent.end, importMetaMainExpr(options.importMetaMain));
|
|
51
|
+
break;
|
|
34
52
|
}
|
|
35
53
|
}
|
|
36
54
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Node } from 'oxc-parser';
|
|
2
|
+
import type { IdentMeta, Scope } from '../types.cjs';
|
|
3
|
+
declare const collectScopeIdentifiers: (node: Node, scopes: Scope[]) => void;
|
|
4
|
+
/**
|
|
5
|
+
* Collects all module scope identifiers in the AST.
|
|
6
|
+
*
|
|
7
|
+
* Ignores identifiers that are in functions or classes.
|
|
8
|
+
* Ignores new scopes for StaticBlock nodes (can only reference static class members).
|
|
9
|
+
*
|
|
10
|
+
* Special case handling for these which create their own scopes,
|
|
11
|
+
* but are also valid module scope identifiers:
|
|
12
|
+
* - ClassDeclaration
|
|
13
|
+
* - FunctionDeclaration
|
|
14
|
+
*
|
|
15
|
+
* Special case handling for var inside BlockStatement
|
|
16
|
+
* which are also valid module scope identifiers.
|
|
17
|
+
*/
|
|
18
|
+
declare const collectModuleIdentifiers: (ast: Node, hoisting?: boolean) => Promise<Map<string, IdentMeta>>;
|
|
19
|
+
export { collectScopeIdentifiers, collectModuleIdentifiers };
|
package/dist/cjs/module.cjs
CHANGED
|
@@ -7,9 +7,9 @@ exports.transform = void 0;
|
|
|
7
7
|
var _nodePath = require("node:path");
|
|
8
8
|
var _promises = require("node:fs/promises");
|
|
9
9
|
var _specifier = require("@knighted/specifier");
|
|
10
|
-
var _parse = require("
|
|
11
|
-
var _format = require("
|
|
12
|
-
var
|
|
10
|
+
var _parse = require("#parse");
|
|
11
|
+
var _format = require("#format");
|
|
12
|
+
var _lang = require("#utils/lang.js");
|
|
13
13
|
const defaultOptions = {
|
|
14
14
|
target: 'commonjs',
|
|
15
15
|
sourceType: 'auto',
|
|
@@ -18,6 +18,7 @@ const defaultOptions = {
|
|
|
18
18
|
rewriteSpecifier: undefined,
|
|
19
19
|
dirFilename: 'inject',
|
|
20
20
|
importMeta: 'shim',
|
|
21
|
+
importMetaMain: 'shim',
|
|
21
22
|
requireSource: 'builtin',
|
|
22
23
|
cjsDefault: 'auto',
|
|
23
24
|
topLevelAwait: 'error',
|
|
@@ -34,7 +35,7 @@ const transform = async (filename, options = defaultOptions) => {
|
|
|
34
35
|
const ast = (0, _parse.parse)(filename, code);
|
|
35
36
|
let source = await (0, _format.format)(code, ast, opts);
|
|
36
37
|
if (opts.rewriteSpecifier) {
|
|
37
|
-
const code = await _specifier.specifier.updateSrc(source, (0,
|
|
38
|
+
const code = await _specifier.specifier.updateSrc(source, (0, _lang.getLangFromExt)(filename), ({
|
|
38
39
|
value
|
|
39
40
|
}) => {
|
|
40
41
|
if (typeof opts.rewriteSpecifier === 'function') {
|
package/dist/cjs/types.d.cts
CHANGED
|
@@ -8,6 +8,7 @@ export type ModuleOptions = {
|
|
|
8
8
|
rewriteSpecifier?: RewriteSpecifier;
|
|
9
9
|
dirFilename?: 'inject' | 'preserve' | 'error';
|
|
10
10
|
importMeta?: 'preserve' | 'shim' | 'error';
|
|
11
|
+
importMetaMain?: 'shim' | 'warn' | 'error';
|
|
11
12
|
requireSource?: 'builtin' | 'create-require';
|
|
12
13
|
cjsDefault?: 'module-exports' | 'auto' | 'none';
|
|
13
14
|
topLevelAwait?: 'error' | 'wrap' | 'preserve';
|
|
@@ -21,6 +22,14 @@ export type ExportsMeta = {
|
|
|
21
22
|
hasDefaultExportBeenAssigned: boolean;
|
|
22
23
|
defaultExportValue: unknown;
|
|
23
24
|
};
|
|
25
|
+
export type CjsExport = {
|
|
26
|
+
key: string;
|
|
27
|
+
writes: SpannedNode[];
|
|
28
|
+
fromIdentifier?: string;
|
|
29
|
+
via: Set<'exports' | 'module.exports'>;
|
|
30
|
+
reassignments: SpannedNode[];
|
|
31
|
+
hasGetter?: boolean;
|
|
32
|
+
};
|
|
24
33
|
export type IdentMeta = {
|
|
25
34
|
declare: SpannedNode[];
|
|
26
35
|
read: SpannedNode[];
|