@knighted/module 1.0.0-rc.1 → 1.0.0-rc.3
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 +39 -1
- package/dist/cjs/format.cjs +113 -14
- package/dist/cjs/formatters/identifier.cjs +1 -1
- package/dist/cjs/formatters/memberExpression.cjs +3 -2
- package/dist/cjs/helpers/identifier.cjs +48 -7
- package/dist/cjs/memberExpression.d.cts +6 -1
- package/dist/cjs/module.cjs +152 -15
- package/dist/cjs/specifier.cjs +1 -1
- package/dist/cjs/types.d.cts +24 -0
- package/dist/cjs/utils/exports.cjs +41 -18
- package/dist/cjs/utils/identifiers.cjs +34 -16
- package/dist/format.js +113 -14
- package/dist/formatters/identifier.js +1 -1
- package/dist/formatters/memberExpression.js +3 -2
- package/dist/helpers/identifier.js +48 -7
- package/dist/memberExpression.d.cts +6 -1
- package/dist/memberExpression.d.ts +6 -1
- package/dist/module.js +155 -15
- package/dist/specifier.js +1 -1
- package/dist/src/formatters/memberExpression.d.ts +6 -1
- package/dist/src/types.d.ts +24 -0
- package/dist/types.d.cts +24 -0
- package/dist/types.d.ts +24 -0
- package/dist/utils/exports.js +41 -18
- package/dist/utils/identifiers.js +34 -16
- package/package.json +10 -9
package/dist/cjs/types.d.cts
CHANGED
|
@@ -1,18 +1,42 @@
|
|
|
1
1
|
import type { Node, Span, IdentifierName, IdentifierReference, BindingIdentifier, LabelIdentifier, TSIndexSignatureName } from 'oxc-parser';
|
|
2
2
|
export type RewriteSpecifier = '.js' | '.mjs' | '.cjs' | '.ts' | '.mts' | '.cts' | ((value: string) => string | null | undefined);
|
|
3
|
+
/** Options that control how modules are parsed, transformed, and emitted. */
|
|
3
4
|
export type ModuleOptions = {
|
|
5
|
+
/** Output format to emit. */
|
|
4
6
|
target: 'module' | 'commonjs';
|
|
7
|
+
/** Explicit source type; auto infers from file extension. */
|
|
5
8
|
sourceType?: 'auto' | 'module' | 'commonjs';
|
|
9
|
+
/** Enable syntax transforms beyond parsing. */
|
|
6
10
|
transformSyntax?: boolean;
|
|
11
|
+
/** How to emit live bindings for ESM exports. */
|
|
7
12
|
liveBindings?: 'strict' | 'loose' | 'off';
|
|
13
|
+
/** Rewrite import specifiers (e.g. add extensions). */
|
|
8
14
|
rewriteSpecifier?: RewriteSpecifier;
|
|
15
|
+
/** Whether to append .js to relative imports. */
|
|
16
|
+
appendJsExtension?: 'off' | 'relative-only' | 'all';
|
|
17
|
+
/** Add directory index (e.g. /index.js) or disable. */
|
|
18
|
+
appendDirectoryIndex?: string | false;
|
|
19
|
+
/** Control __dirname and __filename handling. */
|
|
9
20
|
dirFilename?: 'inject' | 'preserve' | 'error';
|
|
21
|
+
/** How to treat import.meta. */
|
|
10
22
|
importMeta?: 'preserve' | 'shim' | 'error';
|
|
23
|
+
/** Strategy for import.meta.main emulation. */
|
|
11
24
|
importMetaMain?: 'shim' | 'warn' | 'error';
|
|
25
|
+
/** Resolution strategy for detecting the main module. */
|
|
26
|
+
requireMainStrategy?: 'import-meta-main' | 'realpath';
|
|
27
|
+
/** Detect circular require usage level. */
|
|
28
|
+
detectCircularRequires?: 'off' | 'warn' | 'error';
|
|
29
|
+
/** Source used to provide require in ESM output. */
|
|
12
30
|
requireSource?: 'builtin' | 'create-require';
|
|
31
|
+
/** How to rewrite nested or non-hoistable require calls. */
|
|
32
|
+
nestedRequireStrategy?: 'create-require' | 'dynamic-import';
|
|
33
|
+
/** Default interop style for CommonJS default imports. */
|
|
13
34
|
cjsDefault?: 'module-exports' | 'auto' | 'none';
|
|
35
|
+
/** Handling for top-level await constructs. */
|
|
14
36
|
topLevelAwait?: 'error' | 'wrap' | 'preserve';
|
|
37
|
+
/** Output directory or file path when writing. */
|
|
15
38
|
out?: string;
|
|
39
|
+
/** Overwrite input files instead of writing to out. */
|
|
16
40
|
inPlace?: boolean;
|
|
17
41
|
};
|
|
18
42
|
export type SpannedNode = Node & Span;
|
|
@@ -17,7 +17,9 @@ const literalPropName = (prop, literals) => {
|
|
|
17
17
|
}
|
|
18
18
|
return null;
|
|
19
19
|
};
|
|
20
|
-
const
|
|
20
|
+
const bindsThis = ancestor => ancestor.type === 'FunctionDeclaration' || ancestor.type === 'FunctionExpression' || ancestor.type === 'ClassDeclaration' || ancestor.type === 'ClassExpression';
|
|
21
|
+
const isTopLevelThis = ancestors => !ancestors?.some(node => bindsThis(node));
|
|
22
|
+
const resolveExportTarget = (node, aliases, literals, ancestors) => {
|
|
21
23
|
if (node.type === 'Identifier' && node.name === 'exports') {
|
|
22
24
|
return {
|
|
23
25
|
key: 'default',
|
|
@@ -35,7 +37,7 @@ const resolveExportTarget = (node, aliases, literals) => {
|
|
|
35
37
|
const prop = node.property;
|
|
36
38
|
const key = literalPropName(prop, literals);
|
|
37
39
|
if (!key) return null;
|
|
38
|
-
const baseVia = resolveBase(base, aliases);
|
|
40
|
+
const baseVia = resolveBase(base, aliases, ancestors);
|
|
39
41
|
if (!baseVia) return null;
|
|
40
42
|
if (baseVia === 'module.exports' && key === 'exports') {
|
|
41
43
|
return {
|
|
@@ -48,12 +50,15 @@ const resolveExportTarget = (node, aliases, literals) => {
|
|
|
48
50
|
via: baseVia
|
|
49
51
|
};
|
|
50
52
|
};
|
|
51
|
-
const resolveBase = (node, aliases) => {
|
|
53
|
+
const resolveBase = (node, aliases, ancestors) => {
|
|
52
54
|
if (node.type === 'Identifier') {
|
|
53
55
|
if (node.name === 'exports') return 'exports';
|
|
54
56
|
const alias = aliases.get(node.name);
|
|
55
57
|
if (alias) return alias;
|
|
56
58
|
}
|
|
59
|
+
if (node.type === 'ThisExpression' && isTopLevelThis(ancestors)) {
|
|
60
|
+
return 'exports';
|
|
61
|
+
}
|
|
57
62
|
if (node.type === 'MemberExpression' && node.object.type === 'Identifier' && node.object.name === 'module' && node.property.type === 'Identifier' && node.property.name === 'exports') {
|
|
58
63
|
return 'module.exports';
|
|
59
64
|
}
|
|
@@ -85,22 +90,24 @@ const collectCjsExports = async ast => {
|
|
|
85
90
|
exportsMap.set(ref.key, entry);
|
|
86
91
|
};
|
|
87
92
|
await (0, _walk.ancestorWalk)(ast, {
|
|
88
|
-
enter(node) {
|
|
93
|
+
enter(node, ancestors) {
|
|
89
94
|
if (node.type === 'VariableDeclarator' && node.id.type === 'Identifier' && node.init) {
|
|
90
|
-
const via = resolveBase(node.init, aliases);
|
|
95
|
+
const via = resolveBase(node.init, aliases, ancestors);
|
|
91
96
|
if (via) {
|
|
92
97
|
aliases.set(node.id.name, via);
|
|
93
98
|
}
|
|
99
|
+
const parentDecl = ancestors?.[ancestors.length - 2];
|
|
100
|
+
const isConstDecl = parentDecl?.type === 'VariableDeclaration' && parentDecl.kind === 'const';
|
|
94
101
|
if (node.init.type === 'Literal' && (typeof node.init.value === 'string' || typeof node.init.value === 'number')) {
|
|
95
|
-
literals.set(node.id.name, node.init.value);
|
|
102
|
+
if (isConstDecl) literals.set(node.id.name, node.init.value);
|
|
96
103
|
}
|
|
97
104
|
if (node.init.type === 'TemplateLiteral' && node.init.expressions.length === 0 && node.init.quasis.length === 1) {
|
|
98
105
|
const cooked = node.init.quasis[0].value.cooked ?? node.init.quasis[0].value.raw;
|
|
99
|
-
literals.set(node.id.name, cooked);
|
|
106
|
+
if (isConstDecl) literals.set(node.id.name, cooked);
|
|
100
107
|
}
|
|
101
108
|
}
|
|
102
109
|
if (node.type === 'AssignmentExpression') {
|
|
103
|
-
const target = resolveExportTarget(node.left, aliases, literals);
|
|
110
|
+
const target = resolveExportTarget(node.left, aliases, literals, ancestors);
|
|
104
111
|
if (target) {
|
|
105
112
|
const rhsIdent = node.right.type === 'Identifier' ? node.right : undefined;
|
|
106
113
|
addExport(target, node, rhsIdent);
|
|
@@ -118,15 +125,31 @@ const collectCjsExports = async ast => {
|
|
|
118
125
|
});
|
|
119
126
|
}
|
|
120
127
|
}
|
|
121
|
-
if (node.left.type === 'ObjectPattern') {
|
|
122
|
-
|
|
123
|
-
if (
|
|
124
|
-
const ref = resolveExportTarget(
|
|
125
|
-
if (ref)
|
|
126
|
-
|
|
128
|
+
if (node.left.type === 'ObjectPattern' || node.left.type === 'ArrayPattern') {
|
|
129
|
+
const findExportRefs = pattern => {
|
|
130
|
+
if (pattern.type === 'MemberExpression') {
|
|
131
|
+
const ref = resolveExportTarget(pattern, aliases, literals, ancestors);
|
|
132
|
+
if (ref) addExport(ref, node);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (pattern.type === 'ObjectPattern') {
|
|
136
|
+
for (const prop of pattern.properties) {
|
|
137
|
+
const target = prop.type === 'Property' ? prop.value : prop.argument;
|
|
138
|
+
if (target) findExportRefs(target);
|
|
127
139
|
}
|
|
140
|
+
return;
|
|
128
141
|
}
|
|
129
|
-
|
|
142
|
+
if (pattern.type === 'ArrayPattern') {
|
|
143
|
+
for (const el of pattern.elements) {
|
|
144
|
+
if (el) findExportRefs(el);
|
|
145
|
+
}
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (pattern.type === 'RestElement') {
|
|
149
|
+
findExportRefs(pattern.argument);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
findExportRefs(node.left);
|
|
130
153
|
}
|
|
131
154
|
}
|
|
132
155
|
if (node.type === 'CallExpression') {
|
|
@@ -135,7 +158,7 @@ const collectCjsExports = async ast => {
|
|
|
135
158
|
// Object.assign(exports, { foo: bar })
|
|
136
159
|
if (callee.type === 'MemberExpression' && callee.object.type === 'Identifier' && callee.object.name === 'Object' && callee.property.type === 'Identifier' && callee.property.name === 'assign' && node.arguments.length >= 2) {
|
|
137
160
|
const targetArg = node.arguments[0];
|
|
138
|
-
const ref = resolveBase(targetArg, aliases);
|
|
161
|
+
const ref = resolveBase(targetArg, aliases, ancestors);
|
|
139
162
|
if (!ref) return;
|
|
140
163
|
for (let i = 1; i < node.arguments.length; i++) {
|
|
141
164
|
const arg = node.arguments[i];
|
|
@@ -159,7 +182,7 @@ const collectCjsExports = async ast => {
|
|
|
159
182
|
|
|
160
183
|
// Object.defineProperty(exports, 'foo', { value, get, set })
|
|
161
184
|
if (callee.type === 'MemberExpression' && callee.object.type === 'Identifier' && callee.object.name === 'Object' && callee.property.type === 'Identifier' && callee.property.name === 'defineProperty' && node.arguments.length >= 3) {
|
|
162
|
-
const target = resolveBase(node.arguments[0], aliases);
|
|
185
|
+
const target = resolveBase(node.arguments[0], aliases, ancestors);
|
|
163
186
|
if (!target) return;
|
|
164
187
|
const keyName = literalPropName(node.arguments[1], literals);
|
|
165
188
|
if (!keyName) return;
|
|
@@ -190,7 +213,7 @@ const collectCjsExports = async ast => {
|
|
|
190
213
|
|
|
191
214
|
// Object.defineProperties(exports, { foo: { value: ... }, bar: { get: ... } })
|
|
192
215
|
if (callee.type === 'MemberExpression' && callee.object.type === 'Identifier' && callee.object.name === 'Object' && callee.property.type === 'Identifier' && callee.property.name === 'defineProperties' && node.arguments.length >= 2) {
|
|
193
|
-
const target = resolveBase(node.arguments[0], aliases);
|
|
216
|
+
const target = resolveBase(node.arguments[0], aliases, ancestors);
|
|
194
217
|
if (!target) return;
|
|
195
218
|
const descMap = node.arguments[1];
|
|
196
219
|
if (descMap.type !== 'ObjectExpression') return;
|
|
@@ -7,6 +7,34 @@ exports.collectScopeIdentifiers = exports.collectModuleIdentifiers = void 0;
|
|
|
7
7
|
var _walk = require("#walk");
|
|
8
8
|
var _identifier = require("#helpers/identifier.js");
|
|
9
9
|
var _scopeNodes = require("./scopeNodes.cjs");
|
|
10
|
+
const addBindingNames = (pattern, into) => {
|
|
11
|
+
if (!pattern) return;
|
|
12
|
+
switch (pattern.type) {
|
|
13
|
+
case 'Identifier':
|
|
14
|
+
into.add(pattern.name);
|
|
15
|
+
return;
|
|
16
|
+
case 'AssignmentPattern':
|
|
17
|
+
addBindingNames(pattern.left, into);
|
|
18
|
+
return;
|
|
19
|
+
case 'RestElement':
|
|
20
|
+
addBindingNames(pattern.argument, into);
|
|
21
|
+
return;
|
|
22
|
+
case 'ObjectPattern':
|
|
23
|
+
for (const prop of pattern.properties ?? []) {
|
|
24
|
+
if (prop.type === 'Property') {
|
|
25
|
+
addBindingNames(prop.value, into);
|
|
26
|
+
} else if (prop.type === 'RestElement') {
|
|
27
|
+
addBindingNames(prop.argument, into);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return;
|
|
31
|
+
case 'ArrayPattern':
|
|
32
|
+
for (const elem of pattern.elements ?? []) {
|
|
33
|
+
if (elem) addBindingNames(elem, into);
|
|
34
|
+
}
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
10
38
|
const collectScopeIdentifiers = (node, scopes) => {
|
|
11
39
|
const {
|
|
12
40
|
type
|
|
@@ -32,20 +60,10 @@ const collectScopeIdentifiers = (node, scopes) => {
|
|
|
32
60
|
type: 'Function',
|
|
33
61
|
idents: new Set()
|
|
34
62
|
};
|
|
35
|
-
node.params
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (param.type === 'RestElement') {
|
|
40
|
-
return param.argument;
|
|
41
|
-
}
|
|
42
|
-
if (param.type === 'AssignmentPattern') {
|
|
43
|
-
return param.left;
|
|
44
|
-
}
|
|
45
|
-
return param;
|
|
46
|
-
}).filter(_identifier.identifier.isNamed).forEach(param => {
|
|
47
|
-
scope.idents.add(param.name);
|
|
48
|
-
});
|
|
63
|
+
for (const param of node.params) {
|
|
64
|
+
const normalized = param.type === 'TSParameterProperty' ? param.parameter : param;
|
|
65
|
+
addBindingNames(normalized, scope.idents);
|
|
66
|
+
}
|
|
49
67
|
|
|
50
68
|
/**
|
|
51
69
|
* If a FunctionExpression has an id, it is a named function expression.
|
|
@@ -94,8 +112,8 @@ const collectScopeIdentifiers = (node, scopes) => {
|
|
|
94
112
|
if (scopes.length > 0) {
|
|
95
113
|
const scope = scopes[scopes.length - 1];
|
|
96
114
|
node.declarations.forEach(decl => {
|
|
97
|
-
if (decl.type === 'VariableDeclarator'
|
|
98
|
-
|
|
115
|
+
if (decl.type === 'VariableDeclarator') {
|
|
116
|
+
addBindingNames(decl.id, scope.idents);
|
|
99
117
|
}
|
|
100
118
|
});
|
|
101
119
|
}
|
package/dist/format.js
CHANGED
|
@@ -19,6 +19,8 @@ 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 requireInteropName = '__requireDefault';
|
|
23
|
+
const requireInteropHelper = `const ${requireInteropName} = mod => (mod && typeof mod === 'object' && 'default' in mod ? mod.default : mod);\n`;
|
|
22
24
|
const isRequireCallee = (callee, shadowed) => {
|
|
23
25
|
if (callee.type === 'Identifier' && callee.name === 'require' && !shadowed.has('require')) {
|
|
24
26
|
return true;
|
|
@@ -33,8 +35,14 @@ const isRequireCall = (node, shadowed) => node.type === 'CallExpression' && isRe
|
|
|
33
35
|
const lowerCjsRequireToImports = (program, code, shadowed) => {
|
|
34
36
|
const transforms = [];
|
|
35
37
|
const imports = [];
|
|
38
|
+
const hoisted = [];
|
|
36
39
|
let nsIndex = 0;
|
|
37
40
|
let needsCreateRequire = false;
|
|
41
|
+
let needsInteropHelper = false;
|
|
42
|
+
const isJsonSpecifier = value => {
|
|
43
|
+
const base = value.split(/[?#]/)[0] ?? value;
|
|
44
|
+
return base.endsWith('.json');
|
|
45
|
+
};
|
|
38
46
|
for (const stmt of program.body) {
|
|
39
47
|
if (stmt.type === 'VariableDeclaration') {
|
|
40
48
|
const decls = stmt.declarations;
|
|
@@ -42,14 +50,21 @@ const lowerCjsRequireToImports = (program, code, shadowed) => {
|
|
|
42
50
|
if (allStatic) {
|
|
43
51
|
for (const decl of decls) {
|
|
44
52
|
const init = decl.init;
|
|
45
|
-
const
|
|
53
|
+
const arg = init.arguments[0];
|
|
54
|
+
const source = code.slice(arg.start, arg.end);
|
|
55
|
+
const value = arg.value;
|
|
56
|
+
const isJson = typeof value === 'string' && isJsonSpecifier(value);
|
|
57
|
+
const ns = `__cjsImport${nsIndex++}`;
|
|
58
|
+
const jsonImport = isJson ? `${source} with { type: "json" }` : source;
|
|
46
59
|
if (decl.id.type === 'Identifier') {
|
|
47
|
-
imports.push(`import * as ${
|
|
48
|
-
|
|
49
|
-
|
|
60
|
+
imports.push(isJson ? `import ${ns} from ${jsonImport};\n` : `import * as ${ns} from ${jsonImport};\n`);
|
|
61
|
+
hoisted.push(isJson ? `const ${decl.id.name} = ${ns};\n` : `const ${decl.id.name} = ${requireInteropName}(${ns});\n`);
|
|
62
|
+
needsInteropHelper ||= !isJson;
|
|
63
|
+
} else if (decl.id.type === 'ObjectPattern' || decl.id.type === 'ArrayPattern') {
|
|
50
64
|
const pattern = code.slice(decl.id.start, decl.id.end);
|
|
51
|
-
imports.push(`import * as ${ns} from ${
|
|
52
|
-
|
|
65
|
+
imports.push(isJson ? `import ${ns} from ${jsonImport};\n` : `import * as ${ns} from ${jsonImport};\n`);
|
|
66
|
+
hoisted.push(isJson ? `const ${pattern} = ${ns};\n` : `const ${pattern} = ${requireInteropName}(${ns});\n`);
|
|
67
|
+
needsInteropHelper ||= !isJson;
|
|
53
68
|
} else {
|
|
54
69
|
needsCreateRequire = true;
|
|
55
70
|
}
|
|
@@ -71,8 +86,12 @@ const lowerCjsRequireToImports = (program, code, shadowed) => {
|
|
|
71
86
|
if (stmt.type === 'ExpressionStatement') {
|
|
72
87
|
const expr = stmt.expression;
|
|
73
88
|
if (expr && isStaticRequire(expr, shadowed)) {
|
|
74
|
-
const
|
|
75
|
-
|
|
89
|
+
const arg = expr.arguments[0];
|
|
90
|
+
const source = code.slice(arg.start, arg.end);
|
|
91
|
+
const value = arg.value;
|
|
92
|
+
const isJson = typeof value === 'string' && isJsonSpecifier(value);
|
|
93
|
+
const jsonImport = isJson ? `${source} with { type: "json" }` : source;
|
|
94
|
+
imports.push(`import ${jsonImport};\n`);
|
|
76
95
|
transforms.push({
|
|
77
96
|
start: stmt.start,
|
|
78
97
|
end: stmt.end,
|
|
@@ -88,7 +107,9 @@ const lowerCjsRequireToImports = (program, code, shadowed) => {
|
|
|
88
107
|
return {
|
|
89
108
|
transforms,
|
|
90
109
|
imports,
|
|
91
|
-
|
|
110
|
+
hoisted,
|
|
111
|
+
needsCreateRequire,
|
|
112
|
+
needsInteropHelper
|
|
92
113
|
};
|
|
93
114
|
};
|
|
94
115
|
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';
|
|
@@ -129,6 +150,20 @@ const hasTopLevelAwait = program => {
|
|
|
129
150
|
walkNode(program, false);
|
|
130
151
|
return found;
|
|
131
152
|
};
|
|
153
|
+
const isAsyncContext = ancestors => {
|
|
154
|
+
for (let i = ancestors.length - 1; i >= 0; i -= 1) {
|
|
155
|
+
const node = ancestors[i];
|
|
156
|
+
if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
|
|
157
|
+
return !!node.async;
|
|
158
|
+
}
|
|
159
|
+
if (node.type === 'ClassDeclaration' || node.type === 'ClassExpression') {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Program scope (top-level) supports await in ESM.
|
|
165
|
+
return true;
|
|
166
|
+
};
|
|
132
167
|
const lowerEsmToCjs = (program, code, opts, containsTopLevelAwait) => {
|
|
133
168
|
const live = opts.liveBindings ?? 'strict';
|
|
134
169
|
const importTransforms = [];
|
|
@@ -338,11 +373,17 @@ const format = async (src, ast, opts) => {
|
|
|
338
373
|
const exportTable = opts.target === 'module' ? await collectCjsExports(ast.program) : null;
|
|
339
374
|
const shouldCheckTopLevelAwait = opts.target === 'commonjs' && opts.transformSyntax;
|
|
340
375
|
const containsTopLevelAwait = shouldCheckTopLevelAwait ? hasTopLevelAwait(ast.program) : false;
|
|
376
|
+
const requireMainStrategy = opts.requireMainStrategy ?? 'import-meta-main';
|
|
377
|
+
let requireMainNeedsRealpath = false;
|
|
378
|
+
let needsRequireResolveHelper = false;
|
|
379
|
+
const nestedRequireStrategy = opts.nestedRequireStrategy ?? 'create-require';
|
|
341
380
|
const shouldLowerCjs = opts.target === 'commonjs' && opts.transformSyntax;
|
|
342
381
|
const shouldRaiseEsm = opts.target === 'module' && opts.transformSyntax;
|
|
343
382
|
let hoistedImports = [];
|
|
383
|
+
let hoistedStatements = [];
|
|
344
384
|
let pendingRequireTransforms = [];
|
|
345
385
|
let needsCreateRequire = false;
|
|
386
|
+
let needsImportInterop = false;
|
|
346
387
|
let pendingCjsTransforms = null;
|
|
347
388
|
if (shouldLowerCjs && opts.topLevelAwait === 'error' && containsTopLevelAwait) {
|
|
348
389
|
throw new Error('Top-level await is not supported when targeting CommonJS (set topLevelAwait to "wrap" or "preserve" to override).');
|
|
@@ -351,11 +392,15 @@ const format = async (src, ast, opts) => {
|
|
|
351
392
|
const {
|
|
352
393
|
transforms,
|
|
353
394
|
imports,
|
|
354
|
-
|
|
395
|
+
hoisted,
|
|
396
|
+
needsCreateRequire: reqCreate,
|
|
397
|
+
needsInteropHelper: reqInteropHelper
|
|
355
398
|
} = lowerCjsRequireToImports(ast.program, code, shadowedBindings);
|
|
356
399
|
pendingRequireTransforms = transforms;
|
|
357
400
|
hoistedImports = imports;
|
|
401
|
+
hoistedStatements = hoisted;
|
|
358
402
|
needsCreateRequire = reqCreate;
|
|
403
|
+
needsImportInterop = reqInteropHelper;
|
|
359
404
|
}
|
|
360
405
|
await ancestorWalk(ast.program, {
|
|
361
406
|
async enter(node, ancestors) {
|
|
@@ -370,7 +415,11 @@ const format = async (src, ast, opts) => {
|
|
|
370
415
|
const rightModule = node.right.type === 'Identifier' && node.right.name === 'module' && !shadowedBindings.has('module');
|
|
371
416
|
if (leftMain && rightModule || rightMain && leftModule) {
|
|
372
417
|
const negate = op === '!==' || op === '!=';
|
|
373
|
-
|
|
418
|
+
const mainExpr = requireMainStrategy === 'import-meta-main' ? 'import.meta.main' : 'import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href';
|
|
419
|
+
if (requireMainStrategy === 'realpath') {
|
|
420
|
+
requireMainNeedsRealpath = true;
|
|
421
|
+
}
|
|
422
|
+
code.update(node.start, node.end, negate ? `!(${mainExpr})` : mainExpr);
|
|
374
423
|
return;
|
|
375
424
|
}
|
|
376
425
|
}
|
|
@@ -392,6 +441,18 @@ const format = async (src, ast, opts) => {
|
|
|
392
441
|
const topLevelVarDecl = parent?.type === 'VariableDeclarator' && grandparent?.type === 'VariableDeclaration' && greatGrandparent?.type === 'Program';
|
|
393
442
|
const hoistableTopLevel = isStatic && (topLevelExprStmt || topLevelVarDecl);
|
|
394
443
|
if (!isStatic || !hoistableTopLevel) {
|
|
444
|
+
if (nestedRequireStrategy === 'dynamic-import') {
|
|
445
|
+
const asyncCapable = isAsyncContext(ancestors);
|
|
446
|
+
if (asyncCapable) {
|
|
447
|
+
const arg = node.arguments[0];
|
|
448
|
+
const argSrc = arg ? code.slice(arg.start, arg.end) : 'undefined';
|
|
449
|
+
const literalVal = arg?.value;
|
|
450
|
+
const isJson = arg?.type === 'Literal' && typeof literalVal === 'string' && (literalVal.split(/[?#]/)[0] ?? literalVal).endsWith('.json');
|
|
451
|
+
const importTarget = isJson ? `${argSrc} with { type: "json" }` : argSrc;
|
|
452
|
+
code.update(node.start, node.end, `(await import(${importTarget}))`);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
395
456
|
needsCreateRequire = true;
|
|
396
457
|
}
|
|
397
458
|
}
|
|
@@ -453,7 +514,23 @@ const format = async (src, ast, opts) => {
|
|
|
453
514
|
metaProperty(node, parent, code, opts);
|
|
454
515
|
}
|
|
455
516
|
if (node.type === 'MemberExpression') {
|
|
456
|
-
memberExpression(node, parent, code, opts, shadowedBindings
|
|
517
|
+
memberExpression(node, parent, code, opts, shadowedBindings, {
|
|
518
|
+
onRequireResolve: () => {
|
|
519
|
+
if (shouldRaiseEsm) needsRequireResolveHelper = true;
|
|
520
|
+
},
|
|
521
|
+
requireResolveName: '__requireResolve'
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
if (shouldRaiseEsm && node.type === 'ThisExpression') {
|
|
525
|
+
const bindsThis = ancestor => {
|
|
526
|
+
return ancestor.type === 'FunctionDeclaration' || ancestor.type === 'FunctionExpression' || ancestor.type === 'ClassDeclaration' || ancestor.type === 'ClassExpression';
|
|
527
|
+
};
|
|
528
|
+
const bindingAncestor = ancestors.find(ancestor => bindsThis(ancestor));
|
|
529
|
+
const isTopLevel = !bindingAncestor;
|
|
530
|
+
if (isTopLevel) {
|
|
531
|
+
code.update(node.start, node.end, exportsRename);
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
457
534
|
}
|
|
458
535
|
if (isIdentifierName(node)) {
|
|
459
536
|
identifier({
|
|
@@ -522,14 +599,36 @@ const format = async (src, ast, opts) => {
|
|
|
522
599
|
}
|
|
523
600
|
if (shouldRaiseEsm && opts.transformSyntax) {
|
|
524
601
|
const importPrelude = [];
|
|
525
|
-
if (needsCreateRequire) {
|
|
602
|
+
if (needsCreateRequire || needsRequireResolveHelper) {
|
|
526
603
|
importPrelude.push('import { createRequire } from "node:module";\n');
|
|
527
604
|
}
|
|
605
|
+
if (needsRequireResolveHelper) {
|
|
606
|
+
importPrelude.push('import { fileURLToPath } from "node:url";\n');
|
|
607
|
+
}
|
|
608
|
+
if (requireMainNeedsRealpath) {
|
|
609
|
+
importPrelude.push('import { realpathSync } from "node:fs";\n');
|
|
610
|
+
importPrelude.push('import { pathToFileURL } from "node:url";\n');
|
|
611
|
+
}
|
|
528
612
|
if (hoistedImports.length) {
|
|
529
613
|
importPrelude.push(...hoistedImports);
|
|
530
614
|
}
|
|
615
|
+
const setupPrelude = [];
|
|
616
|
+
if (needsImportInterop) {
|
|
617
|
+
setupPrelude.push(requireInteropHelper);
|
|
618
|
+
}
|
|
619
|
+
if (hoistedStatements.length) {
|
|
620
|
+
setupPrelude.push(...hoistedStatements);
|
|
621
|
+
}
|
|
531
622
|
const requireInit = needsCreateRequire ? 'const require = createRequire(import.meta.url);\n' : '';
|
|
532
|
-
const
|
|
623
|
+
const requireResolveInit = needsRequireResolveHelper ? needsCreateRequire ? `const __requireResolve = (id, parent) => {
|
|
624
|
+
const resolved = require.resolve(id, parent);
|
|
625
|
+
return resolved.startsWith("file://") ? fileURLToPath(resolved) : resolved;
|
|
626
|
+
};\n` : `const __requireResolve = (id, parent) => {
|
|
627
|
+
const req = createRequire(parent ?? import.meta.url);
|
|
628
|
+
const resolved = req.resolve(id, parent);
|
|
629
|
+
return resolved.startsWith("file://") ? fileURLToPath(resolved) : resolved;
|
|
630
|
+
};\n` : '';
|
|
631
|
+
const prelude = `${importPrelude.join('')}${importPrelude.length ? '\n' : ''}${setupPrelude.join('')}${setupPrelude.length ? '\n' : ''}${requireInit}${requireResolveInit}let ${exportsRename} = {};
|
|
533
632
|
void import.meta.filename;
|
|
534
633
|
`;
|
|
535
634
|
code.prepend(prelude);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { exportsRename } from '#utils/exports.js';
|
|
2
|
-
export const memberExpression = (node, parent, src, options, shadowed) => {
|
|
2
|
+
export const memberExpression = (node, parent, src, options, shadowed, extras) => {
|
|
3
3
|
if (options.target === 'module') {
|
|
4
4
|
if (node.object.type === 'Identifier' && shadowed?.has(node.object.name) || node.property.type === 'Identifier' && shadowed?.has(node.property.name)) {
|
|
5
5
|
return;
|
|
@@ -26,7 +26,8 @@ export const memberExpression = (node, parent, src, options, shadowed) => {
|
|
|
26
26
|
src.update(start, end, 'import.meta.main');
|
|
27
27
|
break;
|
|
28
28
|
case 'resolve':
|
|
29
|
-
|
|
29
|
+
extras?.onRequireResolve?.();
|
|
30
|
+
src.update(start, end, extras?.requireResolveName ?? 'import.meta.resolve');
|
|
30
31
|
break;
|
|
31
32
|
case 'cache':
|
|
32
33
|
/**
|
|
@@ -24,6 +24,31 @@ const getScopeContext = program => {
|
|
|
24
24
|
scopeCache.set(program, context);
|
|
25
25
|
return context;
|
|
26
26
|
};
|
|
27
|
+
const isInBindingPattern = (pattern, target) => {
|
|
28
|
+
if (pattern === target) return true;
|
|
29
|
+
switch (pattern.type) {
|
|
30
|
+
case 'Identifier':
|
|
31
|
+
return pattern === target;
|
|
32
|
+
case 'AssignmentPattern':
|
|
33
|
+
return isInBindingPattern(pattern.left, target);
|
|
34
|
+
case 'RestElement':
|
|
35
|
+
return isInBindingPattern(pattern.argument, target);
|
|
36
|
+
case 'ObjectPattern':
|
|
37
|
+
return (pattern.properties ?? []).some(prop => {
|
|
38
|
+
if (prop.type === 'Property') {
|
|
39
|
+
return isInBindingPattern(prop.value, target);
|
|
40
|
+
}
|
|
41
|
+
if (prop.type === 'RestElement') {
|
|
42
|
+
return isInBindingPattern(prop.argument, target);
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
});
|
|
46
|
+
case 'ArrayPattern':
|
|
47
|
+
return (pattern.elements ?? []).some(elem => elem && isInBindingPattern(elem, target));
|
|
48
|
+
default:
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
27
52
|
|
|
28
53
|
/**
|
|
29
54
|
* All methods receive the full set of ancestors, which
|
|
@@ -42,6 +67,7 @@ const identifier = {
|
|
|
42
67
|
isModuleScope(ancestors, includeImports = false) {
|
|
43
68
|
const node = ancestors[ancestors.length - 1];
|
|
44
69
|
const parent = ancestors[ancestors.length - 2];
|
|
70
|
+
const grandParent = ancestors[ancestors.length - 3];
|
|
45
71
|
const program = ancestors[0];
|
|
46
72
|
if (!identifier.isNamed(node) || identifier.isMetaProperty(ancestors) || parent.type === 'LabeledStatement' || parent.type === 'BreakStatement' || parent.type === 'ContinueStatement') {
|
|
47
73
|
return false;
|
|
@@ -50,7 +76,9 @@ const identifier = {
|
|
|
50
76
|
return includeImports && parent.local.name === node.name;
|
|
51
77
|
}
|
|
52
78
|
if (parent.type === 'Property' && parent.key === node && !parent.computed) {
|
|
53
|
-
|
|
79
|
+
if (grandParent?.type !== 'ObjectPattern') {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
54
82
|
}
|
|
55
83
|
if (parent.type === 'MemberExpression' && parent.property === node && !parent.computed) {
|
|
56
84
|
return false;
|
|
@@ -72,8 +100,17 @@ const identifier = {
|
|
|
72
100
|
},
|
|
73
101
|
isDeclaration(ancestors) {
|
|
74
102
|
const node = ancestors[ancestors.length - 1];
|
|
75
|
-
|
|
76
|
-
|
|
103
|
+
// Walk outwards to find a declarator that binds the node
|
|
104
|
+
for (let i = ancestors.length - 2; i >= 0; i--) {
|
|
105
|
+
const parent = ancestors[i];
|
|
106
|
+
if (parent.type === 'VariableDeclarator') {
|
|
107
|
+
return parent.id === node || isInBindingPattern(parent.id, node);
|
|
108
|
+
}
|
|
109
|
+
if (parent.type === 'FunctionDeclaration' || parent.type === 'ClassDeclaration') {
|
|
110
|
+
return parent.id === node;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
77
114
|
},
|
|
78
115
|
isClassOrFuncDeclarationId(ancestors) {
|
|
79
116
|
const node = ancestors[ancestors.length - 1];
|
|
@@ -82,12 +119,16 @@ const identifier = {
|
|
|
82
119
|
},
|
|
83
120
|
isVarDeclarationInGlobalScope(ancestors) {
|
|
84
121
|
const node = ancestors[ancestors.length - 1];
|
|
85
|
-
const parent = ancestors[ancestors.length - 2];
|
|
86
|
-
const grandParent = ancestors[ancestors.length - 3];
|
|
87
122
|
const varBoundScopes = ['ClassDeclaration', 'ClassExpression', 'FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression'];
|
|
88
|
-
|
|
89
|
-
return
|
|
123
|
+
const declaratorIndex = ancestors.findIndex(ancestor => {
|
|
124
|
+
return ancestor.type === 'VariableDeclarator' && (ancestor === node || isInBindingPattern(ancestor.id, node));
|
|
90
125
|
});
|
|
126
|
+
if (declaratorIndex === -1) return false;
|
|
127
|
+
const declarator = ancestors[declaratorIndex];
|
|
128
|
+
const declaration = ancestors[declaratorIndex - 1];
|
|
129
|
+
return declaration?.type === 'VariableDeclaration' && declaration.kind === 'var' && ancestors.every(ancestor => {
|
|
130
|
+
return !varBoundScopes.includes(ancestor.type);
|
|
131
|
+
}) && (declarator.id === node || isInBindingPattern(declarator.id, node));
|
|
91
132
|
},
|
|
92
133
|
isIife(ancestors) {
|
|
93
134
|
const parent = ancestors[ancestors.length - 2];
|
|
@@ -1,4 +1,9 @@
|
|
|
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
|
-
|
|
4
|
+
type MemberExpressionExtras = {
|
|
5
|
+
onRequireResolve?: () => void;
|
|
6
|
+
requireResolveName?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare const memberExpression: (node: MemberExpression, parent: Node | null, src: MagicString, options: FormatterOptions, shadowed?: Set<string>, extras?: MemberExpressionExtras) => void;
|
|
9
|
+
export {};
|
|
@@ -1,4 +1,9 @@
|
|
|
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
|
-
|
|
4
|
+
type MemberExpressionExtras = {
|
|
5
|
+
onRequireResolve?: () => void;
|
|
6
|
+
requireResolveName?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare const memberExpression: (node: MemberExpression, parent: Node | null, src: MagicString, options: FormatterOptions, shadowed?: Set<string>, extras?: MemberExpressionExtras) => void;
|
|
9
|
+
export {};
|