@shriyanss/js-recon 1.4.1-alpha.2 → 1.4.1-alpha.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/CHANGELOG.md +41 -0
- package/CLAUDE.md +39 -2
- package/README.md +2 -0
- package/build/analyze/engine/index.js.map +1 -1
- package/build/analyze/helpers/schemas.js +1 -1
- package/build/analyze/helpers/schemas.js.map +1 -1
- package/build/analyze/index.js +1 -0
- package/build/analyze/index.js.map +1 -1
- package/build/globalConfig.js +2 -2
- package/build/globalConfig.js.map +1 -1
- package/build/index.js +73 -3
- package/build/index.js.map +1 -1
- package/build/lazyLoad/downloadQueue.js +1 -1
- package/build/lazyLoad/downloadQueue.js.map +1 -1
- package/build/lazyLoad/index.js +25 -0
- package/build/lazyLoad/index.js.map +1 -1
- package/build/lazyLoad/methodFilter.js +1 -0
- package/build/lazyLoad/methodFilter.js.map +1 -1
- package/build/lazyLoad/nuxt_js/nuxt_getBuildsManifest.js +64 -0
- package/build/lazyLoad/nuxt_js/nuxt_getBuildsManifest.js.map +1 -0
- package/build/lazyLoad/react/react_followImports.js +8 -2
- package/build/lazyLoad/react/react_followImports.js.map +1 -1
- package/build/lazyLoad/svelte/svelte_getFromPageSource.js +16 -0
- package/build/lazyLoad/svelte/svelte_getFromPageSource.js.map +1 -1
- package/build/lazyLoad/svelte/svelte_getVersionJson.js +40 -0
- package/build/lazyLoad/svelte/svelte_getVersionJson.js.map +1 -0
- package/build/map/angular_js/getAngularConnections.js +88 -0
- package/build/map/angular_js/getAngularConnections.js.map +1 -0
- package/build/map/angular_js/interactive.js +4 -0
- package/build/map/angular_js/interactive.js.map +1 -0
- package/build/map/index.js +38 -0
- package/build/map/index.js.map +1 -1
- package/build/refactor/index.js +478 -12
- package/build/refactor/index.js.map +1 -1
- package/build/refactor/react/helpers.js +13 -0
- package/build/refactor/react/helpers.js.map +1 -1
- package/build/refactor/react/index.js +149 -41
- package/build/refactor/react/index.js.map +1 -1
- package/build/refactor/react/library-classify.js +232 -1
- package/build/refactor/react/library-classify.js.map +1 -1
- package/build/refactor/react/transform.js +1046 -34
- package/build/refactor/react/transform.js.map +1 -1
- package/build/refactor/react-vite/index.js +618 -0
- package/build/refactor/react-vite/index.js.map +1 -0
- package/build/refactor/react-vite/vendor-analyze.js +336 -0
- package/build/refactor/react-vite/vendor-analyze.js.map +1 -0
- package/build/refactor/remote/cache.js +211 -0
- package/build/refactor/remote/cache.js.map +1 -0
- package/build/refactor/remote/config.js +47 -0
- package/build/refactor/remote/config.js.map +1 -0
- package/build/refactor/remote/hf-client.js +137 -0
- package/build/refactor/remote/hf-client.js.map +1 -0
- package/build/run/index.js +116 -11
- package/build/run/index.js.map +1 -1
- package/build/utility/banner.js +68 -0
- package/build/utility/banner.js.map +1 -0
- package/package.json +2 -1
|
@@ -2,7 +2,7 @@ import _traverse from "@babel/traverse";
|
|
|
2
2
|
import _generator from "@babel/generator";
|
|
3
3
|
import * as t from "@babel/types";
|
|
4
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";
|
|
5
|
+
import { REACT_CANONICAL, JSX_RUNTIME_CANONICAL, REACT_DOM_CLIENT_CANONICAL, REACT_ROUTER_DOM_CANONICAL, librarySource, } from "./library-classify.js";
|
|
6
6
|
const traverse = _traverse.default;
|
|
7
7
|
const generate = _generator.default;
|
|
8
8
|
/**
|
|
@@ -55,6 +55,92 @@ export const transformModule = (mod) => {
|
|
|
55
55
|
}
|
|
56
56
|
body.body = next;
|
|
57
57
|
}
|
|
58
|
+
// Pass 1.5 — handle webpack ES module registration patterns:
|
|
59
|
+
// `<requireParam>.r(<exportsParam>)` → drop (marks module as ES module, not needed in ESM)
|
|
60
|
+
// `<requireParam>.d(<exportsParam>, {name: ()=>binding, ...})` → named exports
|
|
61
|
+
// This handles the common lazy-chunk pattern where webpack emits r.r(t), r.d(t, {default:()=>X})
|
|
62
|
+
// at the top of the module body instead of the older t.exports = X form.
|
|
63
|
+
if (requireParam && exportsParam) {
|
|
64
|
+
const processWebpackRegExpr = (expr) => {
|
|
65
|
+
if (!t.isCallExpression(expr))
|
|
66
|
+
return null;
|
|
67
|
+
const callee = expr.callee;
|
|
68
|
+
if (!t.isMemberExpression(callee) || callee.computed)
|
|
69
|
+
return null;
|
|
70
|
+
if (!t.isIdentifier(callee.object, { name: requireParam }))
|
|
71
|
+
return null;
|
|
72
|
+
const method = t.isIdentifier(callee.property) ? callee.property.name : null;
|
|
73
|
+
if (!method)
|
|
74
|
+
return null;
|
|
75
|
+
// r.r(t) → drop
|
|
76
|
+
if (method === "r" &&
|
|
77
|
+
expr.arguments.length === 1 &&
|
|
78
|
+
t.isIdentifier(expr.arguments[0], { name: exportsParam })) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
// r.d(t, {name: ()=>binding, ...}) → named export statements
|
|
82
|
+
if (method === "d" &&
|
|
83
|
+
expr.arguments.length >= 2 &&
|
|
84
|
+
t.isIdentifier(expr.arguments[0], { name: exportsParam }) &&
|
|
85
|
+
t.isObjectExpression(expr.arguments[1])) {
|
|
86
|
+
const stmts = [];
|
|
87
|
+
for (const prop of expr.arguments[1].properties) {
|
|
88
|
+
if (!t.isObjectProperty(prop) || prop.computed)
|
|
89
|
+
continue;
|
|
90
|
+
const key = prop.key;
|
|
91
|
+
const val = prop.value;
|
|
92
|
+
let propName = null;
|
|
93
|
+
if (t.isIdentifier(key))
|
|
94
|
+
propName = key.name;
|
|
95
|
+
else if (t.isStringLiteral(key))
|
|
96
|
+
propName = key.value;
|
|
97
|
+
if (!propName)
|
|
98
|
+
continue;
|
|
99
|
+
// val is typically `() => binding` — unwrap the arrow function body
|
|
100
|
+
const exportedVal = t.isArrowFunctionExpression(val) && !t.isBlockStatement(val.body)
|
|
101
|
+
? val.body
|
|
102
|
+
: val;
|
|
103
|
+
stmts.push(makeNamedExportStatement(propName, exportedVal));
|
|
104
|
+
}
|
|
105
|
+
return stmts;
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
};
|
|
109
|
+
const next = [];
|
|
110
|
+
for (const stmt of body.body) {
|
|
111
|
+
if (!t.isExpressionStatement(stmt)) {
|
|
112
|
+
next.push(stmt);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const expr = stmt.expression;
|
|
116
|
+
if (t.isSequenceExpression(expr)) {
|
|
117
|
+
let hadMatch = false;
|
|
118
|
+
const splitted = [];
|
|
119
|
+
for (const sub of expr.expressions) {
|
|
120
|
+
const handled = processWebpackRegExpr(sub);
|
|
121
|
+
if (handled !== null) {
|
|
122
|
+
hadMatch = true;
|
|
123
|
+
splitted.push(...handled);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
splitted.push(t.expressionStatement(sub));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (hadMatch) {
|
|
130
|
+
next.push(...splitted);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const handled = processWebpackRegExpr(expr);
|
|
135
|
+
if (handled !== null) {
|
|
136
|
+
next.push(...handled);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
next.push(stmt);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
body.body = next;
|
|
143
|
+
}
|
|
58
144
|
// Pass 2 — handle `<exportsParam>.<propName> = <rhs>` at the top level only.
|
|
59
145
|
if (exportsParam) {
|
|
60
146
|
const next = [];
|
|
@@ -142,6 +228,58 @@ export const transformModule = (mod) => {
|
|
|
142
228
|
},
|
|
143
229
|
});
|
|
144
230
|
}
|
|
231
|
+
// Pass 4.5 — convert webpack async chunk loading to a true dynamic import:
|
|
232
|
+
// requireParam.e(N).then(requireParam.bind(requireParam, N)) → import('./N.js')
|
|
233
|
+
// This pattern is emitted by webpack for React.lazy() code-split points. After the
|
|
234
|
+
// outer module wrapper is stripped, `requireParam` is no longer in scope, so the
|
|
235
|
+
// expression must be replaced before Step 5 removes it.
|
|
236
|
+
if (requireParam) {
|
|
237
|
+
fnPath.traverse({
|
|
238
|
+
CallExpression(callPath) {
|
|
239
|
+
const node = callPath.node;
|
|
240
|
+
// Outer call must be X.then(bindExpr) — non-computed member
|
|
241
|
+
if (!t.isMemberExpression(node.callee) || node.callee.computed)
|
|
242
|
+
return;
|
|
243
|
+
if (!t.isIdentifier(node.callee.property, { name: "then" }))
|
|
244
|
+
return;
|
|
245
|
+
// X must be requireParam.e(numId)
|
|
246
|
+
const eCallNode = node.callee.object;
|
|
247
|
+
if (!t.isCallExpression(eCallNode))
|
|
248
|
+
return;
|
|
249
|
+
const eCallee = eCallNode.callee;
|
|
250
|
+
if (!t.isMemberExpression(eCallee) || eCallee.computed)
|
|
251
|
+
return;
|
|
252
|
+
if (!t.isIdentifier(eCallee.object, { name: requireParam }))
|
|
253
|
+
return;
|
|
254
|
+
if (!t.isIdentifier(eCallee.property, { name: "e" }))
|
|
255
|
+
return;
|
|
256
|
+
const eArgs = eCallNode.arguments;
|
|
257
|
+
if (eArgs.length !== 1 || !t.isNumericLiteral(eArgs[0]))
|
|
258
|
+
return;
|
|
259
|
+
const numId = eArgs[0].value;
|
|
260
|
+
// The .then() argument must be requireParam.bind(requireParam, numId)
|
|
261
|
+
if (node.arguments.length !== 1 || !t.isCallExpression(node.arguments[0]))
|
|
262
|
+
return;
|
|
263
|
+
const bindNode = node.arguments[0];
|
|
264
|
+
if (!t.isMemberExpression(bindNode.callee) || bindNode.callee.computed)
|
|
265
|
+
return;
|
|
266
|
+
if (!t.isIdentifier(bindNode.callee.object, { name: requireParam }))
|
|
267
|
+
return;
|
|
268
|
+
if (!t.isIdentifier(bindNode.callee.property, { name: "bind" }))
|
|
269
|
+
return;
|
|
270
|
+
const bindArgs = bindNode.arguments;
|
|
271
|
+
if (bindArgs.length !== 2)
|
|
272
|
+
return;
|
|
273
|
+
if (!t.isIdentifier(bindArgs[0], { name: requireParam }))
|
|
274
|
+
return;
|
|
275
|
+
if (!t.isNumericLiteral(bindArgs[1]) || bindArgs[1].value !== numId)
|
|
276
|
+
return;
|
|
277
|
+
// Replace with import('./N.js') — a dynamic import expression
|
|
278
|
+
callPath.replaceWith(t.callExpression(t.import(), [t.stringLiteral(`./${numId}.js`)]));
|
|
279
|
+
callPath.skip();
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
}
|
|
145
283
|
// Step 5 — strip outer function wrapper, prepend hoisted static imports.
|
|
146
284
|
const importStmts = [];
|
|
147
285
|
for (const [spec, name] of hoistedImports) {
|
|
@@ -194,6 +332,8 @@ function resolveLibraryProp(varName, prop, varToLib) {
|
|
|
194
332
|
return JSX_RUNTIME_CANONICAL.has(name) || name === "Fragment";
|
|
195
333
|
if (info.type === "react-dom-client")
|
|
196
334
|
return REACT_DOM_CLIENT_CANONICAL.has(name);
|
|
335
|
+
if (info.type === "react-router-dom")
|
|
336
|
+
return REACT_ROUTER_DOM_CANONICAL.has(name);
|
|
197
337
|
return false;
|
|
198
338
|
};
|
|
199
339
|
// Check exportMap: only trust the value if it IS a canonical name.
|
|
@@ -281,6 +421,36 @@ function rewriteLibraryCalls(statements, varToLib) {
|
|
|
281
421
|
p.replaceWith(t.identifier(resolved.canonical));
|
|
282
422
|
p.skip();
|
|
283
423
|
},
|
|
424
|
+
// Rewrite JSX member expressions: <ns.Component> → <CanonicalName>
|
|
425
|
+
// These are JSXMemberExpression nodes (not MemberExpression), so the visitor above misses them.
|
|
426
|
+
JSXOpeningElement(p) {
|
|
427
|
+
const elem = p.node;
|
|
428
|
+
if (!t.isJSXMemberExpression(elem.name))
|
|
429
|
+
return;
|
|
430
|
+
const obj = elem.name.object;
|
|
431
|
+
const prop = elem.name.property;
|
|
432
|
+
if (!t.isJSXIdentifier(obj) || !t.isJSXIdentifier(prop))
|
|
433
|
+
return;
|
|
434
|
+
const resolved = resolveLibraryProp(obj.name, prop.name, varToLib);
|
|
435
|
+
if (!resolved)
|
|
436
|
+
return;
|
|
437
|
+
record(resolved.libType, resolved.canonical);
|
|
438
|
+
elem.name = t.jsxIdentifier(resolved.canonical);
|
|
439
|
+
},
|
|
440
|
+
JSXClosingElement(p) {
|
|
441
|
+
const elem = p.node;
|
|
442
|
+
if (!t.isJSXMemberExpression(elem.name))
|
|
443
|
+
return;
|
|
444
|
+
const obj = elem.name.object;
|
|
445
|
+
const prop = elem.name.property;
|
|
446
|
+
if (!t.isJSXIdentifier(obj) || !t.isJSXIdentifier(prop))
|
|
447
|
+
return;
|
|
448
|
+
const resolved = resolveLibraryProp(obj.name, prop.name, varToLib);
|
|
449
|
+
if (!resolved)
|
|
450
|
+
return;
|
|
451
|
+
// Don't record() again — already recorded from JSXOpeningElement.
|
|
452
|
+
elem.name = t.jsxIdentifier(resolved.canonical);
|
|
453
|
+
},
|
|
284
454
|
});
|
|
285
455
|
return usedExports;
|
|
286
456
|
}
|
|
@@ -384,11 +554,174 @@ function collapseSlicedToArray(statements) {
|
|
|
384
554
|
}
|
|
385
555
|
return out;
|
|
386
556
|
}
|
|
557
|
+
// ---------------------------------------------------------------------------
|
|
558
|
+
// Pass E.2 — collapse named slicedToArray helper calls
|
|
559
|
+
// ---------------------------------------------------------------------------
|
|
560
|
+
/**
|
|
561
|
+
* Detects a named slicedToArray helper function by the TypeError throw at the
|
|
562
|
+
* end of its body. Shape: function(e, t) { return ... || (() => { throw new TypeError("...non-iterable...") })() }
|
|
563
|
+
*/
|
|
564
|
+
function isNamedSlicedToArrayHelper(stmt) {
|
|
565
|
+
if (!t.isFunctionDeclaration(stmt) || !stmt.id || stmt.params.length !== 2)
|
|
566
|
+
return null;
|
|
567
|
+
// Direct name match: _slicedToArray is always a Babel array-destructure helper.
|
|
568
|
+
if (stmt.id.name === "_slicedToArray" || stmt.id.name === "slicedToArray")
|
|
569
|
+
return stmt.id.name;
|
|
570
|
+
const body = stmt.body.body;
|
|
571
|
+
if (body.length === 0)
|
|
572
|
+
return null;
|
|
573
|
+
const last = body[body.length - 1];
|
|
574
|
+
if (!t.isReturnStatement(last) || !last.argument)
|
|
575
|
+
return null;
|
|
576
|
+
// The return value must (somewhere in its || chain) throw new TypeError
|
|
577
|
+
const hasTypeError = (node) => {
|
|
578
|
+
if (t.isCallExpression(node) &&
|
|
579
|
+
(t.isFunctionExpression(node.callee) || t.isArrowFunctionExpression(node.callee))) {
|
|
580
|
+
const innerBody = node.callee.body;
|
|
581
|
+
if (t.isBlockStatement(innerBody)) {
|
|
582
|
+
return innerBody.body.some((s) => t.isThrowStatement(s) &&
|
|
583
|
+
t.isNewExpression(s.argument) &&
|
|
584
|
+
t.isIdentifier(s.argument.callee, { name: "TypeError" }) &&
|
|
585
|
+
s.argument.arguments.some((a) => t.isStringLiteral(a) && a.value.includes("non-iterable")));
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
if (t.isLogicalExpression(node) && node.operator === "||")
|
|
589
|
+
return hasTypeError(node.right);
|
|
590
|
+
return false;
|
|
591
|
+
};
|
|
592
|
+
return hasTypeError(last.argument) ? stmt.id.name : null;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Collapses `const temp = helper(expr, n), a = temp[0], b = temp[1]` into
|
|
596
|
+
* `const [a, b] = expr` for a given list of statements, knowing which function
|
|
597
|
+
* names are slicedToArray helpers (passed in as `helperNames`).
|
|
598
|
+
*/
|
|
599
|
+
function collapseSlicedToArrayCalls(stmts, helperNames) {
|
|
600
|
+
if (helperNames.size === 0)
|
|
601
|
+
return stmts;
|
|
602
|
+
const out = [];
|
|
603
|
+
for (const stmt of stmts) {
|
|
604
|
+
if (!t.isVariableDeclaration(stmt)) {
|
|
605
|
+
out.push(stmt);
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
const decls = stmt.declarations;
|
|
609
|
+
const newDecls = [];
|
|
610
|
+
let i = 0;
|
|
611
|
+
while (i < decls.length) {
|
|
612
|
+
const d = decls[i];
|
|
613
|
+
if (!t.isIdentifier(d.id) || !d.init || !t.isCallExpression(d.init)) {
|
|
614
|
+
newDecls.push(d);
|
|
615
|
+
i++;
|
|
616
|
+
continue;
|
|
617
|
+
}
|
|
618
|
+
const callExpr = d.init;
|
|
619
|
+
const callee = callExpr.callee;
|
|
620
|
+
if (!t.isIdentifier(callee) || !helperNames.has(callee.name)) {
|
|
621
|
+
newDecls.push(d);
|
|
622
|
+
i++;
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
const tempName = d.id.name;
|
|
626
|
+
let actualExpr = callExpr.arguments[0];
|
|
627
|
+
// If actualExpr references the immediately preceding declarator, inline its init
|
|
628
|
+
// so we get `const [a, b] = actualCall` instead of `const _x = actualCall, [a, b] = _x`.
|
|
629
|
+
if (t.isIdentifier(actualExpr) &&
|
|
630
|
+
newDecls.length > 0 &&
|
|
631
|
+
t.isIdentifier(newDecls[newDecls.length - 1].id) &&
|
|
632
|
+
newDecls[newDecls.length - 1].id.name === actualExpr.name &&
|
|
633
|
+
newDecls[newDecls.length - 1].init != null) {
|
|
634
|
+
actualExpr = newDecls[newDecls.length - 1].init;
|
|
635
|
+
newDecls.pop(); // remove the now-inlined declarator
|
|
636
|
+
}
|
|
637
|
+
const targets = [];
|
|
638
|
+
let j = i + 1;
|
|
639
|
+
while (j < decls.length) {
|
|
640
|
+
const nd = decls[j];
|
|
641
|
+
if (!t.isIdentifier(nd.id) ||
|
|
642
|
+
!nd.init ||
|
|
643
|
+
!t.isMemberExpression(nd.init) ||
|
|
644
|
+
!nd.init.computed ||
|
|
645
|
+
!t.isIdentifier(nd.init.object, { name: tempName }) ||
|
|
646
|
+
!t.isNumericLiteral(nd.init.property))
|
|
647
|
+
break;
|
|
648
|
+
targets.push({
|
|
649
|
+
id: nd.id,
|
|
650
|
+
index: nd.init.property.value,
|
|
651
|
+
});
|
|
652
|
+
j++;
|
|
653
|
+
}
|
|
654
|
+
if (targets.length > 0) {
|
|
655
|
+
const sorted = [...targets].sort((a, b) => a.index - b.index);
|
|
656
|
+
const pattern = t.arrayPattern(sorted.map((tgt) => tgt.id));
|
|
657
|
+
newDecls.push(t.variableDeclarator(pattern, actualExpr));
|
|
658
|
+
i = j;
|
|
659
|
+
}
|
|
660
|
+
else {
|
|
661
|
+
newDecls.push(d);
|
|
662
|
+
i++;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
if (newDecls.length > 0)
|
|
666
|
+
out.push(t.variableDeclaration(stmt.kind, newDecls));
|
|
667
|
+
}
|
|
668
|
+
return out;
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Given a list of statements, finds any named slicedToArray helpers, then:
|
|
672
|
+
* 1. Collapses `const temp = helper(expr, n), a = temp[0], b = temp[1]` → `const [a, b] = expr`
|
|
673
|
+
* 2. Rewrites inline `helper(expr, n)[k]` → `expr[k]` (single-element extraction with chained access)
|
|
674
|
+
* 3. Removes the helper declarations once they are no longer referenced.
|
|
675
|
+
*/
|
|
676
|
+
function collapseNamedSlicedToArray(statements) {
|
|
677
|
+
const helperNames = new Set();
|
|
678
|
+
for (const stmt of statements) {
|
|
679
|
+
const name = isNamedSlicedToArrayHelper(stmt);
|
|
680
|
+
if (name)
|
|
681
|
+
helperNames.add(name);
|
|
682
|
+
}
|
|
683
|
+
if (helperNames.size === 0)
|
|
684
|
+
return statements;
|
|
685
|
+
// Remove helper declarations and collapse top-level declarator-sequence usages
|
|
686
|
+
const withoutHelpers = statements.filter((s) => !(t.isFunctionDeclaration(s) && s.id && helperNames.has(s.id.name)));
|
|
687
|
+
const topCollapsed = collapseSlicedToArrayCalls(withoutHelpers, helperNames);
|
|
688
|
+
// Recurse into nested function bodies via traverse — collapse declarator sequences
|
|
689
|
+
const syntheticFile = t.file(t.program(topCollapsed, [], "module"));
|
|
690
|
+
traverse(syntheticFile, {
|
|
691
|
+
BlockStatement(p) {
|
|
692
|
+
const collapsed = collapseSlicedToArrayCalls(p.node.body, helperNames);
|
|
693
|
+
if (collapsed !== p.node.body)
|
|
694
|
+
p.node.body = collapsed;
|
|
695
|
+
},
|
|
696
|
+
// Rewrite inline `helper(expr, n)[k]` → `expr[k]`
|
|
697
|
+
MemberExpression(p) {
|
|
698
|
+
const node = p.node;
|
|
699
|
+
if (!node.computed)
|
|
700
|
+
return;
|
|
701
|
+
if (!t.isNumericLiteral(node.property))
|
|
702
|
+
return;
|
|
703
|
+
if (!t.isCallExpression(node.object))
|
|
704
|
+
return;
|
|
705
|
+
const call = node.object;
|
|
706
|
+
if (!t.isIdentifier(call.callee) || !helperNames.has(call.callee.name))
|
|
707
|
+
return;
|
|
708
|
+
const actualExpr = call.arguments[0];
|
|
709
|
+
if (!actualExpr)
|
|
710
|
+
return;
|
|
711
|
+
p.replaceWith(t.memberExpression(actualExpr, node.property, true));
|
|
712
|
+
p.skip();
|
|
713
|
+
},
|
|
714
|
+
});
|
|
715
|
+
return topCollapsed;
|
|
716
|
+
}
|
|
387
717
|
/** Traverse all function bodies in a statement list and collapse slicedToArray inside them. */
|
|
388
718
|
function collapseSlicedToArrayDeep(statements) {
|
|
389
|
-
// First collapse
|
|
390
|
-
|
|
391
|
-
|
|
719
|
+
// First collapse named helpers (module-level factored-out slicedToArray function).
|
|
720
|
+
// This also recurses into all nested function bodies via traverse internally.
|
|
721
|
+
const afterNamed = collapseNamedSlicedToArray(statements);
|
|
722
|
+
// Collapse the inline expansion pattern at top level
|
|
723
|
+
const top = collapseSlicedToArray(afterNamed);
|
|
724
|
+
// Then recurse into function bodies via a traverse for the inline pattern
|
|
392
725
|
const syntheticFile = t.file(t.program(top, [], "module"));
|
|
393
726
|
traverse(syntheticFile, {
|
|
394
727
|
BlockStatement(p) {
|
|
@@ -404,8 +737,15 @@ function collapseSlicedToArrayDeep(statements) {
|
|
|
404
737
|
// Pass F — JSX recovery
|
|
405
738
|
// ---------------------------------------------------------------------------
|
|
406
739
|
function exprToJsxName(expr) {
|
|
740
|
+
var _a;
|
|
407
741
|
if (t.isStringLiteral(expr))
|
|
408
742
|
return t.jsxIdentifier(expr.value);
|
|
743
|
+
// Handle template literals with no expressions: `div` → "div"
|
|
744
|
+
if (t.isTemplateLiteral(expr) && expr.expressions.length === 0 && expr.quasis.length === 1) {
|
|
745
|
+
const raw = (_a = expr.quasis[0].value.cooked) !== null && _a !== void 0 ? _a : expr.quasis[0].value.raw;
|
|
746
|
+
if (raw)
|
|
747
|
+
return t.jsxIdentifier(raw);
|
|
748
|
+
}
|
|
409
749
|
if (t.isIdentifier(expr))
|
|
410
750
|
return t.jsxIdentifier(expr.name);
|
|
411
751
|
if (t.isMemberExpression(expr) && !expr.computed && t.isIdentifier(expr.property)) {
|
|
@@ -421,8 +761,15 @@ function exprToJsxAttrValue(expr) {
|
|
|
421
761
|
return t.jsxExpressionContainer(expr);
|
|
422
762
|
}
|
|
423
763
|
function childToJsxChild(child) {
|
|
764
|
+
var _a;
|
|
424
765
|
if (t.isStringLiteral(child))
|
|
425
766
|
return t.jsxText(child.value);
|
|
767
|
+
// Template literal with no expressions: `Dashboard` → JSXText
|
|
768
|
+
if (t.isTemplateLiteral(child) && child.expressions.length === 0 && child.quasis.length === 1) {
|
|
769
|
+
const raw = (_a = child.quasis[0].value.cooked) !== null && _a !== void 0 ? _a : child.quasis[0].value.raw;
|
|
770
|
+
if (raw !== undefined)
|
|
771
|
+
return t.jsxText(raw);
|
|
772
|
+
}
|
|
426
773
|
if (t.isJSXElement(child) || t.isJSXFragment(child))
|
|
427
774
|
return child;
|
|
428
775
|
// Recursively convert nested jsx(...) calls into JSX elements.
|
|
@@ -433,23 +780,52 @@ function childToJsxChild(child) {
|
|
|
433
780
|
}
|
|
434
781
|
return t.jsxExpressionContainer(child);
|
|
435
782
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
783
|
+
// Detect Babel's compiled object-spread IIFE:
|
|
784
|
+
// (function(target) { for(var i=1; i<arguments.length; i++){...} return target; })(base, spread1, ...)
|
|
785
|
+
// Returns { base, spreads } if matched, null otherwise.
|
|
786
|
+
function tryUnpackSpreadIIFE(expr) {
|
|
787
|
+
if (!t.isCallExpression(expr))
|
|
439
788
|
return null;
|
|
440
|
-
|
|
789
|
+
const callee = expr.callee;
|
|
790
|
+
if (!t.isFunctionExpression(callee))
|
|
441
791
|
return null;
|
|
442
|
-
if (
|
|
792
|
+
if (callee.params.length !== 1 || !t.isIdentifier(callee.params[0]))
|
|
443
793
|
return null;
|
|
444
|
-
const
|
|
445
|
-
const
|
|
446
|
-
|
|
447
|
-
|
|
794
|
+
const paramName = callee.params[0].name;
|
|
795
|
+
const body = callee.body.body;
|
|
796
|
+
if (body.length < 2)
|
|
797
|
+
return null;
|
|
798
|
+
// Must contain a for loop that iterates `arguments.length`
|
|
799
|
+
const hasArgumentsLoop = body.some((s) => {
|
|
800
|
+
if (!t.isForStatement(s) || !s.test || !t.isBinaryExpression(s.test))
|
|
801
|
+
return false;
|
|
802
|
+
const right = s.test.right;
|
|
803
|
+
return (s.test.operator === "<" &&
|
|
804
|
+
t.isMemberExpression(right) &&
|
|
805
|
+
t.isIdentifier(right.object, { name: "arguments" }) &&
|
|
806
|
+
t.isIdentifier(right.property, { name: "length" }));
|
|
807
|
+
});
|
|
808
|
+
if (!hasArgumentsLoop)
|
|
448
809
|
return null;
|
|
810
|
+
// Must return the first param
|
|
811
|
+
const hasReturn = body.some((s) => t.isReturnStatement(s) && s.argument && t.isIdentifier(s.argument, { name: paramName }));
|
|
812
|
+
if (!hasReturn)
|
|
813
|
+
return null;
|
|
814
|
+
const args = expr.arguments;
|
|
815
|
+
if (args.length < 1)
|
|
816
|
+
return null;
|
|
817
|
+
return { base: args[0], spreads: args.slice(1) };
|
|
818
|
+
}
|
|
819
|
+
/** Build JSXAttributes + children from an ObjectExpression or a spread-IIFE propsArg. */
|
|
820
|
+
function propsArgToAttrsAndChildren(propsArg) {
|
|
449
821
|
const attrs = [];
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
for (const prop of
|
|
822
|
+
const childExprs = [];
|
|
823
|
+
const processObjectExpr = (obj) => {
|
|
824
|
+
for (const prop of obj.properties) {
|
|
825
|
+
if (t.isSpreadElement(prop)) {
|
|
826
|
+
attrs.push(t.jsxSpreadAttribute(prop.argument));
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
453
829
|
if (!t.isObjectProperty(prop) || prop.computed)
|
|
454
830
|
continue;
|
|
455
831
|
const keyNode = prop.key;
|
|
@@ -458,35 +834,94 @@ function tryConvertToJSX(call) {
|
|
|
458
834
|
if (!keyName)
|
|
459
835
|
continue;
|
|
460
836
|
if (keyName === "children") {
|
|
461
|
-
// children can be a single value or an array
|
|
462
837
|
if (t.isArrayExpression(valNode)) {
|
|
463
838
|
for (const el of valNode.elements) {
|
|
464
|
-
if (
|
|
465
|
-
|
|
466
|
-
const ch = childToJsxChild(el);
|
|
467
|
-
if (ch)
|
|
468
|
-
children.push(ch);
|
|
839
|
+
if (el)
|
|
840
|
+
childExprs.push(el);
|
|
469
841
|
}
|
|
470
842
|
}
|
|
471
843
|
else {
|
|
472
|
-
|
|
473
|
-
if (ch)
|
|
474
|
-
children.push(ch);
|
|
844
|
+
childExprs.push(valNode);
|
|
475
845
|
}
|
|
476
846
|
continue;
|
|
477
847
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
848
|
+
attrs.push(t.jsxAttribute(t.jsxIdentifier(keyName), exprToJsxAttrValue(valNode)));
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
if (t.isObjectExpression(propsArg)) {
|
|
852
|
+
processObjectExpr(propsArg);
|
|
853
|
+
}
|
|
854
|
+
else {
|
|
855
|
+
// Try to unpack Babel's compiled object-spread IIFE
|
|
856
|
+
const unpacked = tryUnpackSpreadIIFE(propsArg);
|
|
857
|
+
if (unpacked) {
|
|
858
|
+
if (t.isObjectExpression(unpacked.base)) {
|
|
859
|
+
processObjectExpr(unpacked.base);
|
|
482
860
|
}
|
|
483
861
|
else {
|
|
484
|
-
attrs.push(t.
|
|
862
|
+
attrs.push(t.jsxSpreadAttribute(unpacked.base));
|
|
863
|
+
}
|
|
864
|
+
for (const spreadExpr of unpacked.spreads) {
|
|
865
|
+
attrs.push(t.jsxSpreadAttribute(spreadExpr));
|
|
485
866
|
}
|
|
486
867
|
}
|
|
868
|
+
else {
|
|
869
|
+
// Fallback: emit the entire props argument as a single JSX spread
|
|
870
|
+
attrs.push(t.jsxSpreadAttribute(propsArg));
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
return { attrs, childExprs };
|
|
874
|
+
}
|
|
875
|
+
const JSX_METHOD_NAMES = new Set(["jsx", "jsxs", "jsxDEV"]);
|
|
876
|
+
// Extract the jsx method name from a callee that may be:
|
|
877
|
+
// - bare identifier: `jsx`
|
|
878
|
+
// - member expression: `ns.jsx`
|
|
879
|
+
// - sequence (0, ns.jsx) after a `(0, ...)()` call
|
|
880
|
+
// Returns the method name string, or null if not a jsx call.
|
|
881
|
+
function getJsxMethodName(callee) {
|
|
882
|
+
if (t.isIdentifier(callee) && JSX_METHOD_NAMES.has(callee.name)) {
|
|
883
|
+
return callee.name;
|
|
487
884
|
}
|
|
885
|
+
if (t.isMemberExpression(callee) && !callee.computed) {
|
|
886
|
+
const prop = callee.property;
|
|
887
|
+
if (t.isIdentifier(prop) && JSX_METHOD_NAMES.has(prop.name)) {
|
|
888
|
+
return prop.name;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
// (0, ns.jsx) sequence expression
|
|
892
|
+
if (t.isSequenceExpression(callee)) {
|
|
893
|
+
const exprs = callee.expressions;
|
|
894
|
+
const last = exprs[exprs.length - 1];
|
|
895
|
+
if (last && t.isMemberExpression(last) && !last.computed) {
|
|
896
|
+
const prop = last.property;
|
|
897
|
+
if (t.isIdentifier(prop) && JSX_METHOD_NAMES.has(prop.name)) {
|
|
898
|
+
return prop.name;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
return null;
|
|
903
|
+
}
|
|
904
|
+
function tryConvertToJSX(call) {
|
|
905
|
+
const callee = call.callee;
|
|
906
|
+
const methodName = getJsxMethodName(callee);
|
|
907
|
+
if (!methodName)
|
|
908
|
+
return null;
|
|
909
|
+
if (call.arguments.length < 2)
|
|
910
|
+
return null;
|
|
911
|
+
const tagArg = call.arguments[0];
|
|
912
|
+
const propsArg = call.arguments[1];
|
|
913
|
+
const jsxName = exprToJsxName(tagArg);
|
|
914
|
+
if (!jsxName)
|
|
915
|
+
return null;
|
|
916
|
+
const { attrs, childExprs } = propsArgToAttrsAndChildren(propsArg);
|
|
917
|
+
const children = childExprs.map((e) => childToJsxChild(e)).filter(Boolean);
|
|
918
|
+
// Fragment identifier → JSXFragment shorthand (<>...</>)
|
|
919
|
+
if (t.isJSXIdentifier(jsxName) && jsxName.name === "Fragment") {
|
|
920
|
+
return t.jsxFragment(t.jsxOpeningFragment(), t.jsxClosingFragment(), children);
|
|
921
|
+
}
|
|
922
|
+
const jsxAttrs = attrs.filter((a) => t.isJSXAttribute(a) || t.isJSXSpreadAttribute(a));
|
|
488
923
|
const selfClosing = children.length === 0;
|
|
489
|
-
const openingElement = t.jsxOpeningElement(jsxName,
|
|
924
|
+
const openingElement = t.jsxOpeningElement(jsxName, jsxAttrs, selfClosing);
|
|
490
925
|
const closingElement = selfClosing ? null : t.jsxClosingElement(jsxName);
|
|
491
926
|
return t.jsxElement(openingElement, closingElement, children, selfClosing);
|
|
492
927
|
}
|
|
@@ -499,7 +934,7 @@ function recoverJSX(statements) {
|
|
|
499
934
|
if (!jsxEl)
|
|
500
935
|
return;
|
|
501
936
|
p.replaceWith(jsxEl);
|
|
502
|
-
|
|
937
|
+
// Do NOT skip — nested jsx calls inside lambdas/expression containers need visiting.
|
|
503
938
|
},
|
|
504
939
|
});
|
|
505
940
|
}
|
|
@@ -617,6 +1052,50 @@ function isBabelObjectSpreadHelper(stmt) {
|
|
|
617
1052
|
const bodyStr = JSON.stringify(body);
|
|
618
1053
|
return bodyStr.includes('"getOwnPropertySymbols"');
|
|
619
1054
|
}
|
|
1055
|
+
// Detect `_objectSpread2` — Babel's 1-param spread helper that uses arguments.length
|
|
1056
|
+
// and Object.defineProperties to merge source objects into the target.
|
|
1057
|
+
function isBabelObjectSpread2Helper(stmt) {
|
|
1058
|
+
if (!t.isFunctionDeclaration(stmt))
|
|
1059
|
+
return false;
|
|
1060
|
+
const params = stmt.params;
|
|
1061
|
+
if (params.length !== 1)
|
|
1062
|
+
return false;
|
|
1063
|
+
const body = stmt.body.body;
|
|
1064
|
+
// Must have a for loop that reads arguments.length, then calls Object.defineProperties
|
|
1065
|
+
const hasArgumentsLength = JSON.stringify(body).includes('"arguments"');
|
|
1066
|
+
const hasDefineProperties = JSON.stringify(body).includes('"defineProperties"') || JSON.stringify(body).includes('"defineProperty"');
|
|
1067
|
+
return hasArgumentsLength && hasDefineProperties;
|
|
1068
|
+
}
|
|
1069
|
+
// Detect `_ownKeys` / `ownKeys` — companion helper to _objectSpread2.
|
|
1070
|
+
// Shape: 1- or 2-param function whose first statement declares a var via Object.keys(param)
|
|
1071
|
+
// and whose body references getOwnPropertySymbols.
|
|
1072
|
+
function isBabelOwnKeysHelper(stmt) {
|
|
1073
|
+
if (!t.isFunctionDeclaration(stmt))
|
|
1074
|
+
return false;
|
|
1075
|
+
const params = stmt.params;
|
|
1076
|
+
if (params.length < 1 || params.length > 2)
|
|
1077
|
+
return false;
|
|
1078
|
+
const body = stmt.body.body;
|
|
1079
|
+
if (body.length === 0)
|
|
1080
|
+
return false;
|
|
1081
|
+
// First statement declares a var via Object.keys(param)
|
|
1082
|
+
const first = body[0];
|
|
1083
|
+
if (!t.isVariableDeclaration(first))
|
|
1084
|
+
return false;
|
|
1085
|
+
const bodyStr = JSON.stringify(body);
|
|
1086
|
+
return bodyStr.includes('"keys"') && bodyStr.includes('"getOwnPropertySymbols"');
|
|
1087
|
+
}
|
|
1088
|
+
// Named Babel array-destructure companion helpers that appear as FunctionDeclarations.
|
|
1089
|
+
const NAMED_BABEL_ARRAY_HELPERS = new Set([
|
|
1090
|
+
"_arrayWithHoles",
|
|
1091
|
+
"_arrayLikeToArray",
|
|
1092
|
+
"_iterableToArrayLimit",
|
|
1093
|
+
"_unsupportedIterableToArray",
|
|
1094
|
+
"_nonIterableRest",
|
|
1095
|
+
]);
|
|
1096
|
+
function isNamedBabelArrayHelper(stmt) {
|
|
1097
|
+
return t.isFunctionDeclaration(stmt) && stmt.id != null && NAMED_BABEL_ARRAY_HELPERS.has(stmt.id.name);
|
|
1098
|
+
}
|
|
620
1099
|
/** Remove `var x = {}` declarations where every declarator has an empty-object init.
|
|
621
1100
|
* These are webpack module-cache variables. */
|
|
622
1101
|
function dropEmptyObjectVars(stmts) {
|
|
@@ -692,6 +1171,481 @@ function pruneUnusedNamedImports(importStmts, bodyStmts) {
|
|
|
692
1171
|
.filter(Boolean);
|
|
693
1172
|
}
|
|
694
1173
|
// ---------------------------------------------------------------------------
|
|
1174
|
+
// Pass D for module files — library-aware import rewriting
|
|
1175
|
+
// ---------------------------------------------------------------------------
|
|
1176
|
+
/**
|
|
1177
|
+
* Applies Pass D to an already-transformed module file whose imports are in the
|
|
1178
|
+
* form `import * as ns from './N.js'`. Looks up each module ID `N` in
|
|
1179
|
+
* `libModuleMap`, rewrites call-sites from `(0, ns.hook)(...)` to `hook(...)`,
|
|
1180
|
+
* replaces namespace imports with named imports from the real library path, and
|
|
1181
|
+
* prunes unused specifiers.
|
|
1182
|
+
*
|
|
1183
|
+
* Returns the rewritten statement list (imports first, then body).
|
|
1184
|
+
* If `libModuleMap` is empty / not provided, returns `statements` unchanged.
|
|
1185
|
+
*/
|
|
1186
|
+
export const applyLibraryImportRewriting = (statements, libModuleMap) => {
|
|
1187
|
+
if (!libModuleMap || libModuleMap.size === 0)
|
|
1188
|
+
return statements;
|
|
1189
|
+
// Separate existing import declarations from body statements
|
|
1190
|
+
const importDecls = [];
|
|
1191
|
+
const bodyStmts = [];
|
|
1192
|
+
for (const stmt of statements) {
|
|
1193
|
+
if (t.isImportDeclaration(stmt))
|
|
1194
|
+
importDecls.push(stmt);
|
|
1195
|
+
else
|
|
1196
|
+
bodyStmts.push(stmt);
|
|
1197
|
+
}
|
|
1198
|
+
// Build varName → moduleId from `import * as ns from './N.js'`
|
|
1199
|
+
const varToModuleId = new Map(); // ns → numericModuleId
|
|
1200
|
+
for (const decl of importDecls) {
|
|
1201
|
+
const source = decl.source.value;
|
|
1202
|
+
const match = source.match(/^\.\/(\d+)\.js$/);
|
|
1203
|
+
if (!match)
|
|
1204
|
+
continue;
|
|
1205
|
+
const numId = parseInt(match[1], 10);
|
|
1206
|
+
for (const spec of decl.specifiers) {
|
|
1207
|
+
if (t.isImportNamespaceSpecifier(spec)) {
|
|
1208
|
+
varToModuleId.set(spec.local.name, numId);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
// Build varName → LibraryModuleInfo
|
|
1213
|
+
const varToLib = new Map();
|
|
1214
|
+
for (const [varName, numId] of varToModuleId) {
|
|
1215
|
+
const info = libModuleMap.get(String(numId));
|
|
1216
|
+
if (info)
|
|
1217
|
+
varToLib.set(varName, info);
|
|
1218
|
+
}
|
|
1219
|
+
if (varToLib.size === 0)
|
|
1220
|
+
return statements;
|
|
1221
|
+
// --- CSS injection stripping ---
|
|
1222
|
+
// Collect CSS-typed import vars (style-loader, css-module)
|
|
1223
|
+
const cssImportVars = new Set();
|
|
1224
|
+
for (const [varName, info] of varToLib) {
|
|
1225
|
+
if (info.type === "style-loader" || info.type === "css-module") {
|
|
1226
|
+
cssImportVars.add(varName);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
if (cssImportVars.size > 0) {
|
|
1230
|
+
const CSS_INJECT_PROPS = new Set([
|
|
1231
|
+
"styleTagTransform",
|
|
1232
|
+
"setAttributes",
|
|
1233
|
+
"insert",
|
|
1234
|
+
"domAPI",
|
|
1235
|
+
"insertStyleElement",
|
|
1236
|
+
]);
|
|
1237
|
+
// Find CSS wrapper vars: var x = <expr>.n(libVar) for any library import.
|
|
1238
|
+
// webpack's .n() (interopRequireDefault) only appears in the CSS injection
|
|
1239
|
+
// setup in React apps; it's safe to strip all library-import .n() wrappers.
|
|
1240
|
+
const allLibVars = new Set(varToLib.keys());
|
|
1241
|
+
const cssWrapperVars = new Set();
|
|
1242
|
+
for (const stmt of bodyStmts) {
|
|
1243
|
+
if (!t.isVariableDeclaration(stmt))
|
|
1244
|
+
continue;
|
|
1245
|
+
for (const decl of stmt.declarations) {
|
|
1246
|
+
if (!t.isIdentifier(decl.id) || !decl.init)
|
|
1247
|
+
continue;
|
|
1248
|
+
if (!t.isCallExpression(decl.init))
|
|
1249
|
+
continue;
|
|
1250
|
+
const callee = decl.init.callee;
|
|
1251
|
+
const args = decl.init.arguments;
|
|
1252
|
+
if (t.isMemberExpression(callee) &&
|
|
1253
|
+
!callee.computed &&
|
|
1254
|
+
t.isIdentifier(callee.property, { name: "n" }) &&
|
|
1255
|
+
args.length === 1 &&
|
|
1256
|
+
t.isIdentifier(args[0]) &&
|
|
1257
|
+
allLibVars.has(args[0].name)) {
|
|
1258
|
+
cssWrapperVars.add(decl.id.name);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
// Find CSS config object vars: var g = {} where g is assigned CSS injection props
|
|
1263
|
+
const cssConfigObjVars = new Set();
|
|
1264
|
+
for (const stmt of bodyStmts) {
|
|
1265
|
+
if (!t.isExpressionStatement(stmt))
|
|
1266
|
+
continue;
|
|
1267
|
+
const expr = stmt.expression;
|
|
1268
|
+
if (!t.isAssignmentExpression(expr))
|
|
1269
|
+
continue;
|
|
1270
|
+
const lhs = expr.left;
|
|
1271
|
+
if (!t.isMemberExpression(lhs) || lhs.computed)
|
|
1272
|
+
continue;
|
|
1273
|
+
const prop = lhs.property;
|
|
1274
|
+
if (t.isIdentifier(prop) && CSS_INJECT_PROPS.has(prop.name)) {
|
|
1275
|
+
if (t.isIdentifier(lhs.object))
|
|
1276
|
+
cssConfigObjVars.add(lhs.object.name);
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
const isCssMemberRef = (e) => {
|
|
1280
|
+
if (!t.isMemberExpression(e) || e.computed)
|
|
1281
|
+
return false;
|
|
1282
|
+
return (t.isIdentifier(e.object) &&
|
|
1283
|
+
cssImportVars.has(e.object.name));
|
|
1284
|
+
};
|
|
1285
|
+
const hasLogicalCssRef = (e) => {
|
|
1286
|
+
if (isCssMemberRef(e))
|
|
1287
|
+
return true;
|
|
1288
|
+
if (t.isLogicalExpression(e))
|
|
1289
|
+
return (hasLogicalCssRef(e.left) ||
|
|
1290
|
+
hasLogicalCssRef(e.right));
|
|
1291
|
+
return false;
|
|
1292
|
+
};
|
|
1293
|
+
const isCssInjectionStmt = (stmt) => {
|
|
1294
|
+
if (!t.isExpressionStatement(stmt))
|
|
1295
|
+
return false;
|
|
1296
|
+
const expr = stmt.expression;
|
|
1297
|
+
// g.styleTagTransform = ..., g.setAttributes = ..., etc.
|
|
1298
|
+
if (t.isAssignmentExpression(expr) &&
|
|
1299
|
+
t.isMemberExpression(expr.left) &&
|
|
1300
|
+
!expr.left.computed &&
|
|
1301
|
+
t.isIdentifier(expr.left.property) &&
|
|
1302
|
+
CSS_INJECT_PROPS.has(expr.left.property.name))
|
|
1303
|
+
return true;
|
|
1304
|
+
// cssWrapperVar()(cssModuleVar.A, configObj)
|
|
1305
|
+
if (t.isCallExpression(expr) && t.isCallExpression(expr.callee)) {
|
|
1306
|
+
const innerCallee = expr.callee.callee;
|
|
1307
|
+
if (t.isIdentifier(innerCallee) && cssWrapperVars.has(innerCallee.name))
|
|
1308
|
+
return true;
|
|
1309
|
+
}
|
|
1310
|
+
// cssModuleVar.A && cssModuleVar.A.locals && cssModuleVar.A.locals
|
|
1311
|
+
if (t.isLogicalExpression(expr) && hasLogicalCssRef(expr))
|
|
1312
|
+
return true;
|
|
1313
|
+
return false;
|
|
1314
|
+
};
|
|
1315
|
+
const filteredStmts = [];
|
|
1316
|
+
for (const stmt of bodyStmts) {
|
|
1317
|
+
if (isCssInjectionStmt(stmt))
|
|
1318
|
+
continue;
|
|
1319
|
+
if (t.isVariableDeclaration(stmt)) {
|
|
1320
|
+
const remaining = stmt.declarations.filter((decl) => {
|
|
1321
|
+
if (!t.isIdentifier(decl.id))
|
|
1322
|
+
return true;
|
|
1323
|
+
const name = decl.id.name;
|
|
1324
|
+
if (cssWrapperVars.has(name))
|
|
1325
|
+
return false;
|
|
1326
|
+
if (cssConfigObjVars.has(name) &&
|
|
1327
|
+
decl.init &&
|
|
1328
|
+
t.isObjectExpression(decl.init) &&
|
|
1329
|
+
decl.init.properties.length === 0)
|
|
1330
|
+
return false;
|
|
1331
|
+
return true;
|
|
1332
|
+
});
|
|
1333
|
+
if (remaining.length === 0)
|
|
1334
|
+
continue;
|
|
1335
|
+
filteredStmts.push(remaining.length < stmt.declarations.length ? t.variableDeclaration(stmt.kind, remaining) : stmt);
|
|
1336
|
+
continue;
|
|
1337
|
+
}
|
|
1338
|
+
filteredStmts.push(stmt);
|
|
1339
|
+
}
|
|
1340
|
+
bodyStmts.splice(0, bodyStmts.length, ...filteredStmts);
|
|
1341
|
+
}
|
|
1342
|
+
// Rewrite call-sites in bodyStmts; collect used exports per library
|
|
1343
|
+
const usedExports = rewriteLibraryCalls(bodyStmts, varToLib);
|
|
1344
|
+
// Build replacement import declarations
|
|
1345
|
+
const finalImportStmts = [];
|
|
1346
|
+
const handledSpecs = new Set();
|
|
1347
|
+
for (const [varName, info] of varToLib) {
|
|
1348
|
+
const src = librarySource(info.type);
|
|
1349
|
+
if (!src)
|
|
1350
|
+
continue;
|
|
1351
|
+
if (!handledSpecs.has(src)) {
|
|
1352
|
+
handledSpecs.add(src);
|
|
1353
|
+
const used = usedExports.get(src);
|
|
1354
|
+
if (used && used.size > 0) {
|
|
1355
|
+
const specifiers = [...used]
|
|
1356
|
+
.sort()
|
|
1357
|
+
.map((name) => t.importSpecifier(t.identifier(name), t.identifier(name)));
|
|
1358
|
+
finalImportStmts.push(t.importDeclaration(specifiers, t.stringLiteral(src)));
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
// Keep non-library namespace imports unchanged.
|
|
1363
|
+
// For library namespace imports: keep only if the namespace variable is still used as
|
|
1364
|
+
// the object of a MemberExpression or JSXMemberExpression (i.e., namespace access).
|
|
1365
|
+
// Using collectReferencedNames would falsely keep imports when a local variable in a
|
|
1366
|
+
// closure happens to share the namespace name (e.g. `const n = e.toLowerCase()`).
|
|
1367
|
+
const namespaceObjRefs = new Set();
|
|
1368
|
+
{
|
|
1369
|
+
const syntheticFile = t.file(t.program(bodyStmts, [], "module"));
|
|
1370
|
+
traverse(syntheticFile, {
|
|
1371
|
+
MemberExpression(p) {
|
|
1372
|
+
if (!p.node.computed && t.isIdentifier(p.node.object)) {
|
|
1373
|
+
namespaceObjRefs.add(p.node.object.name);
|
|
1374
|
+
}
|
|
1375
|
+
},
|
|
1376
|
+
JSXMemberExpression(p) {
|
|
1377
|
+
if (t.isJSXIdentifier(p.node.object)) {
|
|
1378
|
+
namespaceObjRefs.add(p.node.object.name);
|
|
1379
|
+
}
|
|
1380
|
+
},
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
for (const decl of importDecls) {
|
|
1384
|
+
const source = decl.source.value;
|
|
1385
|
+
const match = source.match(/^\.\/(\d+)\.js$/);
|
|
1386
|
+
if (!match) {
|
|
1387
|
+
finalImportStmts.push(decl);
|
|
1388
|
+
continue;
|
|
1389
|
+
}
|
|
1390
|
+
const numId = parseInt(match[1], 10);
|
|
1391
|
+
const isLibrary = [...varToModuleId.entries()].some(([vn, id]) => id === numId && varToLib.has(vn));
|
|
1392
|
+
if (!isLibrary) {
|
|
1393
|
+
finalImportStmts.push(decl);
|
|
1394
|
+
continue;
|
|
1395
|
+
}
|
|
1396
|
+
// Library module: keep the namespace import only if the namespace var is
|
|
1397
|
+
// still used as a namespace object (i.e. some exports were not resolved).
|
|
1398
|
+
const hasRemainingRefs = decl.specifiers.some((spec) => t.isImportNamespaceSpecifier(spec) && namespaceObjRefs.has(spec.local.name));
|
|
1399
|
+
if (hasRemainingRefs)
|
|
1400
|
+
finalImportStmts.push(decl);
|
|
1401
|
+
}
|
|
1402
|
+
// Pass H — prune named imports that are no longer referenced
|
|
1403
|
+
const prunedImports = pruneUnusedNamedImports(finalImportStmts, bodyStmts);
|
|
1404
|
+
return [...prunedImports, ...bodyStmts];
|
|
1405
|
+
};
|
|
1406
|
+
// ---------------------------------------------------------------------------
|
|
1407
|
+
// Module cleanup passes — applies E/F/G standalone (no webpack require context needed)
|
|
1408
|
+
// ---------------------------------------------------------------------------
|
|
1409
|
+
/**
|
|
1410
|
+
* Applies JSX recovery, slicedToArray collapse, and Babel helper removal to
|
|
1411
|
+
* any list of statements. Used for both individual module files and index.js.
|
|
1412
|
+
* Does NOT require a webpack require-function name or library module map.
|
|
1413
|
+
*/
|
|
1414
|
+
export const applyModuleCleanupPasses = (statements) => {
|
|
1415
|
+
// Pass E — collapse Babel slicedToArray expansions.
|
|
1416
|
+
const afterE = collapseSlicedToArrayDeep(statements);
|
|
1417
|
+
// Pass F — JSX recovery.
|
|
1418
|
+
recoverJSX(afterE);
|
|
1419
|
+
// Pass G — remove top-level Babel helper functions and webpack module-cache vars.
|
|
1420
|
+
const afterG = dropEmptyObjectVars(afterE.filter((stmt) => !isBabelArrayLikeToArrayHelper(stmt) &&
|
|
1421
|
+
!isBabelTypeofHelper(stmt) &&
|
|
1422
|
+
!isBabelDefinePropertyHelper(stmt) &&
|
|
1423
|
+
!isBabelObjectSpreadHelper(stmt) &&
|
|
1424
|
+
!isBabelObjectSpread2Helper(stmt) &&
|
|
1425
|
+
!isBabelOwnKeysHelper(stmt) &&
|
|
1426
|
+
!isNamedBabelArrayHelper(stmt)));
|
|
1427
|
+
return afterG;
|
|
1428
|
+
};
|
|
1429
|
+
// ---------------------------------------------------------------------------
|
|
1430
|
+
// Route-aware component renaming
|
|
1431
|
+
// ---------------------------------------------------------------------------
|
|
1432
|
+
/** Derives a component name from a React Router path string. */
|
|
1433
|
+
function pathToComponentName(fullPath) {
|
|
1434
|
+
if (fullPath === "/" || fullPath === "")
|
|
1435
|
+
return "Home";
|
|
1436
|
+
const isIndex = fullPath.endsWith("/index");
|
|
1437
|
+
const base = isIndex ? fullPath.slice(0, -"/index".length) : fullPath;
|
|
1438
|
+
const segments = base
|
|
1439
|
+
.replace(/^\//, "")
|
|
1440
|
+
.split("/")
|
|
1441
|
+
.filter((s) => s && !s.startsWith(":"));
|
|
1442
|
+
if (segments.length === 0)
|
|
1443
|
+
return "Home";
|
|
1444
|
+
const name = segments
|
|
1445
|
+
.map((s) => s.charAt(0).toUpperCase() + s.slice(1).replace(/-(\w)/g, (_, c) => c.toUpperCase()))
|
|
1446
|
+
.join("");
|
|
1447
|
+
return isIndex ? `${name}Dashboard` : name;
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Post-pass that renames minified component variables to meaningful names derived
|
|
1451
|
+
* from React Router route paths. Intended to run after JSX recovery (Pass F) so
|
|
1452
|
+
* that `<Route path="..." element={<X />} />` patterns are visible as JSX.
|
|
1453
|
+
*
|
|
1454
|
+
* Also renames:
|
|
1455
|
+
* - The root App component (the one that contains `<Routes>`)
|
|
1456
|
+
* - The Suspense fallback component (if it's a locally-defined function)
|
|
1457
|
+
*/
|
|
1458
|
+
export const renameRouteComponents = (statements) => {
|
|
1459
|
+
// Step 1: collect lazy-import variable names → spec (e.g. '_' → './45.js')
|
|
1460
|
+
const lazyVars = new Map(); // varName → importSpec
|
|
1461
|
+
for (const stmt of statements) {
|
|
1462
|
+
if (!t.isVariableDeclaration(stmt))
|
|
1463
|
+
continue;
|
|
1464
|
+
for (const decl of stmt.declarations) {
|
|
1465
|
+
if (!t.isIdentifier(decl.id) || !decl.init)
|
|
1466
|
+
continue;
|
|
1467
|
+
if (!t.isCallExpression(decl.init))
|
|
1468
|
+
continue;
|
|
1469
|
+
const callExpr = decl.init;
|
|
1470
|
+
if (!t.isIdentifier(callExpr.callee, { name: "lazy" }))
|
|
1471
|
+
continue;
|
|
1472
|
+
if (callExpr.arguments.length !== 1)
|
|
1473
|
+
continue;
|
|
1474
|
+
const arg = callExpr.arguments[0];
|
|
1475
|
+
if (!t.isArrowFunctionExpression(arg) && !t.isFunctionExpression(arg))
|
|
1476
|
+
continue;
|
|
1477
|
+
const body = arg.body;
|
|
1478
|
+
// body is `import('./N.js')` — a CallExpression with an Import callee
|
|
1479
|
+
const importCall = t.isBlockStatement(body)
|
|
1480
|
+
? body.body.length === 1 &&
|
|
1481
|
+
t.isReturnStatement(body.body[0])
|
|
1482
|
+
? body.body[0].argument
|
|
1483
|
+
: null
|
|
1484
|
+
: body;
|
|
1485
|
+
if (!importCall || !t.isCallExpression(importCall))
|
|
1486
|
+
continue;
|
|
1487
|
+
if (!t.isImport(importCall.callee))
|
|
1488
|
+
continue;
|
|
1489
|
+
const importArgs = importCall.arguments;
|
|
1490
|
+
if (importArgs.length !== 1 || !t.isStringLiteral(importArgs[0]))
|
|
1491
|
+
continue;
|
|
1492
|
+
lazyVars.set(decl.id.name, importArgs[0].value);
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
if (lazyVars.size === 0)
|
|
1496
|
+
return statements;
|
|
1497
|
+
// Step 2: traverse JSX Route elements, tracking parent route paths for full-path assembly.
|
|
1498
|
+
const varToPath = new Map(); // varName → full route path
|
|
1499
|
+
const syntheticFile = t.file(t.program(statements, [], "module"));
|
|
1500
|
+
const pathStack = [];
|
|
1501
|
+
traverse(syntheticFile, {
|
|
1502
|
+
JSXElement: {
|
|
1503
|
+
enter(p) {
|
|
1504
|
+
const opening = p.node.openingElement;
|
|
1505
|
+
if (!t.isJSXIdentifier(opening.name, { name: "Route" }))
|
|
1506
|
+
return;
|
|
1507
|
+
let routePath = null;
|
|
1508
|
+
let isIndex = false;
|
|
1509
|
+
let elementVarName = null;
|
|
1510
|
+
for (const attr of opening.attributes) {
|
|
1511
|
+
if (!t.isJSXAttribute(attr))
|
|
1512
|
+
continue;
|
|
1513
|
+
const keyName = t.isJSXIdentifier(attr.name) ? attr.name.name : null;
|
|
1514
|
+
if (keyName === "path" && t.isStringLiteral(attr.value)) {
|
|
1515
|
+
routePath = attr.value.value;
|
|
1516
|
+
}
|
|
1517
|
+
else if (keyName === "index") {
|
|
1518
|
+
isIndex = true;
|
|
1519
|
+
}
|
|
1520
|
+
else if (keyName === "element") {
|
|
1521
|
+
const val = attr.value;
|
|
1522
|
+
const jsxEl = t.isJSXExpressionContainer(val)
|
|
1523
|
+
? t.isJSXElement(val.expression)
|
|
1524
|
+
? val.expression
|
|
1525
|
+
: null
|
|
1526
|
+
: t.isJSXElement(val)
|
|
1527
|
+
? val
|
|
1528
|
+
: null;
|
|
1529
|
+
if (jsxEl) {
|
|
1530
|
+
const tagName = jsxEl.openingElement.name;
|
|
1531
|
+
if (t.isJSXIdentifier(tagName))
|
|
1532
|
+
elementVarName = tagName.name;
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
const parentPath = pathStack.length > 0 ? pathStack[pathStack.length - 1] : "";
|
|
1537
|
+
let fullPath;
|
|
1538
|
+
if (routePath !== null) {
|
|
1539
|
+
fullPath = routePath.startsWith("/") ? routePath : `${parentPath}/${routePath}`;
|
|
1540
|
+
}
|
|
1541
|
+
else if (isIndex) {
|
|
1542
|
+
fullPath = `${parentPath}/index`;
|
|
1543
|
+
}
|
|
1544
|
+
else {
|
|
1545
|
+
fullPath = parentPath;
|
|
1546
|
+
}
|
|
1547
|
+
pathStack.push(fullPath);
|
|
1548
|
+
if (elementVarName && lazyVars.has(elementVarName)) {
|
|
1549
|
+
varToPath.set(elementVarName, fullPath);
|
|
1550
|
+
}
|
|
1551
|
+
},
|
|
1552
|
+
exit(p) {
|
|
1553
|
+
if (t.isJSXIdentifier(p.node.openingElement.name, { name: "Route" })) {
|
|
1554
|
+
pathStack.pop();
|
|
1555
|
+
}
|
|
1556
|
+
},
|
|
1557
|
+
},
|
|
1558
|
+
});
|
|
1559
|
+
// Step 3: generate semantic names from route paths
|
|
1560
|
+
const renames = new Map(); // oldName → newName
|
|
1561
|
+
const usedNames = new Set();
|
|
1562
|
+
for (const [varName, fullPath] of varToPath) {
|
|
1563
|
+
let name = pathToComponentName(fullPath);
|
|
1564
|
+
if (usedNames.has(name)) {
|
|
1565
|
+
let i = 2;
|
|
1566
|
+
while (usedNames.has(`${name}${i}`))
|
|
1567
|
+
i++;
|
|
1568
|
+
name = `${name}${i}`;
|
|
1569
|
+
}
|
|
1570
|
+
usedNames.add(name);
|
|
1571
|
+
renames.set(varName, name);
|
|
1572
|
+
}
|
|
1573
|
+
// Step 4: find the root App component — the function whose JSX body contains <Routes>
|
|
1574
|
+
traverse(syntheticFile, {
|
|
1575
|
+
JSXElement(p) {
|
|
1576
|
+
if (!t.isJSXIdentifier(p.node.openingElement.name, { name: "Routes" }))
|
|
1577
|
+
return;
|
|
1578
|
+
let ancestor = p.parentPath;
|
|
1579
|
+
while (ancestor) {
|
|
1580
|
+
if (ancestor.isFunctionDeclaration() ||
|
|
1581
|
+
ancestor.isFunctionExpression() ||
|
|
1582
|
+
ancestor.isArrowFunctionExpression()) {
|
|
1583
|
+
if (ancestor.isFunctionDeclaration()) {
|
|
1584
|
+
const id = ancestor.node.id;
|
|
1585
|
+
if (id && !renames.has(id.name))
|
|
1586
|
+
renames.set(id.name, "App");
|
|
1587
|
+
}
|
|
1588
|
+
else {
|
|
1589
|
+
const par = ancestor.parentPath;
|
|
1590
|
+
if (par === null || par === void 0 ? void 0 : par.isVariableDeclarator()) {
|
|
1591
|
+
const declId = par.node.id;
|
|
1592
|
+
if (t.isIdentifier(declId) && !renames.has(declId.name)) {
|
|
1593
|
+
renames.set(declId.name, "App");
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
break;
|
|
1598
|
+
}
|
|
1599
|
+
if (!ancestor.parentPath)
|
|
1600
|
+
break;
|
|
1601
|
+
ancestor = ancestor.parentPath;
|
|
1602
|
+
}
|
|
1603
|
+
p.skip();
|
|
1604
|
+
},
|
|
1605
|
+
});
|
|
1606
|
+
// Step 5: find the Suspense fallback component
|
|
1607
|
+
traverse(syntheticFile, {
|
|
1608
|
+
JSXAttribute(p) {
|
|
1609
|
+
if (!t.isJSXIdentifier(p.node.name, { name: "fallback" }))
|
|
1610
|
+
return;
|
|
1611
|
+
const val = p.node.value;
|
|
1612
|
+
if (!t.isJSXExpressionContainer(val))
|
|
1613
|
+
return;
|
|
1614
|
+
const expr = val.expression;
|
|
1615
|
+
if (!t.isJSXElement(expr))
|
|
1616
|
+
return;
|
|
1617
|
+
const tagName = expr.openingElement.name;
|
|
1618
|
+
if (!t.isJSXIdentifier(tagName))
|
|
1619
|
+
return;
|
|
1620
|
+
const varName = tagName.name;
|
|
1621
|
+
if (!renames.has(varName) && !lazyVars.has(varName)) {
|
|
1622
|
+
renames.set(varName, "Loading");
|
|
1623
|
+
}
|
|
1624
|
+
},
|
|
1625
|
+
});
|
|
1626
|
+
if (renames.size === 0)
|
|
1627
|
+
return statements;
|
|
1628
|
+
// Step 6: rename bindings via scope analysis, then patch any remaining JSXIdentifiers
|
|
1629
|
+
traverse(syntheticFile, {
|
|
1630
|
+
Program(p) {
|
|
1631
|
+
for (const [oldName, newName] of renames) {
|
|
1632
|
+
if (p.scope.hasBinding(oldName)) {
|
|
1633
|
+
p.scope.rename(oldName, newName);
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
},
|
|
1637
|
+
});
|
|
1638
|
+
// JSXIdentifier nodes may not be tracked by scope — patch them explicitly as a fallback
|
|
1639
|
+
traverse(syntheticFile, {
|
|
1640
|
+
JSXIdentifier(p) {
|
|
1641
|
+
const newName = renames.get(p.node.name);
|
|
1642
|
+
if (newName)
|
|
1643
|
+
p.node.name = newName;
|
|
1644
|
+
},
|
|
1645
|
+
});
|
|
1646
|
+
return statements;
|
|
1647
|
+
};
|
|
1648
|
+
// ---------------------------------------------------------------------------
|
|
695
1649
|
// Main export
|
|
696
1650
|
// ---------------------------------------------------------------------------
|
|
697
1651
|
/**
|
|
@@ -720,13 +1674,68 @@ export const transformIndexStatements = (statements, libModuleMap) => {
|
|
|
720
1674
|
}
|
|
721
1675
|
if (requireFnNames.size === 0)
|
|
722
1676
|
return statements; // nothing to do
|
|
1677
|
+
// Pass A.1 — strip webpack runtime bootstrap statements.
|
|
1678
|
+
// Any statement that references requireFn.X (assignments, calls, or var inits)
|
|
1679
|
+
// is webpack infrastructure. Also strip the runtime temp-var declarations that
|
|
1680
|
+
// have no init / empty-collection init and 3+ declarators.
|
|
1681
|
+
const containsRequireFnMember = (node, fnName) => {
|
|
1682
|
+
if (t.isMemberExpression(node) && t.isIdentifier(node.object, { name: fnName }))
|
|
1683
|
+
return true;
|
|
1684
|
+
for (const key of Object.keys(node)) {
|
|
1685
|
+
const child = node[key];
|
|
1686
|
+
if (child && typeof child === "object") {
|
|
1687
|
+
if (Array.isArray(child)) {
|
|
1688
|
+
for (const item of child) {
|
|
1689
|
+
if (item &&
|
|
1690
|
+
typeof item.type === "string" &&
|
|
1691
|
+
containsRequireFnMember(item, fnName))
|
|
1692
|
+
return true;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
else if (typeof child.type === "string") {
|
|
1696
|
+
if (containsRequireFnMember(child, fnName))
|
|
1697
|
+
return true;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
return false;
|
|
1702
|
+
};
|
|
1703
|
+
const isRuntimeTempVarDecl = (stmt) => {
|
|
1704
|
+
if (!t.isVariableDeclaration(stmt) || stmt.declarations.length < 3)
|
|
1705
|
+
return false;
|
|
1706
|
+
return stmt.declarations.every((decl) => {
|
|
1707
|
+
if (!decl.init)
|
|
1708
|
+
return true;
|
|
1709
|
+
if (t.isObjectExpression(decl.init) && decl.init.properties.length === 0)
|
|
1710
|
+
return true;
|
|
1711
|
+
if (t.isArrayExpression(decl.init) && decl.init.elements.length === 0)
|
|
1712
|
+
return true;
|
|
1713
|
+
if (t.isUnaryExpression(decl.init) && decl.init.operator === "void")
|
|
1714
|
+
return true;
|
|
1715
|
+
return false;
|
|
1716
|
+
});
|
|
1717
|
+
};
|
|
1718
|
+
const afterA1 = [];
|
|
1719
|
+
for (const stmt of afterA) {
|
|
1720
|
+
let isRuntime = isRuntimeTempVarDecl(stmt);
|
|
1721
|
+
if (!isRuntime) {
|
|
1722
|
+
for (const fnName of requireFnNames) {
|
|
1723
|
+
if (containsRequireFnMember(stmt, fnName)) {
|
|
1724
|
+
isRuntime = true;
|
|
1725
|
+
break;
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
if (!isRuntime)
|
|
1730
|
+
afterA1.push(stmt);
|
|
1731
|
+
}
|
|
723
1732
|
const hoistedImports = new Map();
|
|
724
1733
|
const importNameByNumId = new Map();
|
|
725
1734
|
// Pass B — hoist top-level `var x = requireFn(N)` to static imports.
|
|
726
1735
|
// Also build varName → moduleId so Pass D can look up library identity.
|
|
727
1736
|
const varToModuleId = new Map(); // localVarName → numericModuleId
|
|
728
1737
|
const afterB = [];
|
|
729
|
-
for (const stmt of
|
|
1738
|
+
for (const stmt of afterA1) {
|
|
730
1739
|
if (!t.isVariableDeclaration(stmt)) {
|
|
731
1740
|
afterB.push(stmt);
|
|
732
1741
|
continue;
|
|
@@ -830,7 +1839,10 @@ export const transformIndexStatements = (statements, libModuleMap) => {
|
|
|
830
1839
|
const afterG = dropEmptyObjectVars(afterE.filter((stmt) => !isBabelArrayLikeToArrayHelper(stmt) &&
|
|
831
1840
|
!isBabelTypeofHelper(stmt) &&
|
|
832
1841
|
!isBabelDefinePropertyHelper(stmt) &&
|
|
833
|
-
!isBabelObjectSpreadHelper(stmt)
|
|
1842
|
+
!isBabelObjectSpreadHelper(stmt) &&
|
|
1843
|
+
!isBabelObjectSpread2Helper(stmt) &&
|
|
1844
|
+
!isBabelOwnKeysHelper(stmt) &&
|
|
1845
|
+
!isNamedBabelArrayHelper(stmt)));
|
|
834
1846
|
// Pass H — prune named imports whose local name is no longer referenced
|
|
835
1847
|
// (e.g. jsx/jsxs after JSX recovery, namespace imports that were cleared).
|
|
836
1848
|
const prunedImports = pruneUnusedNamedImports(finalImportStmts, afterG);
|