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
@@ -0,0 +1,330 @@
1
+ import { parseSync } from 'oxc-parser';
2
+ import { analyze as analyzeScope } from 'eslint-scope';
3
+ import { addASTMetadata } from './ast-compat.js';
4
+ import { classifyModule, detectLazyCandidates } from './classify/index.js';
5
+ import { extractModule } from './extract/index.js';
6
+ /**
7
+ * Determine the OXC parser `lang` option from a file path.
8
+ */
9
+ function getLang(path) {
10
+ if (path.endsWith('.tsx'))
11
+ return 'tsx';
12
+ if (path.endsWith('.ts'))
13
+ return 'ts';
14
+ if (path.endsWith('.jsx'))
15
+ return 'jsx';
16
+ return 'js';
17
+ }
18
+ /**
19
+ * Parse a module and perform scope analysis.
20
+ * Returns the AST, function dependency info, and import info.
21
+ */
22
+ export function parseModule(code, path) {
23
+ // 1. Parse with OXC
24
+ const parseResult = parseSync(path, code, {
25
+ lang: getLang(path),
26
+ sourceType: 'module',
27
+ astType: 'js', // ESTree-compatible output (strips TS-specific nodes)
28
+ });
29
+ // Bail early on parse errors — partial ASTs cause cryptic downstream failures
30
+ if (parseResult.errors.length > 0) {
31
+ const errMsg = parseResult.errors.map((e) => e.message).join(', ');
32
+ throw new Error(`[phantom] Parse errors in ${path}: ${errMsg}`);
33
+ }
34
+ const ast = parseResult.program;
35
+ // 2. Patch AST for eslint-scope + esrap (add range + loc)
36
+ addASTMetadata(ast, code);
37
+ // 3. Run eslint-scope for scope/symbol resolution
38
+ // eslint-scope supports `jsx` but the type definitions don't declare it
39
+ const scopeManager = analyzeScope(ast, {
40
+ ecmaVersion: 2022,
41
+ sourceType: 'module',
42
+ jsx: true,
43
+ fallback: 'iteration',
44
+ });
45
+ // 4. Extract import information from OXC's module info
46
+ const imports = extractImports(parseResult);
47
+ // 5. Extract re-export mappings (for barrel file resolution)
48
+ const reExports = extractReExports(parseResult);
49
+ // 6. Build function dependency map from scope analysis
50
+ const functions = buildFunctionDependencies(scopeManager);
51
+ // 7. Resolve names for anonymous functions from assignment context
52
+ resolveAnonymousNames(ast, functions);
53
+ return { path, ast, functions, imports, reExports };
54
+ }
55
+ /**
56
+ * Extract import information from OXC's parsed module info.
57
+ */
58
+ function extractImports(parseResult) {
59
+ const moduleInfo = parseResult.module;
60
+ if (!moduleInfo)
61
+ return [];
62
+ return moduleInfo.staticImports.map((imp) => ({
63
+ source: imp.moduleRequest.value,
64
+ specifiers: imp.entries.map((entry) => ({
65
+ local: entry.localName.value,
66
+ imported: entry.importName.kind === 'Name'
67
+ ? (entry.importName.name ?? null)
68
+ : null,
69
+ kind: entry.importName.kind === 'Default'
70
+ ? 'default'
71
+ : entry.importName.kind === 'NamespaceObject'
72
+ ? 'namespace'
73
+ : 'named',
74
+ })),
75
+ }));
76
+ }
77
+ /**
78
+ * Extract re-export mappings from OXC's module info.
79
+ * Detects `export { X } from './X'` and `export { default as Y } from './Y'` patterns.
80
+ * These are pass-through re-exports with no local binding (barrel files).
81
+ */
82
+ function extractReExports(parseResult) {
83
+ const moduleInfo = parseResult.module;
84
+ if (!moduleInfo)
85
+ return [];
86
+ const results = [];
87
+ for (const exp of moduleInfo.staticExports) {
88
+ for (const entry of exp.entries) {
89
+ // Skip type-only re-exports
90
+ if (entry.isType)
91
+ continue;
92
+ // Only re-exports have localName.kind === 'None' (no local binding)
93
+ if (entry.localName.kind !== 'None')
94
+ continue;
95
+ // Must have a module source (re-export from another module)
96
+ if (!entry.moduleRequest)
97
+ continue;
98
+ const exportedName = entry.exportName.kind === 'Name' ? entry.exportName.name : null;
99
+ const importedName = entry.importName.kind === 'Name' ? entry.importName.name : null;
100
+ if (!exportedName || !importedName)
101
+ continue;
102
+ results.push({
103
+ exportedName,
104
+ importedName,
105
+ source: entry.moduleRequest.value,
106
+ });
107
+ }
108
+ }
109
+ return results;
110
+ }
111
+ /**
112
+ * Build function dependency information from eslint-scope's analysis.
113
+ *
114
+ * For each function scope, we determine:
115
+ * - locals: variables declared in this function
116
+ * - captured: variables referenced from outer scopes
117
+ * - imported: variables that trace back to import declarations
118
+ * - globals: unresolved references (browser globals, etc.)
119
+ */
120
+ function buildFunctionDependencies(scopeManager) {
121
+ const results = [];
122
+ // Collect all import-declared variable names at module scope
123
+ const importedNames = new Set();
124
+ const moduleScope = scopeManager.scopes.find((s) => s.type === 'module');
125
+ if (moduleScope) {
126
+ for (const variable of moduleScope.variables) {
127
+ // Variables with ImportBinding definitions are imports
128
+ const isImport = variable.defs.some((d) => d.type === 'ImportBinding');
129
+ if (isImport) {
130
+ importedNames.add(variable.name);
131
+ }
132
+ }
133
+ }
134
+ for (const scope of scopeManager.scopes) {
135
+ // Only process function-like scopes
136
+ if (scope.type !== 'function')
137
+ continue;
138
+ const block = scope.block;
139
+ const name = getFunctionName(block);
140
+ const locals = [];
141
+ const captured = [];
142
+ const imported = [];
143
+ const globals = [];
144
+ // Local variables are those declared in this scope
145
+ for (const variable of scope.variables) {
146
+ if (variable.name === 'arguments')
147
+ continue; // skip implicit `arguments`
148
+ locals.push(variable.name);
149
+ }
150
+ // "through" references are those not resolved in this scope
151
+ // They might be captured from parent, imported, or truly global
152
+ for (const ref of scope.through) {
153
+ const refName = ref.identifier.name;
154
+ if (importedNames.has(refName)) {
155
+ if (!imported.includes(refName)) {
156
+ imported.push(refName);
157
+ }
158
+ }
159
+ else if (ref.resolved) {
160
+ // Resolved to an outer scope variable → captured
161
+ if (!captured.includes(refName)) {
162
+ captured.push(refName);
163
+ }
164
+ }
165
+ else {
166
+ // Unresolved → global (browser API, etc.)
167
+ if (!globals.includes(refName)) {
168
+ globals.push(refName);
169
+ }
170
+ }
171
+ }
172
+ // Also check references that ARE resolved but point to outer scopes
173
+ for (const ref of scope.references) {
174
+ const refName = ref.identifier.name;
175
+ if (ref.resolved && !locals.includes(refName) && !importedNames.has(refName)) {
176
+ if (!captured.includes(refName)) {
177
+ captured.push(refName);
178
+ }
179
+ }
180
+ }
181
+ results.push({
182
+ name,
183
+ locals,
184
+ captured,
185
+ imported,
186
+ globals,
187
+ span: {
188
+ start: block.start ?? 0,
189
+ end: block.end ?? 0,
190
+ },
191
+ });
192
+ }
193
+ return results;
194
+ }
195
+ /**
196
+ * Extract a human-readable name for a function AST node.
197
+ */
198
+ function getFunctionName(node) {
199
+ // FunctionDeclaration with an id
200
+ if (node.type === 'FunctionDeclaration' && 'id' in node && node.id) {
201
+ return node.id.name;
202
+ }
203
+ // FunctionExpression with an id (e.g., const x = function foo() {})
204
+ if (node.type === 'FunctionExpression' && 'id' in node && node.id) {
205
+ return node.id.name;
206
+ }
207
+ return '<anonymous>';
208
+ }
209
+ /**
210
+ * Resolve names for anonymous functions from their assignment context.
211
+ *
212
+ * After building FunctionDependency[], walk the AST to find:
213
+ * - `const foo = () => {}` → name is `foo` (VariableDeclarator parent)
214
+ * - `const foo = function() {}` → name is `foo` (VariableDeclarator parent)
215
+ * - `{ key: () => {} }` → name is the property key
216
+ *
217
+ * Matches functions to FunctionDependency entries by span (start/end).
218
+ */
219
+ function resolveAnonymousNames(ast, functions) {
220
+ // Build a quick lookup from span → FunctionDependency for anonymous functions
221
+ const anonBySpan = new Map();
222
+ for (const fn of functions) {
223
+ if (fn.name === '<anonymous>') {
224
+ anonBySpan.set(`${fn.span.start}:${fn.span.end}`, fn);
225
+ }
226
+ }
227
+ if (anonBySpan.size === 0)
228
+ return;
229
+ walkWithParent(ast, null, (node, parent) => {
230
+ if (!isFunctionNode(node))
231
+ return;
232
+ const nStart = node.start;
233
+ const nEnd = node.end;
234
+ if (nStart == null || nEnd == null)
235
+ return;
236
+ const key = `${nStart}:${nEnd}`;
237
+ const fn = anonBySpan.get(key);
238
+ if (!fn)
239
+ return;
240
+ // Case 1: VariableDeclarator — `const foo = () => {}`
241
+ if (parent && parent.type === 'VariableDeclarator' && 'id' in parent) {
242
+ const id = parent.id;
243
+ if (id.type === 'Identifier' && 'name' in id) {
244
+ fn.name = id.name;
245
+ anonBySpan.delete(key);
246
+ return;
247
+ }
248
+ }
249
+ // Case 2: Property / ObjectProperty — `{ key: () => {} }`
250
+ if (parent && (parent.type === 'Property' || parent.type === 'ObjectProperty')) {
251
+ const propKey = parent.key;
252
+ if (propKey && propKey.type === 'Identifier' && 'name' in propKey) {
253
+ fn.name = propKey.name;
254
+ anonBySpan.delete(key);
255
+ return;
256
+ }
257
+ }
258
+ });
259
+ }
260
+ function isFunctionNode(node) {
261
+ return (node.type === 'ArrowFunctionExpression' ||
262
+ node.type === 'FunctionExpression');
263
+ }
264
+ /**
265
+ * Walk AST nodes, passing each node and its parent to the callback.
266
+ */
267
+ function walkWithParent(node, parent, callback) {
268
+ if (!node || typeof node !== 'object')
269
+ return;
270
+ if (Array.isArray(node)) {
271
+ for (const child of node) {
272
+ walkWithParent(child, parent, callback);
273
+ }
274
+ return;
275
+ }
276
+ const obj = node;
277
+ if (typeof obj.type !== 'string')
278
+ return;
279
+ const asNode = obj;
280
+ callback(asNode, parent);
281
+ for (const key of Object.keys(obj)) {
282
+ if (key === 'type')
283
+ continue;
284
+ walkWithParent(obj[key], asNode, callback);
285
+ }
286
+ }
287
+ /**
288
+ * Analyze a single module: parse → scope analysis → classify → extract.
289
+ */
290
+ export function analyzeModule(code, path, _options) {
291
+ const parsed = parseModule(code, path);
292
+ // Phase 3: classification
293
+ const segments = classifyModule(parsed, code);
294
+ // Phase 3.5: lazy candidate detection
295
+ let lazyCandidates;
296
+ let lazyKeptStatic;
297
+ const enableLazy = _options?.enableLazy !== false; // default: true
298
+ if (enableLazy) {
299
+ const lazyResult = detectLazyCandidates(parsed, code, segments, _options?.componentProfiles);
300
+ if (lazyResult.lazy.length > 0) {
301
+ lazyCandidates = lazyResult.lazy;
302
+ }
303
+ if (lazyResult.keepStatic.length > 0) {
304
+ lazyKeptStatic = lazyResult.keepStatic;
305
+ }
306
+ }
307
+ // Phase 4: extraction (handler chunks + lazy transforms)
308
+ const confidenceThreshold = _options?.confidenceThreshold ?? 0.8;
309
+ const extracted = extractModule(parsed, segments, code, confidenceThreshold, path, lazyCandidates);
310
+ if (extracted) {
311
+ return {
312
+ path,
313
+ segments,
314
+ hasExtractions: true,
315
+ clientCode: extracted.clientCode,
316
+ clientMap: extracted.clientMap,
317
+ chunkModules: extracted.chunkModules,
318
+ lazyCandidates,
319
+ lazyKeptStatic,
320
+ };
321
+ }
322
+ return {
323
+ path,
324
+ segments,
325
+ hasExtractions: false,
326
+ lazyCandidates,
327
+ lazyKeptStatic,
328
+ };
329
+ }
330
+ //# sourceMappingURL=analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Add `range` and `loc` properties to all AST nodes.
3
+ *
4
+ * OXC's parser outputs `start`/`end` byte offsets on nodes, but:
5
+ * - eslint-scope expects `range: [start, end]` (ESTree range convention)
6
+ * - esrap needs `loc: { start: { line, column }, end: { line, column } }` for source maps
7
+ *
8
+ * This function walks the AST and patches each node in-place.
9
+ */
10
+ export declare function addASTMetadata(node: unknown, sourceCode: string): void;
11
+ //# sourceMappingURL=ast-compat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast-compat.d.ts","sourceRoot":"","sources":["../src/ast-compat.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAGtE"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Add `range` and `loc` properties to all AST nodes.
3
+ *
4
+ * OXC's parser outputs `start`/`end` byte offsets on nodes, but:
5
+ * - eslint-scope expects `range: [start, end]` (ESTree range convention)
6
+ * - esrap needs `loc: { start: { line, column }, end: { line, column } }` for source maps
7
+ *
8
+ * This function walks the AST and patches each node in-place.
9
+ */
10
+ export function addASTMetadata(node, sourceCode) {
11
+ const lineStarts = buildLineStartTable(sourceCode);
12
+ addMetadataRecursive(node, lineStarts);
13
+ }
14
+ // ── Line/column computation ─────────────────────────────────────────────
15
+ /**
16
+ * Build a table of byte offsets where each line starts.
17
+ * lineStarts[i] = byte offset of the start of line i+1 (0-indexed internally).
18
+ */
19
+ function buildLineStartTable(source) {
20
+ const lineStarts = [0]; // line 1 starts at offset 0
21
+ for (let i = 0; i < source.length; i++) {
22
+ if (source.charCodeAt(i) === 10) { // '\n'
23
+ lineStarts.push(i + 1);
24
+ }
25
+ }
26
+ return lineStarts;
27
+ }
28
+ /**
29
+ * Convert a byte offset to an ESTree position.
30
+ * ESTree uses 1-based lines and 0-based columns.
31
+ */
32
+ function offsetToPosition(offset, lineStarts) {
33
+ // Binary search for the line containing this offset
34
+ let lo = 0;
35
+ let hi = lineStarts.length - 1;
36
+ while (lo < hi) {
37
+ const mid = (lo + hi + 1) >> 1;
38
+ if (lineStarts[mid] <= offset) {
39
+ lo = mid;
40
+ }
41
+ else {
42
+ hi = mid - 1;
43
+ }
44
+ }
45
+ return {
46
+ line: lo + 1, // 1-based
47
+ column: offset - lineStarts[lo], // 0-based
48
+ };
49
+ }
50
+ // ── Recursive AST walker ────────────────────────────────────────────────
51
+ function addMetadataRecursive(node, lineStarts) {
52
+ if (!node || typeof node !== 'object') {
53
+ return;
54
+ }
55
+ if (Array.isArray(node)) {
56
+ for (const child of node) {
57
+ addMetadataRecursive(child, lineStarts);
58
+ }
59
+ return;
60
+ }
61
+ const obj = node;
62
+ // Only process AST nodes (objects with a `type` string property)
63
+ if (typeof obj.type !== 'string') {
64
+ return;
65
+ }
66
+ // Add range and loc if start/end byte offsets are present
67
+ if (typeof obj.start === 'number' && typeof obj.end === 'number') {
68
+ const start = obj.start;
69
+ const end = obj.end;
70
+ obj.range = [start, end];
71
+ obj.loc = {
72
+ start: offsetToPosition(start, lineStarts),
73
+ end: offsetToPosition(end, lineStarts),
74
+ };
75
+ }
76
+ // Recurse into all properties
77
+ for (const key of Object.keys(obj)) {
78
+ if (key === 'type' || key === 'start' || key === 'end' || key === 'range' || key === 'loc') {
79
+ continue;
80
+ }
81
+ addMetadataRecursive(obj[key], lineStarts);
82
+ }
83
+ }
84
+ //# sourceMappingURL=ast-compat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast-compat.js","sourceRoot":"","sources":["../src/ast-compat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { Node } from 'estree';
2
+ import type { ClassifiedSegment, FunctionDependency } from '../types.js';
3
+ import type { TaintResult } from './taint.js';
4
+ import type { PurityResult } from './purity.js';
5
+ interface ClassificationInput {
6
+ fn: FunctionDependency;
7
+ taint: TaintResult;
8
+ purity: PurityResult;
9
+ /** The parent call expression name, if this is a hook callback */
10
+ parentHook: string | null;
11
+ /** The JSX event prop name, if this function is used exclusively as an event handler */
12
+ parentEventProp: string | null;
13
+ /** Source code for content hashing */
14
+ sourceCode: string;
15
+ /** File path for naming */
16
+ filePath: string;
17
+ /** Index among siblings for disambiguation */
18
+ index: number;
19
+ /** The AST node for this function (for JSX detection) */
20
+ astNode: Node | null;
21
+ }
22
+ /**
23
+ * Pass 3: Boundary Detection
24
+ *
25
+ * Determines the final classification for each function and whether
26
+ * it's an extraction candidate.
27
+ */
28
+ export declare function classifySegment(input: ClassificationInput): ClassifiedSegment;
29
+ export {};
30
+ //# sourceMappingURL=boundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../../src/classify/boundary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAyB,MAAM,aAAa,CAAC;AAChG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,UAAU,mBAAmB;IAC3B,EAAE,EAAE,kBAAkB,CAAC;IACvB,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,YAAY,CAAC;IACrB,kEAAkE;IAClE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,wFAAwF;IACxF,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,mBAAmB,GAAG,iBAAiB,CAwF7E"}
@@ -0,0 +1,145 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { EXTRACTABLE_HOOKS, CLIENT_ONLY_HOOKS } from './react-patterns.js';
3
+ /**
4
+ * Pass 3: Boundary Detection
5
+ *
6
+ * Determines the final classification for each function and whether
7
+ * it's an extraction candidate.
8
+ */
9
+ export function classifySegment(input) {
10
+ const { fn, taint, purity, parentHook, parentEventProp, sourceCode, filePath, index, astNode } = input;
11
+ const codeSlice = sourceCode.slice(fn.span.start, fn.span.end);
12
+ const id = generateSegmentId(filePath, codeSlice);
13
+ const name = generateName(fn, filePath, index);
14
+ let classification;
15
+ let confidence;
16
+ const reasons = [];
17
+ // Rule 0: If used exclusively as a JSX event handler prop, it's an event handler
18
+ // This fires before taint analysis because event handlers are extracted regardless
19
+ // of whether they touch browser APIs (they all do — that's the point)
20
+ if (parentEventProp) {
21
+ classification = 'EventHandler';
22
+ confidence = 0.9;
23
+ reasons.push(`Event handler for ${parentEventProp}`);
24
+ }
25
+ // Rule 1: If inside a client-only hook (useEffect, useLayoutEffect), it's client
26
+ else if (parentHook && CLIENT_ONLY_HOOKS.has(parentHook)) {
27
+ classification = 'ClientInteractive';
28
+ confidence = 1.0;
29
+ reasons.push(`Inside ${parentHook} callback (client-side effect)`);
30
+ }
31
+ // Rule 2: If tainted by browser globals, it's client
32
+ else if (taint.tainted) {
33
+ classification = 'ClientInteractive';
34
+ confidence = 0.95;
35
+ reasons.push(`References browser APIs: ${taint.browserGlobals.join(', ')}`);
36
+ }
37
+ // Rule 3: If pure and inside an extractable hook (useMemo, useCallback), it's pure computation
38
+ else if (purity.pure && parentHook && EXTRACTABLE_HOOKS.has(parentHook)) {
39
+ classification = 'PureComputation';
40
+ confidence = 0.9;
41
+ reasons.push(`Pure computation inside ${parentHook}`);
42
+ reasons.push(...purity.reasons);
43
+ }
44
+ // Rule 3.5: If function contains JSX, it's a React component — not extractable
45
+ else if (astNode && containsJSX(astNode)) {
46
+ classification = 'Shared';
47
+ confidence = 0.0;
48
+ reasons.push('React component (contains JSX) — not extractable');
49
+ }
50
+ // Rule 4: If pure and a named helper function, it's pure computation
51
+ else if (purity.pure && fn.name !== '<anonymous>') {
52
+ classification = 'PureComputation';
53
+ confidence = 0.85;
54
+ reasons.push('Pure named helper function');
55
+ reasons.push(...purity.reasons);
56
+ }
57
+ // Rule 5: If pure but anonymous and not in a hook, it's shared
58
+ else if (purity.pure) {
59
+ classification = 'Shared';
60
+ confidence = 0.7;
61
+ reasons.push('Pure anonymous function — may be used in both contexts');
62
+ reasons.push(...purity.reasons);
63
+ }
64
+ // Rule 6a: Cross-environment globals (fetch, crypto, etc.) — ambiguous
65
+ else if (taint.ambiguousGlobals.length > 0) {
66
+ classification = 'Ambiguous';
67
+ confidence = 0.5;
68
+ reasons.push(`Cross-environment APIs: ${taint.ambiguousGlobals.join(', ')}`);
69
+ reasons.push('Available in both browser and Node.js — cannot determine intent');
70
+ }
71
+ // Rule 6b: Unknown globals — ambiguous
72
+ else if (taint.unknownGlobals.length > 0) {
73
+ classification = 'Ambiguous';
74
+ confidence = 0.5;
75
+ reasons.push(`Unknown globals: ${taint.unknownGlobals.join(', ')}`);
76
+ reasons.push('Cannot determine if these are browser-only APIs');
77
+ }
78
+ // Rule 7: Default — ambiguous, leave on client
79
+ else {
80
+ classification = 'Ambiguous';
81
+ confidence = 0.5;
82
+ reasons.push('Could not determine classification');
83
+ reasons.push(...purity.reasons);
84
+ }
85
+ return {
86
+ id,
87
+ name,
88
+ classification,
89
+ confidence,
90
+ reasons,
91
+ dependencies: [...fn.captured, ...fn.imported],
92
+ span: fn.span,
93
+ };
94
+ }
95
+ function generateSegmentId(_filePath, code) {
96
+ // Content-addressable: hash only the code, not the file path.
97
+ // This ensures deterministic IDs across build environments (CI vs local)
98
+ // and enables deduplication of identical handler bodies across files.
99
+ const hash = createHash('sha256')
100
+ .update(code)
101
+ .digest('hex')
102
+ .slice(0, 12);
103
+ return `seg_${hash}`;
104
+ }
105
+ function generateName(fn, filePath, index) {
106
+ const base = filePath.replace(/\.[^.]+$/, '').replace(/[^a-zA-Z0-9]/g, '_');
107
+ if (fn.name !== '<anonymous>') {
108
+ return `${base}_${fn.name}`;
109
+ }
110
+ return `${base}_anon_${index}`;
111
+ }
112
+ /**
113
+ * Check if an AST node contains any JSX elements or fragments.
114
+ * Functions containing JSX are React components and should not be extracted.
115
+ */
116
+ function containsJSX(node) {
117
+ let found = false;
118
+ walkNodeLocal(node, (n) => {
119
+ if (found)
120
+ return;
121
+ if (n.type.startsWith('JSX'))
122
+ found = true;
123
+ });
124
+ return found;
125
+ }
126
+ function walkNodeLocal(node, callback) {
127
+ if (!node || typeof node !== 'object')
128
+ return;
129
+ if (Array.isArray(node)) {
130
+ for (const child of node) {
131
+ walkNodeLocal(child, callback);
132
+ }
133
+ return;
134
+ }
135
+ const obj = node;
136
+ if (typeof obj.type !== 'string')
137
+ return;
138
+ callback(obj);
139
+ for (const key of Object.keys(obj)) {
140
+ if (key === 'type')
141
+ continue;
142
+ walkNodeLocal(obj[key], callback);
143
+ }
144
+ }
145
+ //# sourceMappingURL=boundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundary.js","sourceRoot":"","sources":["../../src/classify/boundary.ts"],"names":[],"mappings":"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"}