@knighted/module 1.0.0-beta.2 → 1.0.0-beta.4
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 +2 -2
- package/dist/cjs/format.cjs +144 -12
- package/dist/cjs/formatters/identifier.cjs +5 -1
- package/dist/cjs/formatters/memberExpression.cjs +12 -12
- package/dist/cjs/memberExpression.d.cts +1 -1
- package/dist/cjs/scopeNodes.d.cts +2 -0
- package/dist/cjs/utils/identifiers.cjs +2 -2
- package/dist/cjs/utils/scopeNodes.cjs +7 -0
- package/dist/cjs/utils.cjs +2 -2
- package/dist/format.js +144 -12
- package/dist/formatters/identifier.js +5 -1
- package/dist/formatters/memberExpression.js +12 -12
- package/dist/memberExpression.d.ts +1 -1
- package/dist/scopeNodes.d.ts +2 -0
- package/dist/src/formatters/identifier.d.ts +2 -1
- package/dist/src/formatters/memberExpression.d.ts +1 -1
- package/dist/src/utils/scopeNodes.d.ts +2 -0
- package/dist/utils/identifiers.js +1 -1
- package/dist/utils/scopeNodes.js +2 -0
- package/dist/utils.js +1 -1
- package/package.json +2 -2
- package/dist/cjs/helpers/scope.cjs +0 -12
- package/dist/cjs/scope.d.cts +0 -6
- package/dist/helpers/scope.js +0 -7
- package/dist/scope.d.ts +0 -6
- package/dist/src/helpers/scope.d.ts +0 -6
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ By default `@knighted/module` transforms the one-to-one [differences between ES
|
|
|
22
22
|
|
|
23
23
|
## Requirements
|
|
24
24
|
|
|
25
|
-
- Node >=
|
|
25
|
+
- Node >= 22.21.1
|
|
26
26
|
|
|
27
27
|
## Install
|
|
28
28
|
|
|
@@ -134,7 +134,7 @@ Behavior notes (defaults in parentheses)
|
|
|
134
134
|
- `cjsDefault` (`auto`): bundler-style default interop vs direct `module.exports`.
|
|
135
135
|
- `out`/`inPlace`: write the transformed code to a file; otherwise the function returns the transformed string only.
|
|
136
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.
|
|
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. For CommonJS to ESM lowering details, read [docs/cjs-to-esm.md](docs/cjs-to-esm.md).
|
|
138
138
|
|
|
139
139
|
## Roadmap
|
|
140
140
|
|
package/dist/cjs/format.cjs
CHANGED
|
@@ -26,6 +26,79 @@ const exportAssignment = (name, expr, live) => {
|
|
|
26
26
|
};
|
|
27
27
|
const defaultInteropName = '__interopDefault';
|
|
28
28
|
const interopHelper = `const ${defaultInteropName} = mod => (mod && mod.__esModule ? mod.default : mod);\n`;
|
|
29
|
+
const isRequireCallee = (callee, shadowed) => {
|
|
30
|
+
if (callee.type === 'Identifier' && callee.name === 'require' && !shadowed.has('require')) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
if (callee.type === 'MemberExpression' && callee.object.type === 'Identifier' && callee.object.name === 'module' && !shadowed.has('module') && callee.property.type === 'Identifier' && callee.property.name === 'require') {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
};
|
|
38
|
+
const isStaticRequire = (node, shadowed) => node.type === 'CallExpression' && isRequireCallee(node.callee, shadowed) && node.arguments.length === 1 && node.arguments[0].type === 'Literal' && typeof node.arguments[0].value === 'string';
|
|
39
|
+
const isRequireCall = (node, shadowed) => node.type === 'CallExpression' && isRequireCallee(node.callee, shadowed);
|
|
40
|
+
const lowerCjsRequireToImports = (program, code, shadowed) => {
|
|
41
|
+
const transforms = [];
|
|
42
|
+
const imports = [];
|
|
43
|
+
let nsIndex = 0;
|
|
44
|
+
let needsCreateRequire = false;
|
|
45
|
+
for (const stmt of program.body) {
|
|
46
|
+
if (stmt.type === 'VariableDeclaration') {
|
|
47
|
+
const decls = stmt.declarations;
|
|
48
|
+
const allStatic = decls.length > 0 && decls.every(decl => decl.init && isStaticRequire(decl.init, shadowed));
|
|
49
|
+
if (allStatic) {
|
|
50
|
+
for (const decl of decls) {
|
|
51
|
+
const init = decl.init;
|
|
52
|
+
const source = code.slice(init.arguments[0].start, init.arguments[0].end);
|
|
53
|
+
if (decl.id.type === 'Identifier') {
|
|
54
|
+
imports.push(`import * as ${decl.id.name} from ${source};\n`);
|
|
55
|
+
} else if (decl.id.type === 'ObjectPattern') {
|
|
56
|
+
const ns = `__cjsImport${nsIndex++}`;
|
|
57
|
+
const pattern = code.slice(decl.id.start, decl.id.end);
|
|
58
|
+
imports.push(`import * as ${ns} from ${source};\n`);
|
|
59
|
+
imports.push(`const ${pattern} = ${ns};\n`);
|
|
60
|
+
} else {
|
|
61
|
+
needsCreateRequire = true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
transforms.push({
|
|
65
|
+
start: stmt.start,
|
|
66
|
+
end: stmt.end,
|
|
67
|
+
code: ';\n'
|
|
68
|
+
});
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
for (const decl of decls) {
|
|
72
|
+
const init = decl.init;
|
|
73
|
+
if (init && isRequireCall(init, shadowed)) {
|
|
74
|
+
needsCreateRequire = true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (stmt.type === 'ExpressionStatement') {
|
|
79
|
+
const expr = stmt.expression;
|
|
80
|
+
if (expr && isStaticRequire(expr, shadowed)) {
|
|
81
|
+
const source = code.slice(expr.arguments[0].start, expr.arguments[0].end);
|
|
82
|
+
imports.push(`import ${source};\n`);
|
|
83
|
+
transforms.push({
|
|
84
|
+
start: stmt.start,
|
|
85
|
+
end: stmt.end,
|
|
86
|
+
code: ';\n'
|
|
87
|
+
});
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (expr && isRequireCall(expr, shadowed)) {
|
|
91
|
+
needsCreateRequire = true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
transforms,
|
|
97
|
+
imports,
|
|
98
|
+
needsCreateRequire
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
const isRequireMainMember = (node, shadowed) => node && node.type === 'MemberExpression' && node.object.type === 'Identifier' && node.object.name === 'require' && !shadowed.has('require') && node.property.type === 'Identifier' && node.property.name === 'main';
|
|
29
102
|
const hasTopLevelAwait = program => {
|
|
30
103
|
let found = false;
|
|
31
104
|
const walkNode = (node, inFunction) => {
|
|
@@ -262,28 +335,67 @@ const format = async (src, ast, opts) => {
|
|
|
262
335
|
hasDefaultExportBeenReassigned: false,
|
|
263
336
|
hasDefaultExportBeenAssigned: false
|
|
264
337
|
};
|
|
338
|
+
const moduleIdentifiers = await (0, _identifiers.collectModuleIdentifiers)(ast.program);
|
|
339
|
+
const shadowedBindings = new Set([...moduleIdentifiers.entries()].filter(([, meta]) => meta.declare.length > 0).map(([name]) => name));
|
|
340
|
+
if (opts.target === 'module' && opts.transformSyntax) {
|
|
341
|
+
if (shadowedBindings.has('module') || shadowedBindings.has('exports')) {
|
|
342
|
+
throw new Error('Cannot transform to ESM: module or exports is shadowed in module scope.');
|
|
343
|
+
}
|
|
344
|
+
}
|
|
265
345
|
const exportTable = opts.target === 'module' ? await (0, _exports.collectCjsExports)(ast.program) : null;
|
|
266
|
-
await (0, _identifiers.collectModuleIdentifiers)(ast.program);
|
|
267
346
|
const shouldCheckTopLevelAwait = opts.target === 'commonjs' && opts.transformSyntax;
|
|
268
347
|
const containsTopLevelAwait = shouldCheckTopLevelAwait ? hasTopLevelAwait(ast.program) : false;
|
|
269
348
|
const shouldLowerCjs = opts.target === 'commonjs' && opts.transformSyntax;
|
|
349
|
+
const shouldRaiseEsm = opts.target === 'module' && opts.transformSyntax;
|
|
350
|
+
let hoistedImports = [];
|
|
351
|
+
let pendingRequireTransforms = [];
|
|
352
|
+
let needsCreateRequire = false;
|
|
270
353
|
let pendingCjsTransforms = null;
|
|
271
354
|
if (shouldLowerCjs && opts.topLevelAwait === 'error' && containsTopLevelAwait) {
|
|
272
355
|
throw new Error('Top-level await is not supported when targeting CommonJS (set topLevelAwait to "wrap" or "preserve" to override).');
|
|
273
356
|
}
|
|
274
|
-
if (
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
357
|
+
if (shouldRaiseEsm) {
|
|
358
|
+
const {
|
|
359
|
+
transforms,
|
|
360
|
+
imports,
|
|
361
|
+
needsCreateRequire: reqCreate
|
|
362
|
+
} = lowerCjsRequireToImports(ast.program, code, shadowedBindings);
|
|
363
|
+
pendingRequireTransforms = transforms;
|
|
364
|
+
hoistedImports = imports;
|
|
365
|
+
needsCreateRequire = reqCreate;
|
|
283
366
|
}
|
|
284
367
|
await (0, _walk.ancestorWalk)(ast.program, {
|
|
285
368
|
async enter(node, ancestors) {
|
|
286
369
|
const parent = ancestors[ancestors.length - 2] ?? null;
|
|
370
|
+
if (shouldRaiseEsm && node.type === 'BinaryExpression') {
|
|
371
|
+
const op = node.operator;
|
|
372
|
+
const isEquality = op === '===' || op === '==' || op === '!==' || op === '!=';
|
|
373
|
+
if (isEquality) {
|
|
374
|
+
const leftMain = isRequireMainMember(node.left, shadowedBindings);
|
|
375
|
+
const rightMain = isRequireMainMember(node.right, shadowedBindings);
|
|
376
|
+
const leftModule = node.left.type === 'Identifier' && node.left.name === 'module' && !shadowedBindings.has('module');
|
|
377
|
+
const rightModule = node.right.type === 'Identifier' && node.right.name === 'module' && !shadowedBindings.has('module');
|
|
378
|
+
if (leftMain && rightModule || rightMain && leftModule) {
|
|
379
|
+
const negate = op === '!==' || op === '!=';
|
|
380
|
+
code.update(node.start, node.end, negate ? '!import.meta.main' : 'import.meta.main');
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (shouldRaiseEsm && node.type === 'CallExpression' && isRequireCall(node, shadowedBindings)) {
|
|
386
|
+
const isStatic = isStaticRequire(node, shadowedBindings);
|
|
387
|
+
const parent = ancestors[ancestors.length - 2] ?? null;
|
|
388
|
+
const grandparent = ancestors[ancestors.length - 3] ?? null;
|
|
389
|
+
const greatGrandparent = ancestors[ancestors.length - 4] ?? null;
|
|
390
|
+
|
|
391
|
+
// Hoistable cases are handled separately and don't need createRequire.
|
|
392
|
+
const topLevelExprStmt = parent?.type === 'ExpressionStatement' && grandparent?.type === 'Program';
|
|
393
|
+
const topLevelVarDecl = parent?.type === 'VariableDeclarator' && grandparent?.type === 'VariableDeclaration' && greatGrandparent?.type === 'Program';
|
|
394
|
+
const hoistableTopLevel = isStatic && (topLevelExprStmt || topLevelVarDecl);
|
|
395
|
+
if (!isStatic || !hoistableTopLevel) {
|
|
396
|
+
needsCreateRequire = true;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
287
399
|
if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
|
|
288
400
|
const skipped = ['__filename', '__dirname'];
|
|
289
401
|
const skippedParams = node.params.filter(param => param.type === 'Identifier' && skipped.includes(param.name));
|
|
@@ -342,7 +454,7 @@ void import.meta.filename;
|
|
|
342
454
|
(0, _metaProperty.metaProperty)(node, parent, code, opts);
|
|
343
455
|
}
|
|
344
456
|
if (node.type === 'MemberExpression') {
|
|
345
|
-
(0, _memberExpression.memberExpression)(node, parent, code, opts);
|
|
457
|
+
(0, _memberExpression.memberExpression)(node, parent, code, opts, shadowedBindings);
|
|
346
458
|
}
|
|
347
459
|
if ((0, _identifier2.isIdentifierName)(node)) {
|
|
348
460
|
(0, _identifier.identifier)({
|
|
@@ -350,11 +462,17 @@ void import.meta.filename;
|
|
|
350
462
|
ancestors,
|
|
351
463
|
code,
|
|
352
464
|
opts,
|
|
353
|
-
meta: exportsMeta
|
|
465
|
+
meta: exportsMeta,
|
|
466
|
+
shadowed: shadowedBindings
|
|
354
467
|
});
|
|
355
468
|
}
|
|
356
469
|
}
|
|
357
470
|
});
|
|
471
|
+
if (pendingRequireTransforms.length) {
|
|
472
|
+
for (const t of pendingRequireTransforms) {
|
|
473
|
+
code.overwrite(t.start, t.end, t.code);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
358
476
|
if (shouldLowerCjs) {
|
|
359
477
|
const {
|
|
360
478
|
importTransforms,
|
|
@@ -403,6 +521,20 @@ void import.meta.filename;
|
|
|
403
521
|
code.append(`\n${lines.join('\n')}\n`);
|
|
404
522
|
}
|
|
405
523
|
}
|
|
524
|
+
if (shouldRaiseEsm && opts.transformSyntax) {
|
|
525
|
+
const importPrelude = [];
|
|
526
|
+
if (needsCreateRequire) {
|
|
527
|
+
importPrelude.push('import { createRequire } from "node:module";\n');
|
|
528
|
+
}
|
|
529
|
+
if (hoistedImports.length) {
|
|
530
|
+
importPrelude.push(...hoistedImports);
|
|
531
|
+
}
|
|
532
|
+
const requireInit = needsCreateRequire ? 'const require = createRequire(import.meta.url);\n' : '';
|
|
533
|
+
const prelude = `${importPrelude.join('')}${importPrelude.length ? '\n' : ''}${requireInit}let ${_exports.exportsRename} = {};
|
|
534
|
+
void import.meta.filename;
|
|
535
|
+
`;
|
|
536
|
+
code.prepend(prelude);
|
|
537
|
+
}
|
|
406
538
|
if (opts.target === 'commonjs' && opts.transformSyntax && containsTopLevelAwait) {
|
|
407
539
|
const body = code.toString();
|
|
408
540
|
if (opts.topLevelAwait === 'wrap') {
|
|
@@ -11,7 +11,8 @@ const identifier = ({
|
|
|
11
11
|
ancestors,
|
|
12
12
|
code,
|
|
13
13
|
opts,
|
|
14
|
-
meta
|
|
14
|
+
meta,
|
|
15
|
+
shadowed
|
|
15
16
|
}) => {
|
|
16
17
|
if (opts.target === 'module') {
|
|
17
18
|
const {
|
|
@@ -19,6 +20,9 @@ const identifier = ({
|
|
|
19
20
|
end,
|
|
20
21
|
name
|
|
21
22
|
} = node;
|
|
23
|
+
if (shadowed?.has(name)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
22
26
|
switch (name) {
|
|
23
27
|
case '__filename':
|
|
24
28
|
code.update(start, end, 'import.meta.url');
|
|
@@ -5,8 +5,11 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.memberExpression = void 0;
|
|
7
7
|
var _exports = require("#utils/exports.js");
|
|
8
|
-
const memberExpression = (node, parent, src, options) => {
|
|
8
|
+
const memberExpression = (node, parent, src, options, shadowed) => {
|
|
9
9
|
if (options.target === 'module') {
|
|
10
|
+
if (node.object.type === 'Identifier' && shadowed?.has(node.object.name) || node.property.type === 'Identifier' && shadowed?.has(node.property.name)) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
10
13
|
if (node.object.type === 'Identifier' && node.property.type === 'Identifier' && node.object.name === 'module' && node.property.name === 'exports') {
|
|
11
14
|
src.update(node.start, node.end, _exports.exportsRename);
|
|
12
15
|
return;
|
|
@@ -23,18 +26,10 @@ const memberExpression = (node, parent, src, options) => {
|
|
|
23
26
|
// CommonJS properties of `require`
|
|
24
27
|
switch (name) {
|
|
25
28
|
case 'main':
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
* @see https://github.com/nodejs/node/pull/32223
|
|
29
|
-
*/
|
|
30
|
-
if (parent?.type === 'ExpressionStatement') {
|
|
31
|
-
// This is a standalone expression so remove it to not cause run-time errors.
|
|
32
|
-
src.remove(start, end);
|
|
29
|
+
if (parent?.type === 'BinaryExpression') {
|
|
30
|
+
return;
|
|
33
31
|
}
|
|
34
|
-
|
|
35
|
-
* Transform require.main === module.
|
|
36
|
-
*/
|
|
37
|
-
if (parent?.type === 'BinaryExpression') {}
|
|
32
|
+
src.update(start, end, 'import.meta.main');
|
|
38
33
|
break;
|
|
39
34
|
case 'resolve':
|
|
40
35
|
src.update(start, end, 'import.meta.resolve');
|
|
@@ -48,6 +43,11 @@ const memberExpression = (node, parent, src, options) => {
|
|
|
48
43
|
break;
|
|
49
44
|
}
|
|
50
45
|
}
|
|
46
|
+
if (node.object.type === 'Identifier' && node.property.type === 'Identifier' && node.object.name === 'module' && node.property.name === 'require') {
|
|
47
|
+
if (!shadowed?.has('module')) {
|
|
48
|
+
src.update(node.start, node.end, 'require');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
53
|
exports.memberExpression = memberExpression;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import MagicString from 'magic-string';
|
|
2
2
|
import type { MemberExpression, Node } from 'oxc-parser';
|
|
3
3
|
import type { FormatterOptions } from '../types.cjs';
|
|
4
|
-
export declare const memberExpression: (node: MemberExpression, parent: Node | null, src: MagicString, options: FormatterOptions) => void;
|
|
4
|
+
export declare const memberExpression: (node: MemberExpression, parent: Node | null, src: MagicString, options: FormatterOptions, shadowed?: Set<string>) => void;
|
|
@@ -5,8 +5,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.collectScopeIdentifiers = exports.collectModuleIdentifiers = void 0;
|
|
7
7
|
var _walk = require("#walk");
|
|
8
|
-
var _scope = require("#helpers/scope.js");
|
|
9
8
|
var _identifier = require("#helpers/identifier.js");
|
|
9
|
+
var _scopeNodes = require("./scopeNodes.cjs");
|
|
10
10
|
const collectScopeIdentifiers = (node, scopes) => {
|
|
11
11
|
const {
|
|
12
12
|
type
|
|
@@ -180,7 +180,7 @@ const collectModuleIdentifiers = async (ast, hoisting = true) => {
|
|
|
180
180
|
const {
|
|
181
181
|
type
|
|
182
182
|
} = node;
|
|
183
|
-
if (
|
|
183
|
+
if (_scopeNodes.scopeNodes.includes(type)) {
|
|
184
184
|
scopes.pop();
|
|
185
185
|
}
|
|
186
186
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.scopeNodes = void 0;
|
|
7
|
+
const scopeNodes = exports.scopeNodes = ['BlockStatement', 'FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression', 'ClassDeclaration', 'ClassExpression', 'ClassBody', 'StaticBlock'];
|
package/dist/cjs/utils.cjs
CHANGED
|
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.requireMainRgx = exports.isValidUrl = exports.getLangFromExt = exports.exportsRename = exports.collectScopeIdentifiers = exports.collectModuleIdentifiers = exports.collectCjsExports = void 0;
|
|
7
7
|
var _nodePath = require("node:path");
|
|
8
8
|
var _walk = require("./walk.cjs");
|
|
9
|
-
var _scope = require("./helpers/scope.cjs");
|
|
10
9
|
var _identifier = require("./helpers/identifier.cjs");
|
|
10
|
+
var _scopeNodes = require("./utils/scopeNodes.cjs");
|
|
11
11
|
const getLangFromExt = filename => {
|
|
12
12
|
const ext = (0, _nodePath.extname)(filename);
|
|
13
13
|
if (ext.endsWith('.js')) {
|
|
@@ -278,7 +278,7 @@ const collectModuleIdentifiers = async (ast, hoisting = true) => {
|
|
|
278
278
|
const {
|
|
279
279
|
type
|
|
280
280
|
} = node;
|
|
281
|
-
if (
|
|
281
|
+
if (_scopeNodes.scopeNodes.includes(type)) {
|
|
282
282
|
scopes.pop();
|
|
283
283
|
}
|
|
284
284
|
}
|
package/dist/format.js
CHANGED
|
@@ -19,6 +19,79 @@ const exportAssignment = (name, expr, live) => {
|
|
|
19
19
|
};
|
|
20
20
|
const defaultInteropName = '__interopDefault';
|
|
21
21
|
const interopHelper = `const ${defaultInteropName} = mod => (mod && mod.__esModule ? mod.default : mod);\n`;
|
|
22
|
+
const isRequireCallee = (callee, shadowed) => {
|
|
23
|
+
if (callee.type === 'Identifier' && callee.name === 'require' && !shadowed.has('require')) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
if (callee.type === 'MemberExpression' && callee.object.type === 'Identifier' && callee.object.name === 'module' && !shadowed.has('module') && callee.property.type === 'Identifier' && callee.property.name === 'require') {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
};
|
|
31
|
+
const isStaticRequire = (node, shadowed) => node.type === 'CallExpression' && isRequireCallee(node.callee, shadowed) && node.arguments.length === 1 && node.arguments[0].type === 'Literal' && typeof node.arguments[0].value === 'string';
|
|
32
|
+
const isRequireCall = (node, shadowed) => node.type === 'CallExpression' && isRequireCallee(node.callee, shadowed);
|
|
33
|
+
const lowerCjsRequireToImports = (program, code, shadowed) => {
|
|
34
|
+
const transforms = [];
|
|
35
|
+
const imports = [];
|
|
36
|
+
let nsIndex = 0;
|
|
37
|
+
let needsCreateRequire = false;
|
|
38
|
+
for (const stmt of program.body) {
|
|
39
|
+
if (stmt.type === 'VariableDeclaration') {
|
|
40
|
+
const decls = stmt.declarations;
|
|
41
|
+
const allStatic = decls.length > 0 && decls.every(decl => decl.init && isStaticRequire(decl.init, shadowed));
|
|
42
|
+
if (allStatic) {
|
|
43
|
+
for (const decl of decls) {
|
|
44
|
+
const init = decl.init;
|
|
45
|
+
const source = code.slice(init.arguments[0].start, init.arguments[0].end);
|
|
46
|
+
if (decl.id.type === 'Identifier') {
|
|
47
|
+
imports.push(`import * as ${decl.id.name} from ${source};\n`);
|
|
48
|
+
} else if (decl.id.type === 'ObjectPattern') {
|
|
49
|
+
const ns = `__cjsImport${nsIndex++}`;
|
|
50
|
+
const pattern = code.slice(decl.id.start, decl.id.end);
|
|
51
|
+
imports.push(`import * as ${ns} from ${source};\n`);
|
|
52
|
+
imports.push(`const ${pattern} = ${ns};\n`);
|
|
53
|
+
} else {
|
|
54
|
+
needsCreateRequire = true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
transforms.push({
|
|
58
|
+
start: stmt.start,
|
|
59
|
+
end: stmt.end,
|
|
60
|
+
code: ';\n'
|
|
61
|
+
});
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
for (const decl of decls) {
|
|
65
|
+
const init = decl.init;
|
|
66
|
+
if (init && isRequireCall(init, shadowed)) {
|
|
67
|
+
needsCreateRequire = true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (stmt.type === 'ExpressionStatement') {
|
|
72
|
+
const expr = stmt.expression;
|
|
73
|
+
if (expr && isStaticRequire(expr, shadowed)) {
|
|
74
|
+
const source = code.slice(expr.arguments[0].start, expr.arguments[0].end);
|
|
75
|
+
imports.push(`import ${source};\n`);
|
|
76
|
+
transforms.push({
|
|
77
|
+
start: stmt.start,
|
|
78
|
+
end: stmt.end,
|
|
79
|
+
code: ';\n'
|
|
80
|
+
});
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (expr && isRequireCall(expr, shadowed)) {
|
|
84
|
+
needsCreateRequire = true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
transforms,
|
|
90
|
+
imports,
|
|
91
|
+
needsCreateRequire
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
const isRequireMainMember = (node, shadowed) => node && node.type === 'MemberExpression' && node.object.type === 'Identifier' && node.object.name === 'require' && !shadowed.has('require') && node.property.type === 'Identifier' && node.property.name === 'main';
|
|
22
95
|
const hasTopLevelAwait = program => {
|
|
23
96
|
let found = false;
|
|
24
97
|
const walkNode = (node, inFunction) => {
|
|
@@ -255,28 +328,67 @@ const format = async (src, ast, opts) => {
|
|
|
255
328
|
hasDefaultExportBeenReassigned: false,
|
|
256
329
|
hasDefaultExportBeenAssigned: false
|
|
257
330
|
};
|
|
331
|
+
const moduleIdentifiers = await collectModuleIdentifiers(ast.program);
|
|
332
|
+
const shadowedBindings = new Set([...moduleIdentifiers.entries()].filter(([, meta]) => meta.declare.length > 0).map(([name]) => name));
|
|
333
|
+
if (opts.target === 'module' && opts.transformSyntax) {
|
|
334
|
+
if (shadowedBindings.has('module') || shadowedBindings.has('exports')) {
|
|
335
|
+
throw new Error('Cannot transform to ESM: module or exports is shadowed in module scope.');
|
|
336
|
+
}
|
|
337
|
+
}
|
|
258
338
|
const exportTable = opts.target === 'module' ? await collectCjsExports(ast.program) : null;
|
|
259
|
-
await collectModuleIdentifiers(ast.program);
|
|
260
339
|
const shouldCheckTopLevelAwait = opts.target === 'commonjs' && opts.transformSyntax;
|
|
261
340
|
const containsTopLevelAwait = shouldCheckTopLevelAwait ? hasTopLevelAwait(ast.program) : false;
|
|
262
341
|
const shouldLowerCjs = opts.target === 'commonjs' && opts.transformSyntax;
|
|
342
|
+
const shouldRaiseEsm = opts.target === 'module' && opts.transformSyntax;
|
|
343
|
+
let hoistedImports = [];
|
|
344
|
+
let pendingRequireTransforms = [];
|
|
345
|
+
let needsCreateRequire = false;
|
|
263
346
|
let pendingCjsTransforms = null;
|
|
264
347
|
if (shouldLowerCjs && opts.topLevelAwait === 'error' && containsTopLevelAwait) {
|
|
265
348
|
throw new Error('Top-level await is not supported when targeting CommonJS (set topLevelAwait to "wrap" or "preserve" to override).');
|
|
266
349
|
}
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
350
|
+
if (shouldRaiseEsm) {
|
|
351
|
+
const {
|
|
352
|
+
transforms,
|
|
353
|
+
imports,
|
|
354
|
+
needsCreateRequire: reqCreate
|
|
355
|
+
} = lowerCjsRequireToImports(ast.program, code, shadowedBindings);
|
|
356
|
+
pendingRequireTransforms = transforms;
|
|
357
|
+
hoistedImports = imports;
|
|
358
|
+
needsCreateRequire = reqCreate;
|
|
276
359
|
}
|
|
277
360
|
await ancestorWalk(ast.program, {
|
|
278
361
|
async enter(node, ancestors) {
|
|
279
362
|
const parent = ancestors[ancestors.length - 2] ?? null;
|
|
363
|
+
if (shouldRaiseEsm && node.type === 'BinaryExpression') {
|
|
364
|
+
const op = node.operator;
|
|
365
|
+
const isEquality = op === '===' || op === '==' || op === '!==' || op === '!=';
|
|
366
|
+
if (isEquality) {
|
|
367
|
+
const leftMain = isRequireMainMember(node.left, shadowedBindings);
|
|
368
|
+
const rightMain = isRequireMainMember(node.right, shadowedBindings);
|
|
369
|
+
const leftModule = node.left.type === 'Identifier' && node.left.name === 'module' && !shadowedBindings.has('module');
|
|
370
|
+
const rightModule = node.right.type === 'Identifier' && node.right.name === 'module' && !shadowedBindings.has('module');
|
|
371
|
+
if (leftMain && rightModule || rightMain && leftModule) {
|
|
372
|
+
const negate = op === '!==' || op === '!=';
|
|
373
|
+
code.update(node.start, node.end, negate ? '!import.meta.main' : 'import.meta.main');
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (shouldRaiseEsm && node.type === 'CallExpression' && isRequireCall(node, shadowedBindings)) {
|
|
379
|
+
const isStatic = isStaticRequire(node, shadowedBindings);
|
|
380
|
+
const parent = ancestors[ancestors.length - 2] ?? null;
|
|
381
|
+
const grandparent = ancestors[ancestors.length - 3] ?? null;
|
|
382
|
+
const greatGrandparent = ancestors[ancestors.length - 4] ?? null;
|
|
383
|
+
|
|
384
|
+
// Hoistable cases are handled separately and don't need createRequire.
|
|
385
|
+
const topLevelExprStmt = parent?.type === 'ExpressionStatement' && grandparent?.type === 'Program';
|
|
386
|
+
const topLevelVarDecl = parent?.type === 'VariableDeclarator' && grandparent?.type === 'VariableDeclaration' && greatGrandparent?.type === 'Program';
|
|
387
|
+
const hoistableTopLevel = isStatic && (topLevelExprStmt || topLevelVarDecl);
|
|
388
|
+
if (!isStatic || !hoistableTopLevel) {
|
|
389
|
+
needsCreateRequire = true;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
280
392
|
if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
|
|
281
393
|
const skipped = ['__filename', '__dirname'];
|
|
282
394
|
const skippedParams = node.params.filter(param => param.type === 'Identifier' && skipped.includes(param.name));
|
|
@@ -335,7 +447,7 @@ void import.meta.filename;
|
|
|
335
447
|
metaProperty(node, parent, code, opts);
|
|
336
448
|
}
|
|
337
449
|
if (node.type === 'MemberExpression') {
|
|
338
|
-
memberExpression(node, parent, code, opts);
|
|
450
|
+
memberExpression(node, parent, code, opts, shadowedBindings);
|
|
339
451
|
}
|
|
340
452
|
if (isIdentifierName(node)) {
|
|
341
453
|
identifier({
|
|
@@ -343,11 +455,17 @@ void import.meta.filename;
|
|
|
343
455
|
ancestors,
|
|
344
456
|
code,
|
|
345
457
|
opts,
|
|
346
|
-
meta: exportsMeta
|
|
458
|
+
meta: exportsMeta,
|
|
459
|
+
shadowed: shadowedBindings
|
|
347
460
|
});
|
|
348
461
|
}
|
|
349
462
|
}
|
|
350
463
|
});
|
|
464
|
+
if (pendingRequireTransforms.length) {
|
|
465
|
+
for (const t of pendingRequireTransforms) {
|
|
466
|
+
code.overwrite(t.start, t.end, t.code);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
351
469
|
if (shouldLowerCjs) {
|
|
352
470
|
const {
|
|
353
471
|
importTransforms,
|
|
@@ -396,6 +514,20 @@ void import.meta.filename;
|
|
|
396
514
|
code.append(`\n${lines.join('\n')}\n`);
|
|
397
515
|
}
|
|
398
516
|
}
|
|
517
|
+
if (shouldRaiseEsm && opts.transformSyntax) {
|
|
518
|
+
const importPrelude = [];
|
|
519
|
+
if (needsCreateRequire) {
|
|
520
|
+
importPrelude.push('import { createRequire } from "node:module";\n');
|
|
521
|
+
}
|
|
522
|
+
if (hoistedImports.length) {
|
|
523
|
+
importPrelude.push(...hoistedImports);
|
|
524
|
+
}
|
|
525
|
+
const requireInit = needsCreateRequire ? 'const require = createRequire(import.meta.url);\n' : '';
|
|
526
|
+
const prelude = `${importPrelude.join('')}${importPrelude.length ? '\n' : ''}${requireInit}let ${exportsRename} = {};
|
|
527
|
+
void import.meta.filename;
|
|
528
|
+
`;
|
|
529
|
+
code.prepend(prelude);
|
|
530
|
+
}
|
|
399
531
|
if (opts.target === 'commonjs' && opts.transformSyntax && containsTopLevelAwait) {
|
|
400
532
|
const body = code.toString();
|
|
401
533
|
if (opts.topLevelAwait === 'wrap') {
|
|
@@ -5,7 +5,8 @@ export const identifier = ({
|
|
|
5
5
|
ancestors,
|
|
6
6
|
code,
|
|
7
7
|
opts,
|
|
8
|
-
meta
|
|
8
|
+
meta,
|
|
9
|
+
shadowed
|
|
9
10
|
}) => {
|
|
10
11
|
if (opts.target === 'module') {
|
|
11
12
|
const {
|
|
@@ -13,6 +14,9 @@ export const identifier = ({
|
|
|
13
14
|
end,
|
|
14
15
|
name
|
|
15
16
|
} = node;
|
|
17
|
+
if (shadowed?.has(name)) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
16
20
|
switch (name) {
|
|
17
21
|
case '__filename':
|
|
18
22
|
code.update(start, end, 'import.meta.url');
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { exportsRename } from '#utils/exports.js';
|
|
2
|
-
export const memberExpression = (node, parent, src, options) => {
|
|
2
|
+
export const memberExpression = (node, parent, src, options, shadowed) => {
|
|
3
3
|
if (options.target === 'module') {
|
|
4
|
+
if (node.object.type === 'Identifier' && shadowed?.has(node.object.name) || node.property.type === 'Identifier' && shadowed?.has(node.property.name)) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
4
7
|
if (node.object.type === 'Identifier' && node.property.type === 'Identifier' && node.object.name === 'module' && node.property.name === 'exports') {
|
|
5
8
|
src.update(node.start, node.end, exportsRename);
|
|
6
9
|
return;
|
|
@@ -17,18 +20,10 @@ export const memberExpression = (node, parent, src, options) => {
|
|
|
17
20
|
// CommonJS properties of `require`
|
|
18
21
|
switch (name) {
|
|
19
22
|
case 'main':
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
* @see https://github.com/nodejs/node/pull/32223
|
|
23
|
-
*/
|
|
24
|
-
if (parent?.type === 'ExpressionStatement') {
|
|
25
|
-
// This is a standalone expression so remove it to not cause run-time errors.
|
|
26
|
-
src.remove(start, end);
|
|
23
|
+
if (parent?.type === 'BinaryExpression') {
|
|
24
|
+
return;
|
|
27
25
|
}
|
|
28
|
-
|
|
29
|
-
* Transform require.main === module.
|
|
30
|
-
*/
|
|
31
|
-
if (parent?.type === 'BinaryExpression') {}
|
|
26
|
+
src.update(start, end, 'import.meta.main');
|
|
32
27
|
break;
|
|
33
28
|
case 'resolve':
|
|
34
29
|
src.update(start, end, 'import.meta.resolve');
|
|
@@ -42,5 +37,10 @@ export const memberExpression = (node, parent, src, options) => {
|
|
|
42
37
|
break;
|
|
43
38
|
}
|
|
44
39
|
}
|
|
40
|
+
if (node.object.type === 'Identifier' && node.property.type === 'Identifier' && node.object.name === 'module' && node.property.name === 'require') {
|
|
41
|
+
if (!shadowed?.has('module')) {
|
|
42
|
+
src.update(node.start, node.end, 'require');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
45
|
}
|
|
46
46
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import MagicString from 'magic-string';
|
|
2
2
|
import type { MemberExpression, Node } from 'oxc-parser';
|
|
3
3
|
import type { FormatterOptions } from '../types.js';
|
|
4
|
-
export declare const memberExpression: (node: MemberExpression, parent: Node | null, src: MagicString, options: FormatterOptions) => void;
|
|
4
|
+
export declare const memberExpression: (node: MemberExpression, parent: Node | null, src: MagicString, options: FormatterOptions, shadowed?: Set<string>) => void;
|
|
@@ -7,6 +7,7 @@ type IdentifierArg = {
|
|
|
7
7
|
code: MagicString;
|
|
8
8
|
opts: FormatterOptions;
|
|
9
9
|
meta: ExportsMeta;
|
|
10
|
+
shadowed?: Set<string>;
|
|
10
11
|
};
|
|
11
|
-
export declare const identifier: ({ node, ancestors, code, opts, meta }: IdentifierArg) => void;
|
|
12
|
+
export declare const identifier: ({ node, ancestors, code, opts, meta, shadowed, }: IdentifierArg) => void;
|
|
12
13
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import MagicString from 'magic-string';
|
|
2
2
|
import type { MemberExpression, Node } from 'oxc-parser';
|
|
3
3
|
import type { FormatterOptions } from '../types.js';
|
|
4
|
-
export declare const memberExpression: (node: MemberExpression, parent: Node | null, src: MagicString, options: FormatterOptions) => void;
|
|
4
|
+
export declare const memberExpression: (node: MemberExpression, parent: Node | null, src: MagicString, options: FormatterOptions, shadowed?: Set<string>) => void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ancestorWalk } from '#walk';
|
|
2
|
-
import { scopes as scopeNodes } from '#helpers/scope.js';
|
|
3
2
|
import { identifier } from '#helpers/identifier.js';
|
|
3
|
+
import { scopeNodes } from './scopeNodes.js';
|
|
4
4
|
const collectScopeIdentifiers = (node, scopes) => {
|
|
5
5
|
const {
|
|
6
6
|
type
|
package/dist/utils.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { extname } from 'node:path';
|
|
2
2
|
import { ancestorWalk } from './walk.js';
|
|
3
|
-
import { scopes as scopeNodes } from './helpers/scope.js';
|
|
4
3
|
import { identifier } from './helpers/identifier.js';
|
|
4
|
+
import { scopeNodes } from './utils/scopeNodes.js';
|
|
5
5
|
const getLangFromExt = filename => {
|
|
6
6
|
const ext = extname(filename);
|
|
7
7
|
if (ext.endsWith('.js')) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knighted/module",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.4",
|
|
4
4
|
"description": "Transforms differences between ES modules and CommonJS.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/module.js",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"#formatters/*.js": "./src/formatters/*.js"
|
|
28
28
|
},
|
|
29
29
|
"engines": {
|
|
30
|
-
"node": ">=
|
|
30
|
+
"node": ">=22.21.1"
|
|
31
31
|
},
|
|
32
32
|
"engineStrict": true,
|
|
33
33
|
"scripts": {
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.scopes = exports.scope = void 0;
|
|
7
|
-
const scopes = exports.scopes = ['BlockStatement', 'FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression', 'ClassDeclaration', 'ClassExpression', 'ClassBody', 'StaticBlock'];
|
|
8
|
-
const scope = exports.scope = {
|
|
9
|
-
isScope(node) {
|
|
10
|
-
return scopes.includes(node.type);
|
|
11
|
-
}
|
|
12
|
-
};
|
package/dist/cjs/scope.d.cts
DELETED
package/dist/helpers/scope.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
const scopes = ['BlockStatement', 'FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression', 'ClassDeclaration', 'ClassExpression', 'ClassBody', 'StaticBlock'];
|
|
2
|
-
const scope = {
|
|
3
|
-
isScope(node) {
|
|
4
|
-
return scopes.includes(node.type);
|
|
5
|
-
}
|
|
6
|
-
};
|
|
7
|
-
export { scopes, scope };
|
package/dist/scope.d.ts
DELETED