@shriyanss/js-recon 1.3.1-beta.2 → 1.4.1-alpha.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/CHANGELOG.md +56 -0
- package/CLAUDE.md +50 -0
- package/README.md +1 -0
- package/build/cs_mast/index.js +122 -0
- package/build/cs_mast/index.js.map +1 -0
- package/build/globalConfig.js +2 -2
- package/build/globalConfig.js.map +1 -1
- package/build/index.js +32 -2
- package/build/index.js.map +1 -1
- package/build/lazyLoad/downloadLoadedJsUtil.js +6 -1
- package/build/lazyLoad/downloadLoadedJsUtil.js.map +1 -1
- package/build/lazyLoad/index.js +2 -1
- package/build/lazyLoad/index.js.map +1 -1
- package/build/lazyLoad/methodFilter.js +49 -0
- package/build/lazyLoad/methodFilter.js.map +1 -0
- package/build/lazyLoad/next_js/NextJsCrawler.js +12 -11
- package/build/lazyLoad/next_js/NextJsCrawler.js.map +1 -1
- package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js +6 -1
- package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js.map +1 -1
- package/build/lazyLoad/techDetect/checkNextJS.js +24 -15
- package/build/lazyLoad/techDetect/checkNextJS.js.map +1 -1
- package/build/lazyLoad/techDetect/checkSvelte.js +17 -0
- package/build/lazyLoad/techDetect/checkSvelte.js.map +1 -1
- package/build/lazyLoad/techDetect/index.js +11 -4
- package/build/lazyLoad/techDetect/index.js.map +1 -1
- package/build/map/vue_js/vue_resolveHttpClient.js +17 -2
- package/build/map/vue_js/vue_resolveHttpClient.js.map +1 -1
- package/build/map/vue_js/vue_resolveXhr.js +22 -2
- package/build/map/vue_js/vue_resolveXhr.js.map +1 -1
- package/build/refactor/index.js +126 -15
- package/build/refactor/index.js.map +1 -1
- package/build/refactor/next/helpers.js +214 -0
- package/build/refactor/next/helpers.js.map +1 -0
- package/build/refactor/next/index.js +40 -73
- package/build/refactor/next/index.js.map +1 -1
- package/build/refactor/next/transform.js +177 -0
- package/build/refactor/next/transform.js.map +1 -0
- package/build/refactor/next/validator.js +118 -0
- package/build/refactor/next/validator.js.map +1 -0
- package/build/refactor/react/helpers.js +93 -0
- package/build/refactor/react/helpers.js.map +1 -0
- package/build/refactor/react/index.js +186 -589
- package/build/refactor/react/index.js.map +1 -1
- package/build/refactor/react/library-classify.js +166 -0
- package/build/refactor/react/library-classify.js.map +1 -0
- package/build/refactor/react/transform.js +839 -0
- package/build/refactor/react/transform.js.map +1 -0
- package/build/refactor/react/validator.js +122 -0
- package/build/refactor/react/validator.js.map +1 -0
- package/build/run/index.js +13 -8
- package/build/run/index.js.map +1 -1
- package/build/utility/getChromiumPath.js +40 -0
- package/build/utility/getChromiumPath.js.map +1 -0
- package/build/utility/heap.js +42 -0
- package/build/utility/heap.js.map +1 -0
- package/build/utility/makeReq.js +11 -2
- package/build/utility/makeReq.js.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,839 @@
|
|
|
1
|
+
import _traverse from "@babel/traverse";
|
|
2
|
+
import _generator from "@babel/generator";
|
|
3
|
+
import * as t from "@babel/types";
|
|
4
|
+
import { tryExtractModuleExportsAssignment, tryExtractExportsAssignment, tryExtractRequireCall, buildModuleExportStatement, makeNamedExportStatement, } from "./helpers.js";
|
|
5
|
+
import { REACT_CANONICAL, JSX_RUNTIME_CANONICAL, REACT_DOM_CLIENT_CANONICAL, librarySource, } from "./library-classify.js";
|
|
6
|
+
const traverse = _traverse.default;
|
|
7
|
+
const generate = _generator.default;
|
|
8
|
+
/**
|
|
9
|
+
* Transforms a single webpack module function into an array of ES module statements.
|
|
10
|
+
*
|
|
11
|
+
* Four passes (all top-level only to avoid placing exports inside nested functions):
|
|
12
|
+
* 1. `<moduleParam>.exports = <rhs>` → `export * from` or `export default`
|
|
13
|
+
* 2. `<exportsParam>.<prop> = <rhs>` → named export (all modules with an exportsParam)
|
|
14
|
+
* 3. Hoist `var x = <requireParam>(N)` to static `import * as x from "./N.js"`
|
|
15
|
+
* 4. Replace remaining inline `<requireParam>(N)` calls with the hoisted name
|
|
16
|
+
*/
|
|
17
|
+
export const transformModule = (mod) => {
|
|
18
|
+
const { fnPath, moduleParam, exportsParam, requireParam } = mod;
|
|
19
|
+
const body = fnPath.node.body;
|
|
20
|
+
if (!t.isBlockStatement(body))
|
|
21
|
+
return [];
|
|
22
|
+
// Pass 1 — handle `<moduleParam>.exports = <rhs>` at the top level only.
|
|
23
|
+
if (moduleParam) {
|
|
24
|
+
const next = [];
|
|
25
|
+
for (const stmt of body.body) {
|
|
26
|
+
if (!t.isExpressionStatement(stmt)) {
|
|
27
|
+
next.push(stmt);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const expr = stmt.expression;
|
|
31
|
+
const directRhs = tryExtractModuleExportsAssignment(expr, moduleParam);
|
|
32
|
+
if (directRhs) {
|
|
33
|
+
next.push(buildModuleExportStatement(directRhs, requireParam));
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (t.isSequenceExpression(expr)) {
|
|
37
|
+
let hadMatch = false;
|
|
38
|
+
const splitted = [];
|
|
39
|
+
for (const sub of expr.expressions) {
|
|
40
|
+
const rhs = tryExtractModuleExportsAssignment(sub, moduleParam);
|
|
41
|
+
if (rhs) {
|
|
42
|
+
hadMatch = true;
|
|
43
|
+
splitted.push(buildModuleExportStatement(rhs, requireParam));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
splitted.push(t.expressionStatement(sub));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (hadMatch) {
|
|
50
|
+
next.push(...splitted);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
next.push(stmt);
|
|
55
|
+
}
|
|
56
|
+
body.body = next;
|
|
57
|
+
}
|
|
58
|
+
// Pass 2 — handle `<exportsParam>.<propName> = <rhs>` at the top level only.
|
|
59
|
+
if (exportsParam) {
|
|
60
|
+
const next = [];
|
|
61
|
+
for (const stmt of body.body) {
|
|
62
|
+
if (!t.isExpressionStatement(stmt)) {
|
|
63
|
+
next.push(stmt);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const expr = stmt.expression;
|
|
67
|
+
const direct = tryExtractExportsAssignment(expr, exportsParam);
|
|
68
|
+
if (direct) {
|
|
69
|
+
next.push(makeNamedExportStatement(direct.propName, direct.rhs));
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (t.isSequenceExpression(expr)) {
|
|
73
|
+
const splitted = [];
|
|
74
|
+
for (const sub of expr.expressions) {
|
|
75
|
+
const match = tryExtractExportsAssignment(sub, exportsParam);
|
|
76
|
+
if (match) {
|
|
77
|
+
splitted.push(makeNamedExportStatement(match.propName, match.rhs));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
splitted.push(t.expressionStatement(sub));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
next.push(...splitted);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
next.push(stmt);
|
|
87
|
+
}
|
|
88
|
+
body.body = next;
|
|
89
|
+
}
|
|
90
|
+
// Pass 3 — hoist top-level `var <name> = <requireParam>(N)` to static imports.
|
|
91
|
+
// moduleSpec → importName
|
|
92
|
+
const hoistedImports = new Map();
|
|
93
|
+
// numId → canonical importName (used by Pass 4 for inline replacements)
|
|
94
|
+
const importNameByNumId = new Map();
|
|
95
|
+
if (requireParam) {
|
|
96
|
+
const next = [];
|
|
97
|
+
for (const stmt of body.body) {
|
|
98
|
+
if (!t.isVariableDeclaration(stmt)) {
|
|
99
|
+
next.push(stmt);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const kept = [];
|
|
103
|
+
for (const decl of stmt.declarations) {
|
|
104
|
+
if (!t.isIdentifier(decl.id) || !decl.init) {
|
|
105
|
+
kept.push(decl);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const numId = tryExtractRequireCall(decl.init, requireParam);
|
|
109
|
+
if (numId === null) {
|
|
110
|
+
kept.push(decl);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const spec = `./${numId}.js`;
|
|
114
|
+
if (!hoistedImports.has(spec)) {
|
|
115
|
+
hoistedImports.set(spec, decl.id.name);
|
|
116
|
+
importNameByNumId.set(numId, decl.id.name);
|
|
117
|
+
}
|
|
118
|
+
// declarator removed (it becomes the static import)
|
|
119
|
+
}
|
|
120
|
+
if (kept.length > 0) {
|
|
121
|
+
next.push(t.variableDeclaration(stmt.kind, kept));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
body.body = next;
|
|
125
|
+
}
|
|
126
|
+
// Pass 4 — replace any remaining inline `<requireParam>(N)` calls with a synthesized
|
|
127
|
+
// namespace import reference.
|
|
128
|
+
if (requireParam) {
|
|
129
|
+
fnPath.traverse({
|
|
130
|
+
CallExpression(p) {
|
|
131
|
+
const numId = tryExtractRequireCall(p.node, requireParam);
|
|
132
|
+
if (numId === null)
|
|
133
|
+
return;
|
|
134
|
+
let name = importNameByNumId.get(numId);
|
|
135
|
+
if (!name) {
|
|
136
|
+
name = `_jsr_module_${numId}`;
|
|
137
|
+
hoistedImports.set(`./${numId}.js`, name);
|
|
138
|
+
importNameByNumId.set(numId, name);
|
|
139
|
+
}
|
|
140
|
+
p.replaceWith(t.identifier(name));
|
|
141
|
+
p.skip();
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
// Step 5 — strip outer function wrapper, prepend hoisted static imports.
|
|
146
|
+
const importStmts = [];
|
|
147
|
+
for (const [spec, name] of hoistedImports) {
|
|
148
|
+
importStmts.push(t.importDeclaration([t.importNamespaceSpecifier(t.identifier(name))], t.stringLiteral(spec)));
|
|
149
|
+
}
|
|
150
|
+
return [...importStmts, ...body.body];
|
|
151
|
+
};
|
|
152
|
+
// Returns true when fn matches the webpack runtime require helper:
|
|
153
|
+
// function X(id) { … return (moduleMap[id](mod, mod.exports, X), mod.exports); }
|
|
154
|
+
// The key signal is the final return statement: a SequenceExpression whose first element
|
|
155
|
+
// is a computed-member CallExpression and whose second is a `<var>.exports` MemberExpression.
|
|
156
|
+
const isWebpackRequireHelper = (fn) => {
|
|
157
|
+
if (fn.params.length !== 1)
|
|
158
|
+
return false;
|
|
159
|
+
for (const stmt of fn.body.body) {
|
|
160
|
+
if (!t.isReturnStatement(stmt) || !stmt.argument)
|
|
161
|
+
continue;
|
|
162
|
+
const arg = stmt.argument;
|
|
163
|
+
if (!t.isSequenceExpression(arg) || arg.expressions.length !== 2)
|
|
164
|
+
continue;
|
|
165
|
+
const [first, second] = arg.expressions;
|
|
166
|
+
if (!t.isCallExpression(first))
|
|
167
|
+
continue;
|
|
168
|
+
if (!t.isMemberExpression(first.callee) || !first.callee.computed)
|
|
169
|
+
continue;
|
|
170
|
+
if (!t.isMemberExpression(second))
|
|
171
|
+
continue;
|
|
172
|
+
if (!t.isIdentifier(second.property, { name: "exports" }))
|
|
173
|
+
continue;
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
return false;
|
|
177
|
+
};
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
// Pass D helpers — library-aware call-site rewriting
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
/** Resolves the canonical name for an accessed property on a library-bound local var.
|
|
182
|
+
* Returns null if the var is not a known library or the prop has no canonical mapping. */
|
|
183
|
+
function resolveLibraryProp(varName, prop, varToLib) {
|
|
184
|
+
const info = varToLib.get(varName);
|
|
185
|
+
if (!info || info.type === "unknown")
|
|
186
|
+
return null;
|
|
187
|
+
// Helper: is this name actually a canonical export for this library type?
|
|
188
|
+
const isCanonical = (name) => {
|
|
189
|
+
if (info.type === "react")
|
|
190
|
+
return REACT_CANONICAL.has(name);
|
|
191
|
+
// Fragment is also exported by react/jsx-runtime but excluded from JSX_RUNTIME_CANONICAL
|
|
192
|
+
// (which is used only for module classification). Accept it explicitly here for rewriting.
|
|
193
|
+
if (info.type === "react-jsx-runtime")
|
|
194
|
+
return JSX_RUNTIME_CANONICAL.has(name) || name === "Fragment";
|
|
195
|
+
if (info.type === "react-dom-client")
|
|
196
|
+
return REACT_DOM_CLIENT_CANONICAL.has(name);
|
|
197
|
+
return false;
|
|
198
|
+
};
|
|
199
|
+
// Check exportMap: only trust the value if it IS a canonical name.
|
|
200
|
+
// (Avoids mapping e.g. n.jsx = i → canonical 'i' when i is just a local fn.)
|
|
201
|
+
const viaMap = info.exportMap.get(prop);
|
|
202
|
+
if (viaMap && isCanonical(viaMap))
|
|
203
|
+
return { canonical: viaMap, libType: info.type };
|
|
204
|
+
// Fall back to checking the prop name itself (handles cases where the exported
|
|
205
|
+
// property name is already the canonical name, e.g. n.jsx = <iife>).
|
|
206
|
+
if (isCanonical(prop))
|
|
207
|
+
return { canonical: prop, libType: info.type };
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
/** Extracts `varName` and `prop` from a `(0, varName.prop)` or `varName.prop` callee. */
|
|
211
|
+
function tryExtractMemberCallee(callee) {
|
|
212
|
+
// (0, X.Y) form
|
|
213
|
+
if (t.isSequenceExpression(callee) && callee.expressions.length === 2) {
|
|
214
|
+
const [first, second] = callee.expressions;
|
|
215
|
+
if (t.isNumericLiteral(first) && first.value === 0 && t.isMemberExpression(second)) {
|
|
216
|
+
const obj = second.object;
|
|
217
|
+
const prop = second.property;
|
|
218
|
+
if (t.isIdentifier(obj) && t.isIdentifier(prop) && !second.computed) {
|
|
219
|
+
return { varName: obj.name, prop: prop.name };
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// X.Y form
|
|
224
|
+
if (t.isMemberExpression(callee) && !callee.computed) {
|
|
225
|
+
const obj = callee.object;
|
|
226
|
+
const prop = callee.property;
|
|
227
|
+
if (t.isIdentifier(obj) && t.isIdentifier(prop)) {
|
|
228
|
+
return { varName: obj.name, prop: prop.name };
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Pass D – rewrite library calls and collect which named exports are needed.
|
|
235
|
+
*
|
|
236
|
+
* Traverses all statements; for every `(0, X.Y)(args)` or `X.Y(args)` where X is a
|
|
237
|
+
* known library local var, replaces the callee with the canonical identifier and records
|
|
238
|
+
* the needed named import. Also rewrites bare member expressions `X.Y` that are NOT in a
|
|
239
|
+
* call position (e.g. passed as a value to another function).
|
|
240
|
+
*
|
|
241
|
+
* Returns the set of named exports needed per library type so the caller can build
|
|
242
|
+
* proper `import { ... } from "..."` declarations.
|
|
243
|
+
*/
|
|
244
|
+
function rewriteLibraryCalls(statements, varToLib) {
|
|
245
|
+
// libType → set of canonical export names actually used
|
|
246
|
+
const usedExports = new Map();
|
|
247
|
+
const record = (libType, name) => {
|
|
248
|
+
const src = librarySource(libType);
|
|
249
|
+
if (!src)
|
|
250
|
+
return;
|
|
251
|
+
if (!usedExports.has(src))
|
|
252
|
+
usedExports.set(src, new Set());
|
|
253
|
+
usedExports.get(src).add(name);
|
|
254
|
+
};
|
|
255
|
+
const syntheticFile = t.file(t.program(statements, [], "module"));
|
|
256
|
+
traverse(syntheticFile, {
|
|
257
|
+
CallExpression(p) {
|
|
258
|
+
const info = tryExtractMemberCallee(p.node.callee);
|
|
259
|
+
if (!info)
|
|
260
|
+
return;
|
|
261
|
+
const resolved = resolveLibraryProp(info.varName, info.prop, varToLib);
|
|
262
|
+
if (!resolved)
|
|
263
|
+
return;
|
|
264
|
+
record(resolved.libType, resolved.canonical);
|
|
265
|
+
// Replace (0, X.Y)(...) or X.Y(...) with canonicalName(...)
|
|
266
|
+
p.node.callee = t.identifier(resolved.canonical);
|
|
267
|
+
// Do NOT skip — arguments may contain further (0, X.Y)(...) or X.Y member refs.
|
|
268
|
+
},
|
|
269
|
+
MemberExpression(p) {
|
|
270
|
+
// Rewrite X.Y in non-call positions (e.g. passed as callback value)
|
|
271
|
+
if (p.parent && t.isCallExpression(p.parent) && p.parent.callee === p.node)
|
|
272
|
+
return; // handled above
|
|
273
|
+
const obj = p.node.object;
|
|
274
|
+
const prop = p.node.property;
|
|
275
|
+
if (!t.isIdentifier(obj) || !t.isIdentifier(prop) || p.node.computed)
|
|
276
|
+
return;
|
|
277
|
+
const resolved = resolveLibraryProp(obj.name, prop.name, varToLib);
|
|
278
|
+
if (!resolved)
|
|
279
|
+
return;
|
|
280
|
+
record(resolved.libType, resolved.canonical);
|
|
281
|
+
p.replaceWith(t.identifier(resolved.canonical));
|
|
282
|
+
p.skip();
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
return usedExports;
|
|
286
|
+
}
|
|
287
|
+
// ---------------------------------------------------------------------------
|
|
288
|
+
// Pass E — Babel slicedToArray collapse
|
|
289
|
+
// ---------------------------------------------------------------------------
|
|
290
|
+
/** Returns true if the last arm of a `||` chain is an IIFE that throws TypeError. */
|
|
291
|
+
function isSlicedToArrayTail(node) {
|
|
292
|
+
if (!t.isCallExpression(node) || node.arguments.length !== 0)
|
|
293
|
+
return false;
|
|
294
|
+
const callee = node.callee;
|
|
295
|
+
if (!t.isFunctionExpression(callee) && !t.isArrowFunctionExpression(callee))
|
|
296
|
+
return false;
|
|
297
|
+
const body = callee.body;
|
|
298
|
+
if (!t.isBlockStatement(body))
|
|
299
|
+
return false;
|
|
300
|
+
return body.body.some((s) => t.isThrowStatement(s) &&
|
|
301
|
+
t.isNewExpression(s.argument) &&
|
|
302
|
+
t.isIdentifier(s.argument.callee, { name: "TypeError" }));
|
|
303
|
+
}
|
|
304
|
+
function hasSlicedToArrayTail(node) {
|
|
305
|
+
if (isSlicedToArrayTail(node))
|
|
306
|
+
return true;
|
|
307
|
+
if (t.isLogicalExpression(node) && node.operator === "||")
|
|
308
|
+
return hasSlicedToArrayTail(node.right);
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Tries to detect a `var <temps>, resultVar = (<seqExpr>), target0 = resultVar[0], ...`
|
|
313
|
+
* pattern produced by Babel's slicedToArray expansion.
|
|
314
|
+
*
|
|
315
|
+
* Returns `{ actualExpr, resultVar, targets }` on match, or null.
|
|
316
|
+
*/
|
|
317
|
+
function tryDetectSlicedToArray(decl) {
|
|
318
|
+
let resultVar = null;
|
|
319
|
+
let actualExpr = null;
|
|
320
|
+
for (const d of decl.declarations) {
|
|
321
|
+
if (!d.init || !t.isSequenceExpression(d.init))
|
|
322
|
+
continue;
|
|
323
|
+
const exprs = d.init.expressions;
|
|
324
|
+
if (exprs.length < 3)
|
|
325
|
+
continue;
|
|
326
|
+
// exprs[0]: tempVar = <actualCall>
|
|
327
|
+
if (!t.isAssignmentExpression(exprs[0]) || exprs[0].operator !== "=")
|
|
328
|
+
continue;
|
|
329
|
+
const firstAssign = exprs[0];
|
|
330
|
+
if (!t.isIdentifier(firstAssign.left))
|
|
331
|
+
continue;
|
|
332
|
+
// exprs[1]: countVar = <number>
|
|
333
|
+
if (!t.isAssignmentExpression(exprs[1]) || !t.isNumericLiteral(exprs[1].right))
|
|
334
|
+
continue;
|
|
335
|
+
// last expr: the TypeError IIFE (possibly nested in || chain)
|
|
336
|
+
if (!hasSlicedToArrayTail(exprs[exprs.length - 1]))
|
|
337
|
+
continue;
|
|
338
|
+
resultVar = t.isIdentifier(d.id) ? d.id.name : null;
|
|
339
|
+
if (!resultVar)
|
|
340
|
+
continue;
|
|
341
|
+
actualExpr = firstAssign.right;
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
if (!resultVar || !actualExpr)
|
|
345
|
+
return null;
|
|
346
|
+
const targets = [];
|
|
347
|
+
for (const d of decl.declarations) {
|
|
348
|
+
if (!d.init || !t.isMemberExpression(d.init))
|
|
349
|
+
continue;
|
|
350
|
+
if (!t.isIdentifier(d.init.object, { name: resultVar }))
|
|
351
|
+
continue;
|
|
352
|
+
if (!d.init.computed || !t.isNumericLiteral(d.init.property))
|
|
353
|
+
continue;
|
|
354
|
+
if (!t.isIdentifier(d.id))
|
|
355
|
+
continue;
|
|
356
|
+
targets.push({ id: d.id, index: d.init.property.value });
|
|
357
|
+
}
|
|
358
|
+
return targets.length > 0 ? { actualExpr, resultVar, targets } : null;
|
|
359
|
+
}
|
|
360
|
+
/** Collapse Babel slicedToArray expansions in a statement list (including nested blocks). */
|
|
361
|
+
function collapseSlicedToArray(statements) {
|
|
362
|
+
const out = [];
|
|
363
|
+
for (const stmt of statements) {
|
|
364
|
+
// Recurse into block statements (e.g. if/else, function bodies handled via traversal)
|
|
365
|
+
if (t.isBlockStatement(stmt)) {
|
|
366
|
+
stmt.body = collapseSlicedToArray(stmt.body);
|
|
367
|
+
out.push(stmt);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (!t.isVariableDeclaration(stmt)) {
|
|
371
|
+
out.push(stmt);
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
const match = tryDetectSlicedToArray(stmt);
|
|
375
|
+
if (!match) {
|
|
376
|
+
out.push(stmt);
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
const { actualExpr, resultVar, targets } = match;
|
|
380
|
+
// Sort targets by index so array pattern elements are in order
|
|
381
|
+
const sorted = [...targets].sort((a, b) => a.index - b.index);
|
|
382
|
+
const pattern = t.arrayPattern(sorted.map((tgt) => tgt.id));
|
|
383
|
+
out.push(t.variableDeclaration("const", [t.variableDeclarator(pattern, actualExpr)]));
|
|
384
|
+
}
|
|
385
|
+
return out;
|
|
386
|
+
}
|
|
387
|
+
/** Traverse all function bodies in a statement list and collapse slicedToArray inside them. */
|
|
388
|
+
function collapseSlicedToArrayDeep(statements) {
|
|
389
|
+
// First collapse top-level
|
|
390
|
+
const top = collapseSlicedToArray(statements);
|
|
391
|
+
// Then recurse into function bodies via a traverse
|
|
392
|
+
const syntheticFile = t.file(t.program(top, [], "module"));
|
|
393
|
+
traverse(syntheticFile, {
|
|
394
|
+
BlockStatement(p) {
|
|
395
|
+
const collapsed = collapseSlicedToArray(p.node.body);
|
|
396
|
+
if (collapsed !== p.node.body) {
|
|
397
|
+
p.node.body = collapsed;
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
return top;
|
|
402
|
+
}
|
|
403
|
+
// ---------------------------------------------------------------------------
|
|
404
|
+
// Pass F — JSX recovery
|
|
405
|
+
// ---------------------------------------------------------------------------
|
|
406
|
+
function exprToJsxName(expr) {
|
|
407
|
+
if (t.isStringLiteral(expr))
|
|
408
|
+
return t.jsxIdentifier(expr.value);
|
|
409
|
+
if (t.isIdentifier(expr))
|
|
410
|
+
return t.jsxIdentifier(expr.name);
|
|
411
|
+
if (t.isMemberExpression(expr) && !expr.computed && t.isIdentifier(expr.property)) {
|
|
412
|
+
const obj = exprToJsxName(expr.object);
|
|
413
|
+
if (obj)
|
|
414
|
+
return t.jsxMemberExpression(obj, t.jsxIdentifier(expr.property.name));
|
|
415
|
+
}
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
function exprToJsxAttrValue(expr) {
|
|
419
|
+
if (t.isStringLiteral(expr))
|
|
420
|
+
return expr;
|
|
421
|
+
return t.jsxExpressionContainer(expr);
|
|
422
|
+
}
|
|
423
|
+
function childToJsxChild(child) {
|
|
424
|
+
if (t.isStringLiteral(child))
|
|
425
|
+
return t.jsxText(child.value);
|
|
426
|
+
if (t.isJSXElement(child) || t.isJSXFragment(child))
|
|
427
|
+
return child;
|
|
428
|
+
// Recursively convert nested jsx(...) calls into JSX elements.
|
|
429
|
+
if (t.isCallExpression(child)) {
|
|
430
|
+
const converted = tryConvertToJSX(child);
|
|
431
|
+
if (converted)
|
|
432
|
+
return converted;
|
|
433
|
+
}
|
|
434
|
+
return t.jsxExpressionContainer(child);
|
|
435
|
+
}
|
|
436
|
+
function tryConvertToJSX(call) {
|
|
437
|
+
const callee = call.callee;
|
|
438
|
+
if (!t.isIdentifier(callee))
|
|
439
|
+
return null;
|
|
440
|
+
if (callee.name !== "jsx" && callee.name !== "jsxs" && callee.name !== "jsxDEV")
|
|
441
|
+
return null;
|
|
442
|
+
if (call.arguments.length < 2)
|
|
443
|
+
return null;
|
|
444
|
+
const tagArg = call.arguments[0];
|
|
445
|
+
const propsArg = call.arguments[1];
|
|
446
|
+
const jsxName = exprToJsxName(tagArg);
|
|
447
|
+
if (!jsxName)
|
|
448
|
+
return null;
|
|
449
|
+
const attrs = [];
|
|
450
|
+
const children = [];
|
|
451
|
+
if (t.isObjectExpression(propsArg)) {
|
|
452
|
+
for (const prop of propsArg.properties) {
|
|
453
|
+
if (!t.isObjectProperty(prop) || prop.computed)
|
|
454
|
+
continue;
|
|
455
|
+
const keyNode = prop.key;
|
|
456
|
+
const valNode = prop.value;
|
|
457
|
+
const keyName = t.isIdentifier(keyNode) ? keyNode.name : t.isStringLiteral(keyNode) ? keyNode.value : null;
|
|
458
|
+
if (!keyName)
|
|
459
|
+
continue;
|
|
460
|
+
if (keyName === "children") {
|
|
461
|
+
// children can be a single value or an array
|
|
462
|
+
if (t.isArrayExpression(valNode)) {
|
|
463
|
+
for (const el of valNode.elements) {
|
|
464
|
+
if (!el)
|
|
465
|
+
continue;
|
|
466
|
+
const ch = childToJsxChild(el);
|
|
467
|
+
if (ch)
|
|
468
|
+
children.push(ch);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
else {
|
|
472
|
+
const ch = childToJsxChild(valNode);
|
|
473
|
+
if (ch)
|
|
474
|
+
children.push(ch);
|
|
475
|
+
}
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
// Normal prop
|
|
479
|
+
const attrName = t.jsxIdentifier(keyName);
|
|
480
|
+
if (t.isJSXExpressionContainer(valNode) || t.isStringLiteral(valNode)) {
|
|
481
|
+
attrs.push(t.jsxAttribute(attrName, exprToJsxAttrValue(valNode)));
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
attrs.push(t.jsxAttribute(attrName, exprToJsxAttrValue(valNode)));
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
const selfClosing = children.length === 0;
|
|
489
|
+
const openingElement = t.jsxOpeningElement(jsxName, attrs, selfClosing);
|
|
490
|
+
const closingElement = selfClosing ? null : t.jsxClosingElement(jsxName);
|
|
491
|
+
return t.jsxElement(openingElement, closingElement, children, selfClosing);
|
|
492
|
+
}
|
|
493
|
+
/** Traverse statements and replace jsx()/jsxs() calls with JSXElement nodes. */
|
|
494
|
+
function recoverJSX(statements) {
|
|
495
|
+
const syntheticFile = t.file(t.program(statements, [], "module"));
|
|
496
|
+
traverse(syntheticFile, {
|
|
497
|
+
CallExpression(p) {
|
|
498
|
+
const jsxEl = tryConvertToJSX(p.node);
|
|
499
|
+
if (!jsxEl)
|
|
500
|
+
return;
|
|
501
|
+
p.replaceWith(jsxEl);
|
|
502
|
+
p.skip();
|
|
503
|
+
},
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
// ---------------------------------------------------------------------------
|
|
507
|
+
// Pass G — remove top-level Babel helpers and webpack internals
|
|
508
|
+
// ---------------------------------------------------------------------------
|
|
509
|
+
function isBabelArrayLikeToArrayHelper(stmt) {
|
|
510
|
+
if (!t.isFunctionDeclaration(stmt) || !stmt.id)
|
|
511
|
+
return false;
|
|
512
|
+
const body = stmt.body.body;
|
|
513
|
+
if (body.length < 2 || body.length > 4)
|
|
514
|
+
return false;
|
|
515
|
+
// Key signal: contains `for (var ... = Array(n); ...)` and a return of the array
|
|
516
|
+
const hasArrayOf = body.some((s) => {
|
|
517
|
+
if (!t.isForStatement(s))
|
|
518
|
+
return false;
|
|
519
|
+
const init = s.init;
|
|
520
|
+
if (!t.isVariableDeclaration(init))
|
|
521
|
+
return false;
|
|
522
|
+
return init.declarations.some((d) => d.init &&
|
|
523
|
+
t.isCallExpression(d.init) &&
|
|
524
|
+
t.isIdentifier(d.init.callee, { name: "Array" }));
|
|
525
|
+
});
|
|
526
|
+
return hasArrayOf;
|
|
527
|
+
}
|
|
528
|
+
// Detect `_typeof` — Babel's lazy-init typeof polyfill.
|
|
529
|
+
// Shape: 1-param function whose sole body statement is
|
|
530
|
+
// return ((fnName = <conditional>), fnName(arg))
|
|
531
|
+
function isBabelTypeofHelper(stmt) {
|
|
532
|
+
if (!t.isFunctionDeclaration(stmt) || !stmt.id || stmt.params.length !== 1)
|
|
533
|
+
return false;
|
|
534
|
+
const body = stmt.body.body;
|
|
535
|
+
if (body.length !== 1 || !t.isReturnStatement(body[0]))
|
|
536
|
+
return false;
|
|
537
|
+
const arg = body[0].argument;
|
|
538
|
+
if (!arg || !t.isSequenceExpression(arg))
|
|
539
|
+
return false;
|
|
540
|
+
const exprs = arg.expressions;
|
|
541
|
+
if (exprs.length !== 2)
|
|
542
|
+
return false;
|
|
543
|
+
const first = exprs[0];
|
|
544
|
+
return (t.isAssignmentExpression(first) &&
|
|
545
|
+
t.isIdentifier(first.left, { name: stmt.id.name }));
|
|
546
|
+
}
|
|
547
|
+
// Detect `_defineProperty` / `_toPropertyKey`+`_defineProperty` combos.
|
|
548
|
+
// Shape: 3-param function whose body contains an Object.defineProperty call with
|
|
549
|
+
// { value, enumerable, configurable, writable } descriptor.
|
|
550
|
+
function isBabelDefinePropertyHelper(stmt) {
|
|
551
|
+
if (!t.isFunctionDeclaration(stmt) || !stmt.id || stmt.params.length !== 3)
|
|
552
|
+
return false;
|
|
553
|
+
const bodyCode = stmt.body.body;
|
|
554
|
+
const hasDefProp = (node) => {
|
|
555
|
+
if (t.isCallExpression(node) &&
|
|
556
|
+
t.isMemberExpression(node.callee) &&
|
|
557
|
+
t.isIdentifier(node.callee.object, { name: "Object" }) &&
|
|
558
|
+
t.isIdentifier(node.callee.property, {
|
|
559
|
+
name: "defineProperty",
|
|
560
|
+
})) {
|
|
561
|
+
const args = node.arguments;
|
|
562
|
+
if (args.length >= 3 && t.isObjectExpression(args[2])) {
|
|
563
|
+
const props = args[2].properties;
|
|
564
|
+
const keys = props
|
|
565
|
+
.filter((p) => t.isObjectProperty(p) && t.isIdentifier(p.key))
|
|
566
|
+
.map((p) => p.key.name);
|
|
567
|
+
return keys.includes("enumerable") && keys.includes("configurable") && keys.includes("writable");
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return false;
|
|
571
|
+
};
|
|
572
|
+
// Walk statements for the defineProperty call (may be nested in return/conditional)
|
|
573
|
+
const walk = (node) => {
|
|
574
|
+
if (hasDefProp(node))
|
|
575
|
+
return true;
|
|
576
|
+
for (const key of Object.keys(node)) {
|
|
577
|
+
const child = node[key];
|
|
578
|
+
if (!child || typeof child !== "object")
|
|
579
|
+
continue;
|
|
580
|
+
if (Array.isArray(child)) {
|
|
581
|
+
if (child.some((c) => c && typeof c === "object" && "type" in c && walk(c)))
|
|
582
|
+
return true;
|
|
583
|
+
}
|
|
584
|
+
else if ("type" in child) {
|
|
585
|
+
if (walk(child))
|
|
586
|
+
return true;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return false;
|
|
590
|
+
};
|
|
591
|
+
return bodyCode.some((s) => walk(s));
|
|
592
|
+
}
|
|
593
|
+
// Detect `_objectSpreadPropsHelper` / `_objectKeys`.
|
|
594
|
+
// Shape: 2-param function whose first statement declares a variable via Object.keys(param0)
|
|
595
|
+
// and whose body references getOwnPropertySymbols.
|
|
596
|
+
function isBabelObjectSpreadHelper(stmt) {
|
|
597
|
+
if (!t.isFunctionDeclaration(stmt) || !stmt.id || stmt.params.length !== 2)
|
|
598
|
+
return false;
|
|
599
|
+
const body = stmt.body.body;
|
|
600
|
+
if (body.length < 2)
|
|
601
|
+
return false;
|
|
602
|
+
// First statement: var t = Object.keys(e)
|
|
603
|
+
const first = body[0];
|
|
604
|
+
if (!t.isVariableDeclaration(first))
|
|
605
|
+
return false;
|
|
606
|
+
const firstIsObjectKeys = first.declarations.some((d) => {
|
|
607
|
+
if (!d.init || !t.isCallExpression(d.init))
|
|
608
|
+
return false;
|
|
609
|
+
const callee = d.init.callee;
|
|
610
|
+
return (t.isMemberExpression(callee) &&
|
|
611
|
+
t.isIdentifier(callee.object, { name: "Object" }) &&
|
|
612
|
+
t.isIdentifier(callee.property, { name: "keys" }));
|
|
613
|
+
});
|
|
614
|
+
if (!firstIsObjectKeys)
|
|
615
|
+
return false;
|
|
616
|
+
// Body references getOwnPropertySymbols
|
|
617
|
+
const bodyStr = JSON.stringify(body);
|
|
618
|
+
return bodyStr.includes('"getOwnPropertySymbols"');
|
|
619
|
+
}
|
|
620
|
+
/** Remove `var x = {}` declarations where every declarator has an empty-object init.
|
|
621
|
+
* These are webpack module-cache variables. */
|
|
622
|
+
function dropEmptyObjectVars(stmts) {
|
|
623
|
+
return stmts.filter((stmt) => {
|
|
624
|
+
if (!t.isVariableDeclaration(stmt))
|
|
625
|
+
return true;
|
|
626
|
+
return !stmt.declarations.every((d) => d.init && t.isObjectExpression(d.init) && d.init.properties.length === 0);
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
// ---------------------------------------------------------------------------
|
|
630
|
+
// Pass H — prune unused named imports (e.g. jsx/jsxs after JSX recovery)
|
|
631
|
+
// ---------------------------------------------------------------------------
|
|
632
|
+
/** Collect all free identifier references in the given statements. */
|
|
633
|
+
function collectReferencedNames(stmts) {
|
|
634
|
+
const names = new Set();
|
|
635
|
+
const syntheticFile = t.file(t.program(stmts, [], "module"));
|
|
636
|
+
traverse(syntheticFile, {
|
|
637
|
+
Identifier(p) {
|
|
638
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
639
|
+
// Skip identifier nodes that are property names or binding declarations
|
|
640
|
+
if ((_a = p.parentPath) === null || _a === void 0 ? void 0 : _a.isImportSpecifier())
|
|
641
|
+
return;
|
|
642
|
+
if ((_b = p.parentPath) === null || _b === void 0 ? void 0 : _b.isImportDefaultSpecifier())
|
|
643
|
+
return;
|
|
644
|
+
if ((_c = p.parentPath) === null || _c === void 0 ? void 0 : _c.isImportNamespaceSpecifier())
|
|
645
|
+
return;
|
|
646
|
+
if (((_d = p.parentPath) === null || _d === void 0 ? void 0 : _d.isMemberExpression()) &&
|
|
647
|
+
!p.parent.computed &&
|
|
648
|
+
p.parentPath.get("property") === p)
|
|
649
|
+
return;
|
|
650
|
+
if (((_e = p.parentPath) === null || _e === void 0 ? void 0 : _e.isObjectProperty()) &&
|
|
651
|
+
!p.parent.computed &&
|
|
652
|
+
p.parentPath.get("key") === p)
|
|
653
|
+
return;
|
|
654
|
+
if ((_f = p.parentPath) === null || _f === void 0 ? void 0 : _f.isJSXAttribute())
|
|
655
|
+
return;
|
|
656
|
+
if (((_g = p.parentPath) === null || _g === void 0 ? void 0 : _g.isJSXOpeningElement()) || ((_h = p.parentPath) === null || _h === void 0 ? void 0 : _h.isJSXClosingElement()))
|
|
657
|
+
return;
|
|
658
|
+
names.add(p.node.name);
|
|
659
|
+
},
|
|
660
|
+
JSXIdentifier(p) {
|
|
661
|
+
// JSX element names (e.g. <Fragment>) are JSXIdentifier nodes, not Identifier.
|
|
662
|
+
// Add them so their import specifiers survive Pass H pruning.
|
|
663
|
+
names.add(p.node.name);
|
|
664
|
+
},
|
|
665
|
+
});
|
|
666
|
+
return names;
|
|
667
|
+
}
|
|
668
|
+
/** Remove named import specifiers whose local name is not referenced in `bodyStmts`. */
|
|
669
|
+
function pruneUnusedNamedImports(importStmts, bodyStmts) {
|
|
670
|
+
const refs = collectReferencedNames(bodyStmts);
|
|
671
|
+
return importStmts
|
|
672
|
+
.map((stmt) => {
|
|
673
|
+
if (!t.isImportDeclaration(stmt))
|
|
674
|
+
return stmt;
|
|
675
|
+
const prunedSpecifiers = stmt.specifiers.filter((spec) => {
|
|
676
|
+
if (t.isImportNamespaceSpecifier(spec))
|
|
677
|
+
return true; // always keep namespace imports
|
|
678
|
+
if (t.isImportDefaultSpecifier(spec))
|
|
679
|
+
return true;
|
|
680
|
+
if (t.isImportSpecifier(spec)) {
|
|
681
|
+
const localName = t.isIdentifier(spec.local) ? spec.local.name : null;
|
|
682
|
+
return localName ? refs.has(localName) : true;
|
|
683
|
+
}
|
|
684
|
+
return true;
|
|
685
|
+
});
|
|
686
|
+
if (prunedSpecifiers.length === 0)
|
|
687
|
+
return null; // drop empty imports
|
|
688
|
+
if (prunedSpecifiers.length === stmt.specifiers.length)
|
|
689
|
+
return stmt; // unchanged
|
|
690
|
+
return t.importDeclaration(prunedSpecifiers, stmt.source);
|
|
691
|
+
})
|
|
692
|
+
.filter(Boolean);
|
|
693
|
+
}
|
|
694
|
+
// ---------------------------------------------------------------------------
|
|
695
|
+
// Main export
|
|
696
|
+
// ---------------------------------------------------------------------------
|
|
697
|
+
/**
|
|
698
|
+
* Transforms the non-module-map IIFE statements that become `index.js`.
|
|
699
|
+
*
|
|
700
|
+
* Passes:
|
|
701
|
+
* A – Remove webpack require helper function.
|
|
702
|
+
* B – Hoist top-level `var x = requireFn(N)` to static `import * as x from "./N.js"`.
|
|
703
|
+
* C – Replace remaining inline `requireFn(N)` calls.
|
|
704
|
+
* D – Rewrite library module calls using identity map (e.g. `l.H(...)` → `createRoot(...)`)
|
|
705
|
+
* and replace namespace imports with named imports from proper library paths.
|
|
706
|
+
* E – Collapse Babel slicedToArray expansions to `const [a, b] = expr`.
|
|
707
|
+
* F – Recover JSX from `jsx(tag, props)` / `jsxs(tag, props)` calls.
|
|
708
|
+
* G – Remove Babel array-helper functions (e.g. `arrayLikeToArray`).
|
|
709
|
+
*/
|
|
710
|
+
export const transformIndexStatements = (statements, libModuleMap) => {
|
|
711
|
+
// Pass A — detect and remove webpack require helper(s).
|
|
712
|
+
const requireFnNames = new Set();
|
|
713
|
+
const afterA = [];
|
|
714
|
+
for (const stmt of statements) {
|
|
715
|
+
if (t.isFunctionDeclaration(stmt) && stmt.id && isWebpackRequireHelper(stmt)) {
|
|
716
|
+
requireFnNames.add(stmt.id.name);
|
|
717
|
+
continue; // drop
|
|
718
|
+
}
|
|
719
|
+
afterA.push(stmt);
|
|
720
|
+
}
|
|
721
|
+
if (requireFnNames.size === 0)
|
|
722
|
+
return statements; // nothing to do
|
|
723
|
+
const hoistedImports = new Map();
|
|
724
|
+
const importNameByNumId = new Map();
|
|
725
|
+
// Pass B — hoist top-level `var x = requireFn(N)` to static imports.
|
|
726
|
+
// Also build varName → moduleId so Pass D can look up library identity.
|
|
727
|
+
const varToModuleId = new Map(); // localVarName → numericModuleId
|
|
728
|
+
const afterB = [];
|
|
729
|
+
for (const stmt of afterA) {
|
|
730
|
+
if (!t.isVariableDeclaration(stmt)) {
|
|
731
|
+
afterB.push(stmt);
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
const kept = [];
|
|
735
|
+
for (const decl of stmt.declarations) {
|
|
736
|
+
if (!t.isIdentifier(decl.id) || !decl.init) {
|
|
737
|
+
kept.push(decl);
|
|
738
|
+
continue;
|
|
739
|
+
}
|
|
740
|
+
let matched = false;
|
|
741
|
+
for (const fnName of requireFnNames) {
|
|
742
|
+
const numId = tryExtractRequireCall(decl.init, fnName);
|
|
743
|
+
if (numId === null)
|
|
744
|
+
continue;
|
|
745
|
+
const spec = `./${numId}.js`;
|
|
746
|
+
const localName = decl.id.name;
|
|
747
|
+
if (!hoistedImports.has(spec)) {
|
|
748
|
+
hoistedImports.set(spec, localName);
|
|
749
|
+
importNameByNumId.set(numId, localName);
|
|
750
|
+
}
|
|
751
|
+
varToModuleId.set(localName, numId);
|
|
752
|
+
matched = true;
|
|
753
|
+
break;
|
|
754
|
+
}
|
|
755
|
+
if (!matched)
|
|
756
|
+
kept.push(decl);
|
|
757
|
+
}
|
|
758
|
+
if (kept.length > 0)
|
|
759
|
+
afterB.push(t.variableDeclaration(stmt.kind, kept));
|
|
760
|
+
}
|
|
761
|
+
// Pass C — replace remaining inline requireFn(N) calls recursively.
|
|
762
|
+
const syntheticFile = t.file(t.program(afterB, [], "module"));
|
|
763
|
+
traverse(syntheticFile, {
|
|
764
|
+
CallExpression(p) {
|
|
765
|
+
for (const fnName of requireFnNames) {
|
|
766
|
+
const numId = tryExtractRequireCall(p.node, fnName);
|
|
767
|
+
if (numId === null)
|
|
768
|
+
continue;
|
|
769
|
+
let name = importNameByNumId.get(numId);
|
|
770
|
+
if (!name) {
|
|
771
|
+
name = `_jsr_module_${numId}`;
|
|
772
|
+
hoistedImports.set(`./${numId}.js`, name);
|
|
773
|
+
importNameByNumId.set(numId, name);
|
|
774
|
+
}
|
|
775
|
+
p.replaceWith(t.identifier(name));
|
|
776
|
+
p.skip();
|
|
777
|
+
break;
|
|
778
|
+
}
|
|
779
|
+
},
|
|
780
|
+
});
|
|
781
|
+
// Pass D — library-aware import rewriting (only when libModuleMap is supplied).
|
|
782
|
+
// Build varName → LibraryModuleInfo, then rewrite calls and imports.
|
|
783
|
+
const finalImportStmts = [];
|
|
784
|
+
if (libModuleMap && libModuleMap.size > 0) {
|
|
785
|
+
// Map local var names to their library identity
|
|
786
|
+
const varToLib = new Map();
|
|
787
|
+
for (const [varName, numId] of varToModuleId) {
|
|
788
|
+
const info = libModuleMap.get(String(numId));
|
|
789
|
+
if (info)
|
|
790
|
+
varToLib.set(varName, info);
|
|
791
|
+
}
|
|
792
|
+
if (varToLib.size > 0) {
|
|
793
|
+
// Rewrite call-sites; collect which named exports are actually used
|
|
794
|
+
const usedExports = rewriteLibraryCalls(afterB, varToLib);
|
|
795
|
+
// Build import declarations for library modules (named imports).
|
|
796
|
+
// handledSpecs guards only the import-declaration emit (avoid duplicates when
|
|
797
|
+
// two local vars map to the same library source). The namespace-import deletion
|
|
798
|
+
// must happen for EVERY var that maps to a library, regardless of dedup.
|
|
799
|
+
const handledSpecs = new Set();
|
|
800
|
+
for (const [varName, info] of varToLib) {
|
|
801
|
+
const src = librarySource(info.type);
|
|
802
|
+
if (!src)
|
|
803
|
+
continue;
|
|
804
|
+
if (!handledSpecs.has(src)) {
|
|
805
|
+
handledSpecs.add(src);
|
|
806
|
+
const used = usedExports.get(src);
|
|
807
|
+
if (used && used.size > 0) {
|
|
808
|
+
const specifiers = [...used]
|
|
809
|
+
.sort()
|
|
810
|
+
.map((name) => t.importSpecifier(t.identifier(name), t.identifier(name)));
|
|
811
|
+
finalImportStmts.push(t.importDeclaration(specifiers, t.stringLiteral(src)));
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
// Always remove the namespace import for this var, even if src was already handled.
|
|
815
|
+
const numId = varToModuleId.get(varName);
|
|
816
|
+
if (numId !== undefined)
|
|
817
|
+
hoistedImports.delete(`./${numId}.js`);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
// Remaining (non-library) namespace imports
|
|
822
|
+
for (const [spec, name] of hoistedImports) {
|
|
823
|
+
finalImportStmts.push(t.importDeclaration([t.importNamespaceSpecifier(t.identifier(name))], t.stringLiteral(spec)));
|
|
824
|
+
}
|
|
825
|
+
// Pass E — collapse Babel slicedToArray expansions.
|
|
826
|
+
const afterE = collapseSlicedToArrayDeep(afterB);
|
|
827
|
+
// Pass F — JSX recovery.
|
|
828
|
+
recoverJSX(afterE);
|
|
829
|
+
// Pass G — remove top-level Babel helper functions and webpack module-cache vars.
|
|
830
|
+
const afterG = dropEmptyObjectVars(afterE.filter((stmt) => !isBabelArrayLikeToArrayHelper(stmt) &&
|
|
831
|
+
!isBabelTypeofHelper(stmt) &&
|
|
832
|
+
!isBabelDefinePropertyHelper(stmt) &&
|
|
833
|
+
!isBabelObjectSpreadHelper(stmt)));
|
|
834
|
+
// Pass H — prune named imports whose local name is no longer referenced
|
|
835
|
+
// (e.g. jsx/jsxs after JSX recovery, namespace imports that were cleared).
|
|
836
|
+
const prunedImports = pruneUnusedNamedImports(finalImportStmts, afterG);
|
|
837
|
+
return [...prunedImports, ...afterG];
|
|
838
|
+
};
|
|
839
|
+
//# sourceMappingURL=transform.js.map
|