phantom-build 0.1.0

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.
Files changed (94) hide show
  1. package/README.md +378 -0
  2. package/dist/analyzer.d.ts +11 -0
  3. package/dist/analyzer.d.ts.map +1 -0
  4. package/dist/analyzer.js +330 -0
  5. package/dist/analyzer.js.map +1 -0
  6. package/dist/ast-compat.d.ts +11 -0
  7. package/dist/ast-compat.d.ts.map +1 -0
  8. package/dist/ast-compat.js +84 -0
  9. package/dist/ast-compat.js.map +1 -0
  10. package/dist/classify/boundary.d.ts +30 -0
  11. package/dist/classify/boundary.d.ts.map +1 -0
  12. package/dist/classify/boundary.js +145 -0
  13. package/dist/classify/boundary.js.map +1 -0
  14. package/dist/classify/browser-globals.d.ts +29 -0
  15. package/dist/classify/browser-globals.d.ts.map +1 -0
  16. package/dist/classify/browser-globals.js +197 -0
  17. package/dist/classify/browser-globals.js.map +1 -0
  18. package/dist/classify/index.d.ts +14 -0
  19. package/dist/classify/index.d.ts.map +1 -0
  20. package/dist/classify/index.js +294 -0
  21. package/dist/classify/index.js.map +1 -0
  22. package/dist/classify/lazy-llm.d.ts +122 -0
  23. package/dist/classify/lazy-llm.d.ts.map +1 -0
  24. package/dist/classify/lazy-llm.js +142 -0
  25. package/dist/classify/lazy-llm.js.map +1 -0
  26. package/dist/classify/lazy.d.ts +23 -0
  27. package/dist/classify/lazy.d.ts.map +1 -0
  28. package/dist/classify/lazy.js +686 -0
  29. package/dist/classify/lazy.js.map +1 -0
  30. package/dist/classify/llm-client.d.ts +59 -0
  31. package/dist/classify/llm-client.d.ts.map +1 -0
  32. package/dist/classify/llm-client.js +193 -0
  33. package/dist/classify/llm-client.js.map +1 -0
  34. package/dist/classify/purity.d.ts +21 -0
  35. package/dist/classify/purity.d.ts.map +1 -0
  36. package/dist/classify/purity.js +47 -0
  37. package/dist/classify/purity.js.map +1 -0
  38. package/dist/classify/react-patterns.d.ts +15 -0
  39. package/dist/classify/react-patterns.d.ts.map +1 -0
  40. package/dist/classify/react-patterns.js +82 -0
  41. package/dist/classify/react-patterns.js.map +1 -0
  42. package/dist/classify/taint.d.ts +32 -0
  43. package/dist/classify/taint.d.ts.map +1 -0
  44. package/dist/classify/taint.js +68 -0
  45. package/dist/classify/taint.js.map +1 -0
  46. package/dist/cli.d.ts +3 -0
  47. package/dist/cli.d.ts.map +1 -0
  48. package/dist/cli.js +109 -0
  49. package/dist/cli.js.map +1 -0
  50. package/dist/extract/chunk-module.d.ts +20 -0
  51. package/dist/extract/chunk-module.d.ts.map +1 -0
  52. package/dist/extract/chunk-module.js +163 -0
  53. package/dist/extract/chunk-module.js.map +1 -0
  54. package/dist/extract/client-stub.d.ts +25 -0
  55. package/dist/extract/client-stub.d.ts.map +1 -0
  56. package/dist/extract/client-stub.js +233 -0
  57. package/dist/extract/client-stub.js.map +1 -0
  58. package/dist/extract/import-resolver.d.ts +20 -0
  59. package/dist/extract/import-resolver.d.ts.map +1 -0
  60. package/dist/extract/import-resolver.js +51 -0
  61. package/dist/extract/import-resolver.js.map +1 -0
  62. package/dist/extract/index.d.ts +20 -0
  63. package/dist/extract/index.d.ts.map +1 -0
  64. package/dist/extract/index.js +105 -0
  65. package/dist/extract/index.js.map +1 -0
  66. package/dist/extract/lazy-transform.d.ts +14 -0
  67. package/dist/extract/lazy-transform.d.ts.map +1 -0
  68. package/dist/extract/lazy-transform.js +473 -0
  69. package/dist/extract/lazy-transform.js.map +1 -0
  70. package/dist/index.d.ts +4 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +3 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/plugin.d.ts +7 -0
  75. package/dist/plugin.d.ts.map +1 -0
  76. package/dist/plugin.js +535 -0
  77. package/dist/plugin.js.map +1 -0
  78. package/dist/runtime/index.d.ts +28 -0
  79. package/dist/runtime/index.d.ts.map +1 -0
  80. package/dist/runtime/index.js +73 -0
  81. package/dist/runtime/index.js.map +1 -0
  82. package/dist/types.d.ts +219 -0
  83. package/dist/types.d.ts.map +1 -0
  84. package/dist/types.js +2 -0
  85. package/dist/types.js.map +1 -0
  86. package/dist/vite.d.ts +3 -0
  87. package/dist/vite.d.ts.map +1 -0
  88. package/dist/vite.js +3 -0
  89. package/dist/vite.js.map +1 -0
  90. package/dist/webpack.d.ts +3 -0
  91. package/dist/webpack.d.ts.map +1 -0
  92. package/dist/webpack.js +3 -0
  93. package/dist/webpack.js.map +1 -0
  94. package/package.json +79 -0
package/dist/cli.js ADDED
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+ import { analyzeModule } from './analyzer.js';
5
+ // ── Argument parsing ─────────────────────────────────────────────────
6
+ function printUsage() {
7
+ console.log(`
8
+ Usage: phantom analyze <file> [options]
9
+
10
+ Analyze a single file and print segment classification details.
11
+
12
+ Options:
13
+ --threshold <number> Confidence threshold for extraction (default: 0.8)
14
+ --help, -h Show this help message
15
+ `.trim());
16
+ }
17
+ function main() {
18
+ const args = process.argv.slice(2);
19
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
20
+ printUsage();
21
+ process.exit(args.length === 0 ? 1 : 0);
22
+ }
23
+ const command = args[0];
24
+ if (command !== 'analyze') {
25
+ console.error(`Unknown command: ${command}`);
26
+ printUsage();
27
+ process.exit(1);
28
+ }
29
+ const filePath = args[1];
30
+ if (!filePath) {
31
+ console.error('Error: No file specified');
32
+ printUsage();
33
+ process.exit(1);
34
+ }
35
+ // Parse --threshold flag
36
+ let threshold = 0.8;
37
+ const thresholdIdx = args.indexOf('--threshold');
38
+ if (thresholdIdx !== -1) {
39
+ const val = parseFloat(args[thresholdIdx + 1]);
40
+ if (isNaN(val) || val < 0 || val > 1) {
41
+ console.error('Error: --threshold must be a number between 0 and 1');
42
+ process.exit(1);
43
+ }
44
+ threshold = val;
45
+ }
46
+ // Read and analyze the file
47
+ const absolutePath = resolve(filePath);
48
+ let code;
49
+ try {
50
+ code = readFileSync(absolutePath, 'utf-8');
51
+ }
52
+ catch {
53
+ console.error(`Error: Cannot read file "${absolutePath}"`);
54
+ process.exit(1);
55
+ }
56
+ const result = analyzeModule(code, absolutePath, { confidenceThreshold: threshold });
57
+ // ── Output ───────────────────────────────────────────────────────
58
+ console.log(`\nPhantom Analysis: ${filePath}`);
59
+ console.log('═'.repeat(60));
60
+ if (result.segments.length === 0) {
61
+ console.log('No classifiable segments found.');
62
+ return;
63
+ }
64
+ // Segment table
65
+ console.log(`\n ${'Name'.padEnd(30)} ${'Class'.padEnd(20)} ${'Conf'.padEnd(6)} Extracted?`);
66
+ console.log(` ${'─'.repeat(30)} ${'─'.repeat(20)} ${'─'.repeat(6)} ${'─'.repeat(10)}`);
67
+ for (const seg of result.segments) {
68
+ const extracted = result.chunkModules?.some((m) => m.id === seg.id) ? '✓ yes' : ' no';
69
+ console.log(` ${seg.name.padEnd(30)} ${seg.classification.padEnd(20)} ${seg.confidence.toFixed(2).padStart(5)} ${extracted}`);
70
+ if (seg.reasons.length > 0) {
71
+ for (const reason of seg.reasons) {
72
+ console.log(` → ${reason}`);
73
+ }
74
+ }
75
+ }
76
+ // Summary
77
+ console.log('');
78
+ console.log(` Segments: ${result.segments.length}`);
79
+ console.log(` Threshold: ${threshold}`);
80
+ if (result.chunkModules && result.chunkModules.length > 0) {
81
+ console.log(` Chunks extracted: ${result.chunkModules.length}`);
82
+ for (const mod of result.chunkModules) {
83
+ const sizeKb = (Buffer.byteLength(mod.code, 'utf-8') / 1024).toFixed(1);
84
+ console.log(` ${mod.id} (${sizeKb} KB)`);
85
+ }
86
+ }
87
+ else {
88
+ console.log(' Chunks extracted: 0');
89
+ }
90
+ // Lazy component candidates
91
+ if (result.lazyCandidates && result.lazyCandidates.length > 0) {
92
+ console.log(`\n Lazy Components:`);
93
+ console.log(` ${'Name'.padEnd(25)} ${'Strategy'.padEnd(12)} Group`);
94
+ console.log(` ${'─'.repeat(25)} ${'─'.repeat(12)} ${'─'.repeat(15)}`);
95
+ for (const lc of result.lazyCandidates) {
96
+ console.log(` ${lc.localName.padEnd(25)} ${lc.prefetch.padEnd(12)} ${lc.suspenseGroup ?? '(solo)'}`);
97
+ console.log(` → ${lc.reason}`);
98
+ }
99
+ }
100
+ if (result.lazyKeptStatic && result.lazyKeptStatic.length > 0) {
101
+ console.log(`\n Kept Static:`);
102
+ for (const ks of result.lazyKeptStatic) {
103
+ console.log(` ${ks.localName.padEnd(25)} → ${ks.reason}`);
104
+ }
105
+ }
106
+ console.log('');
107
+ }
108
+ main();
109
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { ClassifiedSegment, ImportInfo, SourceMapLike } from '../types.js';
2
+ import type { ExtractableNode } from './client-stub.js';
3
+ /**
4
+ * Generate a lazy-loaded chunk module that exports the extracted function.
5
+ *
6
+ * Output shape:
7
+ *
8
+ * import { someUtil } from './utils';
9
+ *
10
+ * export function seg_abc123(e) {
11
+ * e.preventDefault();
12
+ * const form = e.target;
13
+ * // ...handler logic
14
+ * }
15
+ */
16
+ export declare function generateChunkModule(segment: ClassifiedSegment, astNode: ExtractableNode, imports: ImportInfo[], capturedParams: string[], sourceFilePath: string, sourceCode: string): {
17
+ code: string;
18
+ map: SourceMapLike;
19
+ };
20
+ //# sourceMappingURL=chunk-module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-module.d.ts","sourceRoot":"","sources":["../../src/extract/chunk-module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAyFxD;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,UAAU,EAAE,EACrB,cAAc,EAAE,MAAM,EAAE,EACxB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,GACjB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,aAAa,CAAA;CAAE,CA0FtC"}
@@ -0,0 +1,163 @@
1
+ import { print } from 'esrap';
2
+ import tsx from 'esrap/languages/tsx';
3
+ // ── TypeScript stripping ────────────────────────────────────────────────
4
+ /**
5
+ * TS wrapper node types that should be unwrapped to their inner expression.
6
+ * These nodes exist in OXC's AST when parsing TypeScript but have no JS equivalent.
7
+ */
8
+ const TS_WRAPPER_TYPES = new Set([
9
+ 'TSAsExpression', // `x as Type`
10
+ 'TSSatisfiesExpression', // `x satisfies Type`
11
+ 'TSNonNullExpression', // `x!`
12
+ 'TSTypeAssertion', // `<Type>x`
13
+ 'TSInstantiationExpression', // `fn<Type>`
14
+ ]);
15
+ /**
16
+ * Properties that carry TypeScript type information and should be deleted.
17
+ */
18
+ const TS_ANNOTATION_PROPS = ['typeAnnotation', 'returnType', 'typeParameters', 'typeArguments'];
19
+ /**
20
+ * Recursively strip TypeScript-specific nodes from a cloned ESTree AST.
21
+ *
22
+ * This ensures chunk modules are valid JavaScript even when the source
23
+ * contained TypeScript annotations (e.g., `e.target as HTMLFormElement`).
24
+ *
25
+ * Mutates the tree in place and returns it.
26
+ */
27
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
+ function stripTypeAnnotations(node) {
29
+ if (node == null || typeof node !== 'object')
30
+ return node;
31
+ // Handle arrays (e.g., body[], params[])
32
+ if (Array.isArray(node)) {
33
+ for (let i = 0; i < node.length; i++) {
34
+ node[i] = stripTypeAnnotations(node[i]);
35
+ }
36
+ return node;
37
+ }
38
+ // Skip non-AST objects (Literal values, etc.)
39
+ if (!node.type)
40
+ return node;
41
+ // Unwrap TS wrapper nodes to their inner expression
42
+ if (TS_WRAPPER_TYPES.has(node.type)) {
43
+ return stripTypeAnnotations(node.expression);
44
+ }
45
+ // Delete TS annotation properties
46
+ for (const prop of TS_ANNOTATION_PROPS) {
47
+ if (prop in node) {
48
+ delete node[prop];
49
+ }
50
+ }
51
+ // Strip TS-only statement types entirely (type aliases, interfaces, enums, etc.)
52
+ // These shouldn't appear in function bodies, but guard against it
53
+ if (node.type.startsWith('TS') && node.type !== 'TSModuleBlock') {
54
+ return node;
55
+ }
56
+ // Recurse into all properties
57
+ for (const key of Object.keys(node)) {
58
+ if (key === 'type' || key === 'start' || key === 'end')
59
+ continue;
60
+ const val = node[key];
61
+ if (val && typeof val === 'object') {
62
+ node[key] = stripTypeAnnotations(val);
63
+ }
64
+ }
65
+ return node;
66
+ }
67
+ // ── Chunk module generation ─────────────────────────────────────────────
68
+ /**
69
+ * Generate a lazy-loaded chunk module that exports the extracted function.
70
+ *
71
+ * Output shape:
72
+ *
73
+ * import { someUtil } from './utils';
74
+ *
75
+ * export function seg_abc123(e) {
76
+ * e.preventDefault();
77
+ * const form = e.target;
78
+ * // ...handler logic
79
+ * }
80
+ */
81
+ export function generateChunkModule(segment, astNode, imports, capturedParams, sourceFilePath, sourceCode) {
82
+ const body = [];
83
+ // 1. Import declarations
84
+ for (const imp of imports) {
85
+ const specifiers = imp.specifiers.map((spec) => {
86
+ if (spec.kind === 'default') {
87
+ return {
88
+ type: 'ImportDefaultSpecifier',
89
+ local: { type: 'Identifier', name: spec.local },
90
+ };
91
+ }
92
+ if (spec.kind === 'namespace') {
93
+ return {
94
+ type: 'ImportNamespaceSpecifier',
95
+ local: { type: 'Identifier', name: spec.local },
96
+ };
97
+ }
98
+ return {
99
+ type: 'ImportSpecifier',
100
+ imported: { type: 'Identifier', name: spec.imported ?? spec.local },
101
+ local: { type: 'Identifier', name: spec.local },
102
+ };
103
+ });
104
+ const decl = {
105
+ type: 'ImportDeclaration',
106
+ specifiers,
107
+ source: { type: 'Literal', value: imp.source },
108
+ };
109
+ body.push(decl);
110
+ }
111
+ // 2. Exported function with original params + captured params
112
+ // Original function params come first, then captured variables from outer scope.
113
+ // Strip TypeScript annotations so the generated chunk is valid JavaScript.
114
+ const originalParams = stripTypeAnnotations(structuredClone(astNode.params));
115
+ const capturedIdentifiers = capturedParams.map((name) => ({
116
+ type: 'Identifier',
117
+ name,
118
+ }));
119
+ const params = [...originalParams, ...capturedIdentifiers];
120
+ let fnBody;
121
+ if (astNode.type === 'ArrowFunctionExpression' && astNode.body.type !== 'BlockStatement') {
122
+ // Expression body: () => expr → { return expr; }
123
+ fnBody = stripTypeAnnotations({
124
+ type: 'BlockStatement',
125
+ body: [{ type: 'ReturnStatement', argument: structuredClone(astNode.body) }],
126
+ });
127
+ }
128
+ else if (astNode.type === 'ArrowFunctionExpression' || astNode.type === 'FunctionExpression') {
129
+ fnBody = stripTypeAnnotations(structuredClone(astNode.body));
130
+ }
131
+ else {
132
+ fnBody = stripTypeAnnotations(structuredClone(astNode.body));
133
+ }
134
+ const funcDecl = {
135
+ type: 'FunctionDeclaration',
136
+ id: { type: 'Identifier', name: segment.id },
137
+ params,
138
+ body: fnBody,
139
+ async: astNode.async ?? false,
140
+ generator: astNode.generator ?? false,
141
+ };
142
+ const exportDecl = {
143
+ type: 'ExportNamedDeclaration',
144
+ declaration: funcDecl,
145
+ specifiers: [],
146
+ source: null,
147
+ attributes: [],
148
+ };
149
+ body.push(exportDecl);
150
+ // 3. Generate code with esrap
151
+ const program = {
152
+ type: 'Program',
153
+ sourceType: 'module',
154
+ body,
155
+ };
156
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- esrap tsx visitors expect TSESTree.Node, our estree is compatible at runtime
157
+ const result = print(program, tsx(), {
158
+ sourceMapSource: sourceFilePath,
159
+ sourceMapContent: sourceCode,
160
+ });
161
+ return { code: result.code, map: result.map };
162
+ }
163
+ //# sourceMappingURL=chunk-module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-module.js","sourceRoot":"","sources":["../../src/extract/chunk-module.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { ClassifiedSegment } from '../types.js';
2
+ import type { ArrowFunctionExpression, FunctionDeclaration, FunctionExpression } from 'estree';
3
+ export type ExtractableNode = ArrowFunctionExpression | FunctionExpression | FunctionDeclaration;
4
+ /**
5
+ * Replace the function body with a __phantom_lazy() call + synchronous prelude.
6
+ *
7
+ * Mutates the AST node in place.
8
+ *
9
+ * If the original handler calls preventDefault()/stopPropagation() on its
10
+ * first param, those calls are hoisted into the synchronous stub so they
11
+ * execute immediately during event dispatch (before the async import).
12
+ *
13
+ * Output shapes:
14
+ *
15
+ * Arrow (no prelude):
16
+ * (e) => __phantom_lazy('seg_xxx', e, ...captured)
17
+ *
18
+ * Arrow (with prelude):
19
+ * (e) => { e.preventDefault(); e.persist?.(); __phantom_lazy('seg_xxx', e, ...captured); }
20
+ *
21
+ * FunctionExpression / FunctionDeclaration (always block body):
22
+ * function f(e) { e.preventDefault(); e.persist?.(); __phantom_lazy('seg_xxx', e, ...captured); }
23
+ */
24
+ export declare function replaceWithStub(astNode: ExtractableNode, segment: ClassifiedSegment, capturedParams: string[]): void;
25
+ //# sourceMappingURL=client-stub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-stub.d.ts","sourceRoot":"","sources":["../../src/extract/client-stub.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EACV,uBAAuB,EAIvB,mBAAmB,EACnB,kBAAkB,EAMnB,MAAM,QAAQ,CAAC;AAEhB,MAAM,MAAM,eAAe,GACvB,uBAAuB,GACvB,kBAAkB,GAClB,mBAAmB,CAAC;AAgIxB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,iBAAiB,EAC1B,cAAc,EAAE,MAAM,EAAE,GACvB,IAAI,CAmFN"}
@@ -0,0 +1,233 @@
1
+ // ── Synchronous prelude detection ───────────────────────────────────────
2
+ /**
3
+ * Event methods that must run synchronously during event dispatch.
4
+ * If the handler calls these on its first parameter, the stub must
5
+ * call them BEFORE the async __phantom_lazy() invocation.
6
+ */
7
+ const SYNC_EVENT_METHODS = new Set([
8
+ 'preventDefault',
9
+ 'stopPropagation',
10
+ 'stopImmediatePropagation',
11
+ ]);
12
+ /**
13
+ * Detect synchronous event method calls in the handler body.
14
+ *
15
+ * Walks the AST looking for `e.preventDefault()`, `e.stopPropagation()`, etc.
16
+ * where `e` is the first parameter of the function.
17
+ *
18
+ * Returns deduplicated list of method names that need to be hoisted.
19
+ */
20
+ function detectSyncEventCalls(astNode) {
21
+ const firstParam = astNode.params[0];
22
+ if (!firstParam)
23
+ return [];
24
+ // Extract the param name from Identifier or AssignmentPattern (default value)
25
+ // e.g., (e) => ... or (e = defaultEvent) => ...
26
+ // Destructured params ({target}) can't call e.preventDefault() — skip them.
27
+ let paramName = null;
28
+ if (firstParam.type === 'Identifier') {
29
+ paramName = firstParam.name;
30
+ }
31
+ else if (firstParam.type === 'AssignmentPattern' && firstParam.left.type === 'Identifier') {
32
+ paramName = firstParam.left.name;
33
+ }
34
+ if (!paramName)
35
+ return [];
36
+ const found = new Set();
37
+ walkNode(astNode.body, (node) => {
38
+ if (node.type !== 'CallExpression')
39
+ return;
40
+ const callee = node.callee;
41
+ if (callee.type !== 'MemberExpression')
42
+ return;
43
+ const member = callee;
44
+ if (member.object.type !== 'Identifier' || member.object.name !== paramName)
45
+ return;
46
+ if (member.property.type !== 'Identifier')
47
+ return;
48
+ const methodName = member.property.name;
49
+ if (SYNC_EVENT_METHODS.has(methodName)) {
50
+ found.add(methodName);
51
+ }
52
+ });
53
+ return [...found];
54
+ }
55
+ /**
56
+ * Build AST nodes for the synchronous prelude.
57
+ *
58
+ * Generates:
59
+ * e.preventDefault(); // only if detected
60
+ * e.stopPropagation(); // only if detected
61
+ * e.persist?.(); // always when handler has event param
62
+ */
63
+ function buildPrelude(paramName, syncMethods) {
64
+ const stmts = [];
65
+ // Hoist detected sync methods
66
+ for (const method of syncMethods) {
67
+ stmts.push({
68
+ type: 'ExpressionStatement',
69
+ expression: {
70
+ type: 'CallExpression',
71
+ callee: {
72
+ type: 'MemberExpression',
73
+ object: { type: 'Identifier', name: paramName },
74
+ property: { type: 'Identifier', name: method },
75
+ computed: false,
76
+ optional: false,
77
+ },
78
+ arguments: [],
79
+ optional: false,
80
+ },
81
+ });
82
+ }
83
+ // Always add e.persist?.() for React <17 SyntheticEvent compat
84
+ stmts.push({
85
+ type: 'ExpressionStatement',
86
+ expression: {
87
+ type: 'CallExpression',
88
+ callee: {
89
+ type: 'MemberExpression',
90
+ object: { type: 'Identifier', name: paramName },
91
+ property: { type: 'Identifier', name: 'persist' },
92
+ computed: false,
93
+ optional: false,
94
+ },
95
+ arguments: [],
96
+ optional: true, // e.persist?.() — optional chaining
97
+ },
98
+ });
99
+ return stmts;
100
+ }
101
+ // ── Param name extraction ───────────────────────────────────────────────
102
+ /**
103
+ * Extract parameter names from the original function's params.
104
+ * Handles simple Identifier params and destructuring patterns (uses rest name or fallback).
105
+ */
106
+ function extractParamNames(params) {
107
+ return params.map((p, i) => {
108
+ if (p.type === 'Identifier')
109
+ return p.name;
110
+ // For destructured/rest/default patterns, use the pattern directly
111
+ // The chunk module gets the original params via structuredClone
112
+ if (p.type === 'AssignmentPattern' && p.left.type === 'Identifier')
113
+ return p.left.name;
114
+ if (p.type === 'RestElement' && p.argument.type === 'Identifier')
115
+ return p.argument.name;
116
+ return `__arg${i}`;
117
+ });
118
+ }
119
+ // ── Stub generation ─────────────────────────────────────────────────────
120
+ /**
121
+ * Replace the function body with a __phantom_lazy() call + synchronous prelude.
122
+ *
123
+ * Mutates the AST node in place.
124
+ *
125
+ * If the original handler calls preventDefault()/stopPropagation() on its
126
+ * first param, those calls are hoisted into the synchronous stub so they
127
+ * execute immediately during event dispatch (before the async import).
128
+ *
129
+ * Output shapes:
130
+ *
131
+ * Arrow (no prelude):
132
+ * (e) => __phantom_lazy('seg_xxx', e, ...captured)
133
+ *
134
+ * Arrow (with prelude):
135
+ * (e) => { e.preventDefault(); e.persist?.(); __phantom_lazy('seg_xxx', e, ...captured); }
136
+ *
137
+ * FunctionExpression / FunctionDeclaration (always block body):
138
+ * function f(e) { e.preventDefault(); e.persist?.(); __phantom_lazy('seg_xxx', e, ...captured); }
139
+ */
140
+ export function replaceWithStub(astNode, segment, capturedParams) {
141
+ const originalParamNames = extractParamNames(astNode.params);
142
+ // Detect sync event calls BEFORE mutating the node
143
+ const syncMethods = detectSyncEventCalls(astNode);
144
+ // Build the import factory: () => import('phantom:seg_xxx.chunk.js')
145
+ // This MUST be a static dynamic import so Rollup/Vite can analyze it
146
+ // and emit the chunk as a separate file for code-splitting.
147
+ const importFactory = {
148
+ type: 'ArrowFunctionExpression',
149
+ params: [],
150
+ body: {
151
+ type: 'ImportExpression',
152
+ source: { type: 'Literal', value: `phantom:${segment.id}.chunk.js` },
153
+ }, // ImportExpression is ESTree but not in estree types
154
+ expression: true,
155
+ async: false,
156
+ generator: false,
157
+ };
158
+ const lazyCall = {
159
+ type: 'CallExpression',
160
+ callee: { type: 'Identifier', name: '__phantom_lazy' },
161
+ arguments: [
162
+ importFactory, // factory is first arg
163
+ { type: 'Literal', value: segment.id },
164
+ ...originalParamNames.map((name) => ({ type: 'Identifier', name })),
165
+ ...capturedParams.map((name) => ({ type: 'Identifier', name })),
166
+ ],
167
+ optional: false,
168
+ };
169
+ // Build simple Identifier params for the stub (original param names only)
170
+ const stubParams = originalParamNames.map((name) => ({
171
+ type: 'Identifier',
172
+ name,
173
+ }));
174
+ // Build synchronous prelude. When the handler has an event parameter,
175
+ // we always add e.persist?.() for React <17 SyntheticEvent compat,
176
+ // even if no sync methods (preventDefault, etc.) were detected.
177
+ const firstParamName = originalParamNames[0];
178
+ const hasPrelude = !!firstParamName;
179
+ const preludeStmts = hasPrelude
180
+ ? buildPrelude(firstParamName, syncMethods)
181
+ : [];
182
+ // The lazy call as a statement (event handlers return void)
183
+ const lazyStmt = {
184
+ type: 'ExpressionStatement',
185
+ expression: lazyCall,
186
+ };
187
+ if (hasPrelude || astNode.type === 'FunctionExpression' || astNode.type === 'FunctionDeclaration') {
188
+ // Block body: { prelude...; __phantom_lazy(...); }
189
+ const blockBody = {
190
+ type: 'BlockStatement',
191
+ body: [...preludeStmts, lazyStmt],
192
+ };
193
+ astNode.body = blockBody;
194
+ astNode.params = stubParams;
195
+ // Fix ESTree `expression` property for arrows converted to block body
196
+ if (astNode.type === 'ArrowFunctionExpression') {
197
+ astNode.expression = false;
198
+ }
199
+ // Strip TS-specific properties that survive from the original node
200
+ // (the client code is TSX so esrap handles them, but clean up for correctness)
201
+ if ('returnType' in astNode)
202
+ delete astNode.returnType;
203
+ }
204
+ else {
205
+ // ArrowFunctionExpression without prelude: expression body
206
+ // (e) => __phantom_lazy(...)
207
+ astNode.body = lazyCall;
208
+ astNode.expression = true;
209
+ astNode.params = stubParams;
210
+ if ('returnType' in astNode)
211
+ delete astNode.returnType;
212
+ }
213
+ }
214
+ // ── AST walker ──────────────────────────────────────────────────────────
215
+ function walkNode(node, callback) {
216
+ if (!node || typeof node !== 'object')
217
+ return;
218
+ if (Array.isArray(node)) {
219
+ for (const child of node)
220
+ walkNode(child, callback);
221
+ return;
222
+ }
223
+ const obj = node;
224
+ if (typeof obj.type !== 'string')
225
+ return;
226
+ callback(obj);
227
+ for (const key of Object.keys(obj)) {
228
+ if (key === 'type')
229
+ continue;
230
+ walkNode(obj[key], callback);
231
+ }
232
+ }
233
+ //# sourceMappingURL=client-stub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-stub.js","sourceRoot":"","sources":["../../src/extract/client-stub.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { ClassifiedSegment, AnalyzedModule, ImportInfo } from '../types.js';
2
+ export interface ImportResolution {
3
+ /** Import statements needed in the chunk module */
4
+ imports: ImportInfo[];
5
+ /** Captured variables that become function parameters */
6
+ capturedParams: string[];
7
+ }
8
+ /**
9
+ * Resolve what the extracted segment needs: imports vs captured variables.
10
+ *
11
+ * ClassifiedSegment.dependencies merges fn.captured + fn.imported.
12
+ * We recover the split by matching back to the FunctionDependency via span,
13
+ * then cross-reference imported names against AnalyzedModule.imports.
14
+ *
15
+ * Relative import paths (./foo, ../bar) are rewritten to absolute paths
16
+ * so that chunk virtual modules (which have no filesystem location) can
17
+ * resolve their dependencies correctly.
18
+ */
19
+ export declare function resolveImports(segment: ClassifiedSegment, analyzed: AnalyzedModule, sourceFilePath: string): ImportResolution;
20
+ //# sourceMappingURL=import-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-resolver.d.ts","sourceRoot":"","sources":["../../src/extract/import-resolver.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEjF,MAAM,WAAW,gBAAgB;IAC/B,mDAAmD;IACnD,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,yDAAyD;IACzD,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE,cAAc,EACxB,cAAc,EAAE,MAAM,GACrB,gBAAgB,CAsClB"}