@knighted/module 1.3.0 → 1.3.1
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/dist/ast.d.ts +1 -0
- package/dist/async.d.ts +4 -0
- package/dist/buildEsmPrelude.d.ts +14 -0
- package/dist/cjs/ast.d.cts +1 -0
- package/dist/cjs/async.d.cts +4 -0
- package/dist/cjs/buildEsmPrelude.d.cts +14 -0
- package/dist/cjs/exportBagToEsm.d.cts +13 -0
- package/dist/cjs/format.cjs +78 -852
- package/dist/cjs/formatVisitor.d.cts +42 -0
- package/dist/cjs/helpers/async.cjs +57 -0
- package/dist/cjs/idiomaticPlan.d.cts +28 -0
- package/dist/cjs/interopHelpers.d.cts +5 -0
- package/dist/cjs/lowerCjsRequireToImports.d.cts +17 -0
- package/dist/cjs/lowerEsmToCjs.d.cts +22 -0
- package/dist/cjs/pipeline/buildEsmPrelude.cjs +65 -0
- package/dist/cjs/pipeline/exportBagToEsm.cjs +81 -0
- package/dist/cjs/pipeline/formatVisitor.cjs +171 -0
- package/dist/cjs/pipeline/idiomaticPlan.cjs +224 -0
- package/dist/cjs/pipeline/interopHelpers.cjs +10 -0
- package/dist/cjs/pipeline/lowerCjsRequireToImports.cjs +110 -0
- package/dist/cjs/pipeline/lowerEsmToCjs.cjs +204 -0
- package/dist/exportBagToEsm.d.ts +13 -0
- package/dist/format.js +74 -848
- package/dist/formatVisitor.d.ts +42 -0
- package/dist/helpers/ast.d.ts +1 -0
- package/dist/helpers/async.d.ts +4 -0
- package/dist/helpers/async.js +50 -0
- package/dist/idiomaticPlan.d.ts +28 -0
- package/dist/interopHelpers.d.ts +5 -0
- package/dist/lowerCjsRequireToImports.d.ts +17 -0
- package/dist/lowerEsmToCjs.d.ts +22 -0
- package/dist/pipeline/buildEsmPrelude.d.ts +14 -0
- package/dist/pipeline/buildEsmPrelude.js +59 -0
- package/dist/pipeline/exportBagToEsm.d.ts +13 -0
- package/dist/pipeline/exportBagToEsm.js +75 -0
- package/dist/pipeline/formatVisitor.d.ts +42 -0
- package/dist/pipeline/formatVisitor.js +166 -0
- package/dist/pipeline/idiomaticPlan.d.ts +28 -0
- package/dist/pipeline/idiomaticPlan.js +218 -0
- package/dist/pipeline/interopHelpers.d.ts +5 -0
- package/dist/pipeline/interopHelpers.js +5 -0
- package/dist/pipeline/lowerCjsRequireToImports.d.ts +17 -0
- package/dist/pipeline/lowerCjsRequireToImports.js +102 -0
- package/dist/pipeline/lowerEsmToCjs.d.ts +22 -0
- package/dist/pipeline/lowerEsmToCjs.js +197 -0
- package/package.json +1 -1
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { isAstNode, isCallExpressionNode, isIdentifierNode, isMemberExpressionNode } from '../helpers/ast.js';
|
|
2
|
+
const reservedExports = new Set(['await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do', 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'implements', 'import', 'in', 'instanceof', 'interface', 'let', 'new', 'null', 'package', 'private', 'protected', 'public', 'return', 'static', 'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'var', 'void', 'while', 'with', 'yield']);
|
|
3
|
+
const isValidExportName = name => /^[$A-Z_a-z][$\w]*$/.test(name) && !reservedExports.has(name);
|
|
4
|
+
const isAllowedRhs = node => node.type === 'Identifier' || node.type === 'Literal' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression' || node.type === 'ClassExpression';
|
|
5
|
+
const expressionHasRequireCall = (node, shadowed) => {
|
|
6
|
+
let found = false;
|
|
7
|
+
const walkNode = n => {
|
|
8
|
+
if (!isAstNode(n) || found) return;
|
|
9
|
+
if (isCallExpressionNode(n) && isIdentifierNode(n.callee) && n.callee.name === 'require' && !shadowed.has('require')) {
|
|
10
|
+
found = true;
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (isCallExpressionNode(n) && isMemberExpressionNode(n.callee) && isIdentifierNode(n.callee.object) && n.callee.object.name === 'require' && !shadowed.has('require')) {
|
|
14
|
+
found = true;
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const record = n;
|
|
18
|
+
const keys = Object.keys(record);
|
|
19
|
+
for (const key of keys) {
|
|
20
|
+
const value = record[key];
|
|
21
|
+
if (!value) continue;
|
|
22
|
+
if (Array.isArray(value)) {
|
|
23
|
+
for (const item of value) {
|
|
24
|
+
if (item && typeof item === 'object') walkNode(item);
|
|
25
|
+
if (found) return;
|
|
26
|
+
}
|
|
27
|
+
} else if (value && typeof value === 'object') {
|
|
28
|
+
walkNode(value);
|
|
29
|
+
if (found) return;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
walkNode(node);
|
|
34
|
+
return found;
|
|
35
|
+
};
|
|
36
|
+
const rhsSourceFor = (code, node) => code.slice(node.start, node.end).replace(/\b__dirname\b/g, 'import.meta.dirname').replace(/\b__filename\b/g, 'import.meta.filename');
|
|
37
|
+
const tryObjectLiteralExport = (rhs, code, requireShadowed, baseIsModuleExports, propName) => {
|
|
38
|
+
if (!baseIsModuleExports || propName !== 'exports') return null;
|
|
39
|
+
if (rhs.type !== 'ObjectExpression') return null;
|
|
40
|
+
const exportsOut = [];
|
|
41
|
+
const seenKeys = new Set();
|
|
42
|
+
for (const prop of rhs.properties) {
|
|
43
|
+
if (prop.type !== 'Property') return null;
|
|
44
|
+
if (prop.kind !== 'init') return null;
|
|
45
|
+
if (prop.computed || prop.method) return null;
|
|
46
|
+
if (prop.key.type !== 'Identifier') return null;
|
|
47
|
+
const key = prop.key.name;
|
|
48
|
+
if (key === '__proto__' || key === 'prototype') return null;
|
|
49
|
+
if (!isValidExportName(key)) return null;
|
|
50
|
+
if (seenKeys.has(key)) return null;
|
|
51
|
+
const value = prop.value.type === 'Identifier' && prop.shorthand ? prop.key : prop.value;
|
|
52
|
+
if (!isAllowedRhs(value)) return null;
|
|
53
|
+
if (expressionHasRequireCall(value, requireShadowed)) return null;
|
|
54
|
+
const rhsSrc = rhsSourceFor(code, value);
|
|
55
|
+
if (value.type === 'Identifier' && value.name === key) {
|
|
56
|
+
exportsOut.push(`export { ${key} };`);
|
|
57
|
+
} else if (value.type === 'Identifier') {
|
|
58
|
+
exportsOut.push(`export { ${rhsSrc} as ${key} };`);
|
|
59
|
+
} else {
|
|
60
|
+
exportsOut.push(`export const ${key} = ${rhsSrc};`);
|
|
61
|
+
}
|
|
62
|
+
seenKeys.add(key);
|
|
63
|
+
}
|
|
64
|
+
exportsOut.push(`export default ${rhsSourceFor(code, rhs)};`);
|
|
65
|
+
return {
|
|
66
|
+
exportsOut,
|
|
67
|
+
seenKeys
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
const buildIdiomaticPlan = ({
|
|
71
|
+
src,
|
|
72
|
+
code,
|
|
73
|
+
exportTable,
|
|
74
|
+
shadowedBindings,
|
|
75
|
+
idiomaticMode
|
|
76
|
+
}) => {
|
|
77
|
+
if (idiomaticMode === 'off') return {
|
|
78
|
+
ok: false,
|
|
79
|
+
reason: 'disabled'
|
|
80
|
+
};
|
|
81
|
+
const entries = [...exportTable.values()];
|
|
82
|
+
if (!entries.length) return {
|
|
83
|
+
ok: false,
|
|
84
|
+
reason: 'no-exports'
|
|
85
|
+
};
|
|
86
|
+
if (exportTable.hasUnsupportedExportWrite) {
|
|
87
|
+
return {
|
|
88
|
+
ok: false,
|
|
89
|
+
reason: 'unsupported-left'
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const viaSet = new Set();
|
|
93
|
+
for (const entry of entries) {
|
|
94
|
+
entry.via.forEach(v => viaSet.add(v));
|
|
95
|
+
if (entry.hasGetter) return {
|
|
96
|
+
ok: false,
|
|
97
|
+
reason: 'getter-present'
|
|
98
|
+
};
|
|
99
|
+
if (entry.reassignments.length) return {
|
|
100
|
+
ok: false,
|
|
101
|
+
reason: 'reassignment'
|
|
102
|
+
};
|
|
103
|
+
if (entry.hasNonTopLevelWrite) return {
|
|
104
|
+
ok: false,
|
|
105
|
+
reason: 'non-top-level'
|
|
106
|
+
};
|
|
107
|
+
if (entry.writes.length !== 1) return {
|
|
108
|
+
ok: false,
|
|
109
|
+
reason: 'multiple-writes'
|
|
110
|
+
};
|
|
111
|
+
if (entry.key !== 'default' && !isValidExportName(entry.key)) return {
|
|
112
|
+
ok: false,
|
|
113
|
+
reason: 'non-identifier-key'
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
if (viaSet.size > 1) return {
|
|
117
|
+
ok: false,
|
|
118
|
+
reason: 'mixed-exports'
|
|
119
|
+
};
|
|
120
|
+
const replacements = [];
|
|
121
|
+
const exportsOut = [];
|
|
122
|
+
const seen = new Set();
|
|
123
|
+
const requireShadowed = shadowedBindings;
|
|
124
|
+
for (const entry of entries) {
|
|
125
|
+
const write = entry.writes[0];
|
|
126
|
+
if (write.type !== 'AssignmentExpression') {
|
|
127
|
+
return {
|
|
128
|
+
ok: false,
|
|
129
|
+
reason: 'unsupported-write-kind'
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const left = write.left;
|
|
133
|
+
if (left.type !== 'MemberExpression' || left.computed || left.property.type !== 'Identifier') {
|
|
134
|
+
return {
|
|
135
|
+
ok: false,
|
|
136
|
+
reason: 'unsupported-left'
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const base = left.object;
|
|
140
|
+
const propName = left.property.name;
|
|
141
|
+
const baseIsExports = base.type === 'Identifier' && base.name === 'exports';
|
|
142
|
+
const baseIsModuleExports = base.type === 'Identifier' && base.name === 'module' && propName === 'exports' || base.type === 'MemberExpression' && base.object.type === 'Identifier' && base.object.name === 'module' && base.property.type === 'Identifier' && base.property.name === 'exports';
|
|
143
|
+
if (!baseIsExports && !baseIsModuleExports) {
|
|
144
|
+
return {
|
|
145
|
+
ok: false,
|
|
146
|
+
reason: 'unsupported-base'
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const rhs = write.right;
|
|
150
|
+
const objectLiteralPlan = tryObjectLiteralExport(rhs, code, requireShadowed, baseIsModuleExports, propName);
|
|
151
|
+
if (!objectLiteralPlan) {
|
|
152
|
+
if (!isAllowedRhs(rhs)) return {
|
|
153
|
+
ok: false,
|
|
154
|
+
reason: 'unsupported-rhs'
|
|
155
|
+
};
|
|
156
|
+
if (expressionHasRequireCall(rhs, requireShadowed)) {
|
|
157
|
+
return {
|
|
158
|
+
ok: false,
|
|
159
|
+
reason: 'rhs-require'
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const rhsSrc = rhsSourceFor(code, rhs);
|
|
164
|
+
if (propName === 'exports' && baseIsModuleExports) {
|
|
165
|
+
if (objectLiteralPlan) {
|
|
166
|
+
for (const line of objectLiteralPlan.exportsOut) {
|
|
167
|
+
exportsOut.push(line);
|
|
168
|
+
}
|
|
169
|
+
objectLiteralPlan.seenKeys.forEach(k => seen.add(k));
|
|
170
|
+
} else {
|
|
171
|
+
if (seen.has('default')) return {
|
|
172
|
+
ok: false,
|
|
173
|
+
reason: 'duplicate-default'
|
|
174
|
+
};
|
|
175
|
+
seen.add('default');
|
|
176
|
+
exportsOut.push(`export default ${rhsSrc};`);
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
if (seen.has(propName)) return {
|
|
180
|
+
ok: false,
|
|
181
|
+
reason: 'duplicate-key'
|
|
182
|
+
};
|
|
183
|
+
seen.add(propName);
|
|
184
|
+
if (rhs.type === 'Identifier') {
|
|
185
|
+
const rhsId = rhsSourceFor(code, rhs);
|
|
186
|
+
const rhsName = rhs.name;
|
|
187
|
+
if (rhsId === rhsName && rhsName === propName) {
|
|
188
|
+
exportsOut.push(`export { ${propName} };`);
|
|
189
|
+
} else if (rhsId === rhsName) {
|
|
190
|
+
exportsOut.push(`export { ${rhsId} as ${propName} };`);
|
|
191
|
+
} else {
|
|
192
|
+
exportsOut.push(`export const ${propName} = ${rhsId};`);
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
exportsOut.push(`export const ${propName} = ${rhsSrc};`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
let end = write.end;
|
|
199
|
+
while (end < src.length && (src[end] === ' ' || src[end] === '\t')) end++;
|
|
200
|
+
if (end < src.length && src[end] === ';') end++;
|
|
201
|
+
replacements.push({
|
|
202
|
+
start: write.start,
|
|
203
|
+
end
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
if (!seen.size) return {
|
|
207
|
+
ok: false,
|
|
208
|
+
reason: 'no-seen'
|
|
209
|
+
};
|
|
210
|
+
return {
|
|
211
|
+
ok: true,
|
|
212
|
+
plan: {
|
|
213
|
+
replacements,
|
|
214
|
+
exports: exportsOut
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
export { buildIdiomaticPlan };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
declare const defaultInteropName = "__interopDefault";
|
|
2
|
+
declare const interopHelper = "const __interopDefault = mod => (mod && mod.__esModule ? mod.default : mod);\n";
|
|
3
|
+
declare const requireInteropName = "__requireDefault";
|
|
4
|
+
declare const requireInteropHelper = "const __requireDefault = mod => (mod && typeof mod === 'object' && 'default' in mod ? mod.default : mod);\n";
|
|
5
|
+
export { defaultInteropName, interopHelper, requireInteropHelper, requireInteropName };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
const defaultInteropName = '__interopDefault';
|
|
2
|
+
const interopHelper = `const ${defaultInteropName} = mod => (mod && mod.__esModule ? mod.default : mod);\n`;
|
|
3
|
+
const requireInteropName = '__requireDefault';
|
|
4
|
+
const requireInteropHelper = `const ${requireInteropName} = mod => (mod && typeof mod === 'object' && 'default' in mod ? mod.default : mod);\n`;
|
|
5
|
+
export { defaultInteropName, interopHelper, requireInteropHelper, requireInteropName };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import MagicString from 'magic-string';
|
|
2
|
+
import { type CallExpressionNode, type ProgramNode, type Node } from '../helpers/ast.js';
|
|
3
|
+
type RequireTransform = {
|
|
4
|
+
start: number;
|
|
5
|
+
end: number;
|
|
6
|
+
code: string;
|
|
7
|
+
};
|
|
8
|
+
declare const isStaticRequire: (node: Node, shadowed: Set<string>) => node is CallExpressionNode;
|
|
9
|
+
declare const isRequireCall: (node: Node, shadowed: Set<string>) => node is CallExpressionNode;
|
|
10
|
+
declare const lowerCjsRequireToImports: (program: ProgramNode, code: MagicString, shadowed: Set<string>) => {
|
|
11
|
+
transforms: RequireTransform[];
|
|
12
|
+
imports: string[];
|
|
13
|
+
hoisted: string[];
|
|
14
|
+
needsCreateRequire: boolean;
|
|
15
|
+
needsInteropHelper: boolean;
|
|
16
|
+
};
|
|
17
|
+
export { isRequireCall, isStaticRequire, lowerCjsRequireToImports, type RequireTransform };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { isCallExpressionNode } from '../helpers/ast.js';
|
|
2
|
+
import { requireInteropName } from './interopHelpers.js';
|
|
3
|
+
const isRequireCallee = (callee, shadowed) => {
|
|
4
|
+
if (callee.type === 'Identifier' && callee.name === 'require' && !shadowed.has('require')) {
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
if (callee.type === 'MemberExpression' && callee.object.type === 'Identifier' && callee.object.name === 'module' && !shadowed.has('module') && callee.property.type === 'Identifier' && callee.property.name === 'require') {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
return false;
|
|
11
|
+
};
|
|
12
|
+
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';
|
|
13
|
+
const isRequireCall = (node, shadowed) => node.type === 'CallExpression' && isRequireCallee(node.callee, shadowed);
|
|
14
|
+
const lowerCjsRequireToImports = (program, code, shadowed) => {
|
|
15
|
+
const transforms = [];
|
|
16
|
+
const imports = [];
|
|
17
|
+
const hoisted = [];
|
|
18
|
+
let nsIndex = 0;
|
|
19
|
+
let needsCreateRequire = false;
|
|
20
|
+
let needsInteropHelper = false;
|
|
21
|
+
const isJsonSpecifier = value => {
|
|
22
|
+
const base = value.split(/[?#]/)[0] ?? value;
|
|
23
|
+
return base.endsWith('.json');
|
|
24
|
+
};
|
|
25
|
+
for (const stmt of program.body) {
|
|
26
|
+
if (stmt.type === 'VariableDeclaration') {
|
|
27
|
+
const decls = stmt.declarations;
|
|
28
|
+
const allStatic = decls.length > 0 && decls.every(decl => decl.init && isStaticRequire(decl.init, shadowed));
|
|
29
|
+
if (allStatic) {
|
|
30
|
+
for (const decl of decls) {
|
|
31
|
+
const init = decl.init;
|
|
32
|
+
if (!init || !isCallExpressionNode(init)) {
|
|
33
|
+
needsCreateRequire = true;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const arg = init.arguments[0];
|
|
37
|
+
const source = code.slice(arg.start, arg.end);
|
|
38
|
+
const value = arg.value;
|
|
39
|
+
const isJson = typeof value === 'string' && isJsonSpecifier(value);
|
|
40
|
+
const ns = `__cjsImport${nsIndex++}`;
|
|
41
|
+
const jsonImport = isJson ? `${source} with { type: "json" }` : source;
|
|
42
|
+
if (decl.id.type === 'Identifier') {
|
|
43
|
+
imports.push(isJson ? `import ${ns} from ${jsonImport};\n` : `import * as ${ns} from ${jsonImport};\n`);
|
|
44
|
+
hoisted.push(isJson ? `const ${decl.id.name} = ${ns};\n` : `const ${decl.id.name} = ${requireInteropName}(${ns});\n`);
|
|
45
|
+
needsInteropHelper ||= !isJson;
|
|
46
|
+
} else if (decl.id.type === 'ObjectPattern' || decl.id.type === 'ArrayPattern') {
|
|
47
|
+
const pattern = code.slice(decl.id.start, decl.id.end);
|
|
48
|
+
imports.push(isJson ? `import ${ns} from ${jsonImport};\n` : `import * as ${ns} from ${jsonImport};\n`);
|
|
49
|
+
hoisted.push(isJson ? `const ${pattern} = ${ns};\n` : `const ${pattern} = ${requireInteropName}(${ns});\n`);
|
|
50
|
+
needsInteropHelper ||= !isJson;
|
|
51
|
+
} else {
|
|
52
|
+
needsCreateRequire = true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
transforms.push({
|
|
56
|
+
start: stmt.start,
|
|
57
|
+
end: stmt.end,
|
|
58
|
+
code: ';\n'
|
|
59
|
+
});
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
for (const decl of decls) {
|
|
63
|
+
const init = decl.init;
|
|
64
|
+
if (init && isRequireCall(init, shadowed)) {
|
|
65
|
+
needsCreateRequire = true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (stmt.type === 'ExpressionStatement') {
|
|
70
|
+
const expr = stmt.expression;
|
|
71
|
+
if (expr && isStaticRequire(expr, shadowed)) {
|
|
72
|
+
if (!isCallExpressionNode(expr)) {
|
|
73
|
+
needsCreateRequire = true;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const arg = expr.arguments[0];
|
|
77
|
+
const source = code.slice(arg.start, arg.end);
|
|
78
|
+
const value = arg.value;
|
|
79
|
+
const isJson = typeof value === 'string' && isJsonSpecifier(value);
|
|
80
|
+
const jsonImport = isJson ? `${source} with { type: "json" }` : source;
|
|
81
|
+
imports.push(`import ${jsonImport};\n`);
|
|
82
|
+
transforms.push({
|
|
83
|
+
start: stmt.start,
|
|
84
|
+
end: stmt.end,
|
|
85
|
+
code: ';\n'
|
|
86
|
+
});
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (expr && isRequireCall(expr, shadowed)) {
|
|
90
|
+
needsCreateRequire = true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
transforms,
|
|
96
|
+
imports,
|
|
97
|
+
hoisted,
|
|
98
|
+
needsCreateRequire,
|
|
99
|
+
needsInteropHelper
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
export { isRequireCall, isStaticRequire, lowerCjsRequireToImports };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import MagicString from 'magic-string';
|
|
2
|
+
import { type ProgramNode } from '../helpers/ast.js';
|
|
3
|
+
import type { FormatterOptions } from '../types.js';
|
|
4
|
+
declare const exportAssignment: (name: string, expr: string, live: "strict" | "loose" | "off") => string;
|
|
5
|
+
type ImportTransform = {
|
|
6
|
+
start: number;
|
|
7
|
+
end: number;
|
|
8
|
+
code: string;
|
|
9
|
+
needsInterop: boolean;
|
|
10
|
+
};
|
|
11
|
+
type ExportTransform = {
|
|
12
|
+
start: number;
|
|
13
|
+
end: number;
|
|
14
|
+
code: string;
|
|
15
|
+
needsInterop?: boolean;
|
|
16
|
+
};
|
|
17
|
+
declare const lowerEsmToCjs: (program: ProgramNode, code: MagicString, opts: FormatterOptions, containsTopLevelAwait: boolean) => {
|
|
18
|
+
importTransforms: ImportTransform[];
|
|
19
|
+
exportTransforms: ExportTransform[];
|
|
20
|
+
needsInterop: boolean;
|
|
21
|
+
};
|
|
22
|
+
export { exportAssignment, lowerEsmToCjs, type ExportTransform, type ImportTransform };
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { getModuleExportName } from '../helpers/ast.js';
|
|
2
|
+
import { defaultInteropName } from './interopHelpers.js';
|
|
3
|
+
const isValidIdent = name => /^[$A-Z_a-z][$\w]*$/.test(name);
|
|
4
|
+
const exportAssignment = (name, expr, live) => {
|
|
5
|
+
const prop = isValidIdent(name) ? `.${name}` : `[${JSON.stringify(name)}]`;
|
|
6
|
+
if (live === 'strict') {
|
|
7
|
+
const key = JSON.stringify(name);
|
|
8
|
+
return `Object.defineProperty(exports, ${key}, { enumerable: true, get: () => ${expr} });`;
|
|
9
|
+
}
|
|
10
|
+
return `exports${prop} = ${expr};`;
|
|
11
|
+
};
|
|
12
|
+
const lowerEsmToCjs = (program, code, opts, containsTopLevelAwait) => {
|
|
13
|
+
const live = opts.liveBindings ?? 'strict';
|
|
14
|
+
const importTransforms = [];
|
|
15
|
+
const exportTransforms = [];
|
|
16
|
+
let needsInterop = false;
|
|
17
|
+
let importIndex = 0;
|
|
18
|
+
for (const node of program.body) {
|
|
19
|
+
if (node.type === 'ImportDeclaration') {
|
|
20
|
+
const srcLiteral = code.slice(node.source.start, node.source.end);
|
|
21
|
+
const specifiers = node.specifiers ?? [];
|
|
22
|
+
const defaultSpec = specifiers.find(s => s.type === 'ImportDefaultSpecifier');
|
|
23
|
+
const namespaceSpec = specifiers.find(s => s.type === 'ImportNamespaceSpecifier');
|
|
24
|
+
const namedSpecs = specifiers.filter(s => s.type === 'ImportSpecifier');
|
|
25
|
+
if (!specifiers.length) {
|
|
26
|
+
importTransforms.push({
|
|
27
|
+
start: node.start,
|
|
28
|
+
end: node.end,
|
|
29
|
+
code: `require(${srcLiteral});\n`,
|
|
30
|
+
needsInterop: false
|
|
31
|
+
});
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const modIdent = `__mod${importIndex++}`;
|
|
35
|
+
const lines = [];
|
|
36
|
+
lines.push(`const ${modIdent} = require(${srcLiteral});`);
|
|
37
|
+
if (namespaceSpec) {
|
|
38
|
+
lines.push(`const ${namespaceSpec.local.name} = ${modIdent};`);
|
|
39
|
+
}
|
|
40
|
+
if (defaultSpec) {
|
|
41
|
+
let init = modIdent;
|
|
42
|
+
switch (opts.cjsDefault) {
|
|
43
|
+
case 'module-exports':
|
|
44
|
+
init = modIdent;
|
|
45
|
+
break;
|
|
46
|
+
case 'none':
|
|
47
|
+
init = `${modIdent}.default`;
|
|
48
|
+
break;
|
|
49
|
+
case 'auto':
|
|
50
|
+
default:
|
|
51
|
+
init = `${defaultInteropName}(${modIdent})`;
|
|
52
|
+
needsInterop = true;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
lines.push(`const ${defaultSpec.local.name} = ${init};`);
|
|
56
|
+
}
|
|
57
|
+
if (namedSpecs.length) {
|
|
58
|
+
const pairs = namedSpecs.map(s => {
|
|
59
|
+
const imported = getModuleExportName(s.imported);
|
|
60
|
+
if (!imported) return s.local.name;
|
|
61
|
+
const local = s.local.name;
|
|
62
|
+
return imported === local ? imported : `${imported}: ${local}`;
|
|
63
|
+
});
|
|
64
|
+
lines.push(`const { ${pairs.join(', ')} } = ${modIdent};`);
|
|
65
|
+
}
|
|
66
|
+
importTransforms.push({
|
|
67
|
+
start: node.start,
|
|
68
|
+
end: node.end,
|
|
69
|
+
code: `${lines.join('\n')}\n`,
|
|
70
|
+
needsInterop
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (node.type === 'ExportNamedDeclaration') {
|
|
74
|
+
if (node.declaration) {
|
|
75
|
+
const decl = node.declaration;
|
|
76
|
+
const declSrc = code.slice(decl.start, decl.end);
|
|
77
|
+
const exportedNames = [];
|
|
78
|
+
if (decl.type === 'VariableDeclaration') {
|
|
79
|
+
for (const d of decl.declarations) {
|
|
80
|
+
if (d.id.type === 'Identifier') {
|
|
81
|
+
exportedNames.push(d.id.name);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} else if ('id' in decl && decl.id?.type === 'Identifier') {
|
|
85
|
+
exportedNames.push(decl.id.name);
|
|
86
|
+
}
|
|
87
|
+
const exportLines = exportedNames.map(name => exportAssignment(name, name, live));
|
|
88
|
+
exportTransforms.push({
|
|
89
|
+
start: node.start,
|
|
90
|
+
end: node.end,
|
|
91
|
+
code: `${declSrc}\n${exportLines.join('\n')}\n`
|
|
92
|
+
});
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (node.specifiers?.length) {
|
|
96
|
+
if (node.source) {
|
|
97
|
+
const srcLiteral = code.slice(node.source.start, node.source.end);
|
|
98
|
+
const modIdent = `__mod${importIndex++}`;
|
|
99
|
+
const lines = [`const ${modIdent} = require(${srcLiteral});`];
|
|
100
|
+
for (const spec of node.specifiers) {
|
|
101
|
+
if (spec.type !== 'ExportSpecifier') continue;
|
|
102
|
+
const exported = getModuleExportName(spec.exported);
|
|
103
|
+
const imported = getModuleExportName(spec.local);
|
|
104
|
+
if (!exported || !imported) continue;
|
|
105
|
+
let rhs = `${modIdent}.${imported}`;
|
|
106
|
+
if (imported === 'default') {
|
|
107
|
+
rhs = `${defaultInteropName}(${modIdent})`;
|
|
108
|
+
needsInterop = true;
|
|
109
|
+
}
|
|
110
|
+
lines.push(exportAssignment(exported, rhs, live));
|
|
111
|
+
}
|
|
112
|
+
exportTransforms.push({
|
|
113
|
+
start: node.start,
|
|
114
|
+
end: node.end,
|
|
115
|
+
code: `${lines.join('\n')}\n`,
|
|
116
|
+
needsInterop
|
|
117
|
+
});
|
|
118
|
+
} else {
|
|
119
|
+
const lines = [];
|
|
120
|
+
for (const spec of node.specifiers) {
|
|
121
|
+
if (spec.type !== 'ExportSpecifier') continue;
|
|
122
|
+
const exported = getModuleExportName(spec.exported);
|
|
123
|
+
const local = getModuleExportName(spec.local);
|
|
124
|
+
if (!exported || !local) continue;
|
|
125
|
+
lines.push(exportAssignment(exported, local, live));
|
|
126
|
+
}
|
|
127
|
+
exportTransforms.push({
|
|
128
|
+
start: node.start,
|
|
129
|
+
end: node.end,
|
|
130
|
+
code: `${lines.join('\n')}\n`
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (node.type === 'ExportDefaultDeclaration') {
|
|
136
|
+
const decl = node.declaration;
|
|
137
|
+
const useExportsObject = containsTopLevelAwait && opts.topLevelAwait !== 'error';
|
|
138
|
+
if (decl.type === 'FunctionDeclaration' || decl.type === 'ClassDeclaration') {
|
|
139
|
+
if (decl.id?.name) {
|
|
140
|
+
const declSrc = code.slice(decl.start, decl.end);
|
|
141
|
+
const assign = useExportsObject ? `exports.default = ${decl.id.name};` : `module.exports = ${decl.id.name};`;
|
|
142
|
+
exportTransforms.push({
|
|
143
|
+
start: node.start,
|
|
144
|
+
end: node.end,
|
|
145
|
+
code: `${declSrc}\n${assign}\n`
|
|
146
|
+
});
|
|
147
|
+
} else {
|
|
148
|
+
const declSrc = code.slice(decl.start, decl.end);
|
|
149
|
+
const assign = useExportsObject ? `exports.default = ${declSrc};` : `module.exports = ${declSrc};`;
|
|
150
|
+
exportTransforms.push({
|
|
151
|
+
start: node.start,
|
|
152
|
+
end: node.end,
|
|
153
|
+
code: `${assign}\n`
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
const exprSrc = code.slice(decl.start, decl.end);
|
|
158
|
+
const assign = useExportsObject ? `exports.default = ${exprSrc};` : `module.exports = ${exprSrc};`;
|
|
159
|
+
exportTransforms.push({
|
|
160
|
+
start: node.start,
|
|
161
|
+
end: node.end,
|
|
162
|
+
code: `${assign}\n`
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (node.type === 'ExportAllDeclaration') {
|
|
167
|
+
const srcLiteral = code.slice(node.source.start, node.source.end);
|
|
168
|
+
if ('exported' in node && node.exported) {
|
|
169
|
+
const exported = getModuleExportName(node.exported);
|
|
170
|
+
if (!exported) continue;
|
|
171
|
+
const modIdent = `__mod${importIndex++}`;
|
|
172
|
+
const lines = [`const ${modIdent} = require(${srcLiteral});`, exportAssignment(exported, modIdent, live)];
|
|
173
|
+
exportTransforms.push({
|
|
174
|
+
start: node.start,
|
|
175
|
+
end: node.end,
|
|
176
|
+
code: `${lines.join('\n')}\n`
|
|
177
|
+
});
|
|
178
|
+
} else {
|
|
179
|
+
const modIdent = `__mod${importIndex++}`;
|
|
180
|
+
const lines = [`const ${modIdent} = require(${srcLiteral});`];
|
|
181
|
+
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}`;
|
|
182
|
+
lines.push(loop);
|
|
183
|
+
exportTransforms.push({
|
|
184
|
+
start: node.start,
|
|
185
|
+
end: node.end,
|
|
186
|
+
code: `${lines.join('\n')}\n`
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
importTransforms,
|
|
193
|
+
exportTransforms,
|
|
194
|
+
needsInterop
|
|
195
|
+
};
|
|
196
|
+
};
|
|
197
|
+
export { exportAssignment, lowerEsmToCjs };
|