gitnexus 1.6.6-rc.103 → 1.6.6-rc.105
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/ingestion/call-processor.js +46 -8
- package/dist/core/ingestion/languages/javascript/captures.js +10 -0
- package/dist/core/ingestion/languages/javascript/query.js +105 -0
- package/dist/core/ingestion/languages/typescript/array-callback.d.ts +3 -29
- package/dist/core/ingestion/languages/typescript/array-callback.js +4 -43
- package/dist/core/ingestion/languages/typescript/captures.js +10 -0
- package/dist/core/ingestion/languages/typescript/query.js +84 -0
- package/dist/core/ingestion/languages/typescript.js +44 -14
- package/dist/core/ingestion/method-types.d.ts +2 -2
- package/dist/core/ingestion/parsing-processor.js +32 -2
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +23 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +88 -16
- package/dist/core/ingestion/tree-sitter-queries.d.ts +2 -2
- package/dist/core/ingestion/tree-sitter-queries.js +203 -0
- package/dist/core/ingestion/ts-js-hoc-utils.d.ts +12 -0
- package/dist/core/ingestion/ts-js-hoc-utils.js +105 -0
- package/dist/core/ingestion/type-env.d.ts +2 -1
- package/dist/core/ingestion/type-env.js +5 -5
- package/dist/core/ingestion/utils/ast-helpers.js +3 -1
- package/dist/core/ingestion/utils/deferred-resolution-profile.d.ts +10 -0
- package/dist/core/ingestion/utils/deferred-resolution-profile.js +30 -0
- package/dist/core/ingestion/workers/parse-worker.js +58 -4
- package/dist/core/ingestion/workers/worker-pool.d.ts +38 -3
- package/dist/core/ingestion/workers/worker-pool.js +282 -43
- package/dist/core/run-analyze.js +4 -1
- package/package.json +1 -1
|
@@ -28,7 +28,7 @@ import { generateId } from '../../lib/utils.js';
|
|
|
28
28
|
import { getLanguageFromFilename, SupportedLanguages } from '../../_shared/index.js';
|
|
29
29
|
import { isRegistryPrimary } from './registry-primary-flag.js';
|
|
30
30
|
import { isVerboseIngestionEnabled } from './utils/verbose.js';
|
|
31
|
-
import { deferredCallFileSlowMs, deferredCallLogEveryN, getDeferredProfileDroppedCount, isDeferredResolutionProfileEnabled, logDeferredProfile, profileElapsedMs, resetDeferredProfileDroppedCount, startTimer, } from './utils/deferred-resolution-profile.js';
|
|
31
|
+
import { ALWAYS_ON_SLOW_FILE_WARN_THROTTLE_MS, alwaysOnSlowFileWarnMs, deferredCallFileSlowMs, deferredCallLogEveryN, getDeferredProfileDroppedCount, isDeferredResolutionProfileEnabled, logDeferredProfile, profileElapsedMs, resetDeferredProfileDroppedCount, startTimer, } from './utils/deferred-resolution-profile.js';
|
|
32
32
|
import { yieldToEventLoop } from './utils/event-loop.js';
|
|
33
33
|
import { parseSourceSafe } from '../tree-sitter/safe-parse.js';
|
|
34
34
|
import { CLASS_CONTAINER_TYPES, FUNCTION_NODE_TYPES, findEnclosingClassInfo, genericFuncName, inferFunctionLabel, } from './utils/ast-helpers.js';
|
|
@@ -311,7 +311,7 @@ const findEnclosingFunction = (node, filePath, ctx, provider) => {
|
|
|
311
311
|
let current = node.parent;
|
|
312
312
|
while (current) {
|
|
313
313
|
if (FUNCTION_NODE_TYPES.has(current.type)) {
|
|
314
|
-
const efnResult = provider.methodExtractor?.extractFunctionName?.(current);
|
|
314
|
+
const efnResult = provider.methodExtractor?.extractFunctionName?.(current, filePath);
|
|
315
315
|
const funcName = efnResult?.funcName ?? genericFuncName(current);
|
|
316
316
|
const label = efnResult?.label ?? inferFunctionLabel(current.type);
|
|
317
317
|
if (funcName) {
|
|
@@ -749,6 +749,7 @@ compiledQueryCache) => {
|
|
|
749
749
|
const importedReturnTypes = importedReturnTypesMap?.get(file.path);
|
|
750
750
|
const importedRawReturnTypes = importedRawReturnTypesMap?.get(file.path);
|
|
751
751
|
const typeEnv = buildTypeEnv(tree, language, {
|
|
752
|
+
filePath: file.path,
|
|
752
753
|
model: ctx.model,
|
|
753
754
|
parentMap,
|
|
754
755
|
importedBindings,
|
|
@@ -1050,7 +1051,8 @@ compiledQueryCache) => {
|
|
|
1050
1051
|
let p = callNode.parent;
|
|
1051
1052
|
while (p) {
|
|
1052
1053
|
if (FUNCTION_NODE_TYPES.has(p.type)) {
|
|
1053
|
-
const funcName = provider.methodExtractor?.extractFunctionName?.(p)?.funcName ??
|
|
1054
|
+
const funcName = provider.methodExtractor?.extractFunctionName?.(p, file.path)?.funcName ??
|
|
1055
|
+
genericFuncName(p);
|
|
1054
1056
|
if (funcName) {
|
|
1055
1057
|
scope = `${funcName}@${p.startIndex}`;
|
|
1056
1058
|
break;
|
|
@@ -2303,6 +2305,14 @@ export const processCallsFromExtracted = async (graph, extractedCalls, ctx, onPr
|
|
|
2303
2305
|
const slowFileMs = profileCalls ? deferredCallFileSlowMs() : 0;
|
|
2304
2306
|
const logEveryN = profileCalls ? deferredCallLogEveryN() : 0;
|
|
2305
2307
|
let skippedRegistryPrimaryFiles = 0;
|
|
2308
|
+
// Always-on slow-file watchdog (#1741). Independent of the verbose/profile
|
|
2309
|
+
// gate above: even a plain `analyze` run surfaces ONE actionable warning
|
|
2310
|
+
// when a single file's call resolution is pathologically slow — turning the
|
|
2311
|
+
// silent "stuck at Resolving calls (N/M)" symptom into a named culprit.
|
|
2312
|
+
// Throttled so a genuinely slow repo can't produce a warn storm.
|
|
2313
|
+
const alwaysSlowFileMs = alwaysOnSlowFileWarnMs();
|
|
2314
|
+
let lastSlowFileWarnAt = 0;
|
|
2315
|
+
let suppressedSlowFileWarnings = 0;
|
|
2306
2316
|
// Fresh dropped-log counter per analyze run — the module-private counter
|
|
2307
2317
|
// in deferred-resolution-profile.ts is process-lived, so without a reset
|
|
2308
2318
|
// here it would accumulate across consecutive analyze invocations in the
|
|
@@ -2314,12 +2324,16 @@ export const processCallsFromExtracted = async (graph, extractedCalls, ctx, onPr
|
|
|
2314
2324
|
// denominator stays stable as the loop iterates. Otherwise `${totalFiles -
|
|
2315
2325
|
// skippedRegistryPrimaryFiles}` drifts upward — files iterated before later
|
|
2316
2326
|
// registry-primary skips have been seen carry an inflated denominator, and
|
|
2317
|
-
// the ratio only self-corrects after every file has been classified.
|
|
2318
|
-
//
|
|
2319
|
-
//
|
|
2320
|
-
//
|
|
2327
|
+
// the ratio only self-corrects after every file has been classified.
|
|
2328
|
+
//
|
|
2329
|
+
// Runs whenever its result will actually be read: on the profile path (the
|
|
2330
|
+
// live deferred-profile log) OR when the always-on slow-file watchdog is
|
|
2331
|
+
// active (#1741) — the watchdog's warning prints `${resolvedFiles}/${resolvedTotal}`
|
|
2332
|
+
// unconditionally, so leaving resolvedTotal at 0 on a plain run produced a
|
|
2333
|
+
// bogus "Resolved N/0 files" denominator on exactly the unprofiled runs the
|
|
2334
|
+
// watchdog exists for. When both gates are off, skip the extra Map pass.
|
|
2321
2335
|
let resolvedTotal = 0;
|
|
2322
|
-
if (profileCalls) {
|
|
2336
|
+
if (profileCalls || alwaysSlowFileMs > 0) {
|
|
2323
2337
|
for (const filePath of byFile.keys()) {
|
|
2324
2338
|
const lang = getLanguageFromFilename(filePath);
|
|
2325
2339
|
if (!lang || !isRegistryPrimary(lang))
|
|
@@ -2341,6 +2355,9 @@ export const processCallsFromExtracted = async (graph, extractedCalls, ctx, onPr
|
|
|
2341
2355
|
}
|
|
2342
2356
|
resolvedFiles++;
|
|
2343
2357
|
const tFile = startTimer(profileCalls);
|
|
2358
|
+
// Always-on timer (cheap: one hrtime read) feeding the slow-file watchdog
|
|
2359
|
+
// below. Distinct from `tFile`, which is null unless profiling is on.
|
|
2360
|
+
const tFileAlways = alwaysSlowFileMs > 0 ? process.hrtime.bigint() : null;
|
|
2344
2361
|
if (profileCalls && (resolvedFiles === 1 || resolvedFiles % logEveryN === 0)) {
|
|
2345
2362
|
logDeferredProfile(`calls ${resolvedFiles}/${resolvedTotal} file=${filePath} sites=${calls.length}`);
|
|
2346
2363
|
}
|
|
@@ -2455,6 +2472,27 @@ export const processCallsFromExtracted = async (graph, extractedCalls, ctx, onPr
|
|
|
2455
2472
|
logDeferredProfile(`slow file ${elapsed.toFixed(0)}ms path=${filePath} calls=${calls.length} lang=${fileLanguage ?? 'unknown'}`);
|
|
2456
2473
|
}
|
|
2457
2474
|
}
|
|
2475
|
+
// Always-on slow-file watchdog (#1741) — fires regardless of verbose.
|
|
2476
|
+
if (tFileAlways !== null) {
|
|
2477
|
+
const elapsedAlways = profileElapsedMs(tFileAlways);
|
|
2478
|
+
if (elapsedAlways >= alwaysSlowFileMs) {
|
|
2479
|
+
const now = Date.now();
|
|
2480
|
+
if (now - lastSlowFileWarnAt >= ALWAYS_ON_SLOW_FILE_WARN_THROTTLE_MS) {
|
|
2481
|
+
lastSlowFileWarnAt = now;
|
|
2482
|
+
const suppressedNote = suppressedSlowFileWarnings > 0
|
|
2483
|
+
? ` (+${suppressedSlowFileWarnings} more slow files since the last warning)`
|
|
2484
|
+
: '';
|
|
2485
|
+
logger.warn(`⏳ Call resolution for ${filePath} took ${(elapsedAlways / 1000).toFixed(1)}s ` +
|
|
2486
|
+
`(${calls.length} call sites, ${fileLanguage ?? 'unknown'}). The run is not frozen — ` +
|
|
2487
|
+
`this file is unusually expensive to resolve. Resolved ${resolvedFiles}/${resolvedTotal} ` +
|
|
2488
|
+
`files so far.${suppressedNote} Pass -v for per-file deferred-resolution timing.`);
|
|
2489
|
+
suppressedSlowFileWarnings = 0;
|
|
2490
|
+
}
|
|
2491
|
+
else {
|
|
2492
|
+
suppressedSlowFileWarnings++;
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2458
2496
|
}
|
|
2459
2497
|
if (profileCalls) {
|
|
2460
2498
|
logDeferredProfile(`processCallsFromExtracted done: ${totalFiles} files, ${extractedCalls.length} call sites, skipped registry-primary files=${skippedRegistryPrimaryFiles}`);
|
|
@@ -34,6 +34,7 @@ import { synthesizeTsReceiverBinding } from '../typescript/receiver-binding.js';
|
|
|
34
34
|
import { isArrayMethodCallbackArrow } from '../typescript/array-callback.js';
|
|
35
35
|
import { getTreeSitterBufferSize } from '../../constants.js';
|
|
36
36
|
import { parseSourceSafe } from '../../../tree-sitter/safe-parse.js';
|
|
37
|
+
import { deriveDefaultExportHocName, isBlockedDefaultExportHoc, isDefaultExportHocFunctionNode, } from '../../ts-js-hoc-utils.js';
|
|
37
38
|
/** JS function-like node types that may carry a synthesized `this` binding.
|
|
38
39
|
* Kept in sync with the `@scope.function` patterns in `query.ts`. */
|
|
39
40
|
const FUNCTION_NODE_TYPES = [
|
|
@@ -615,6 +616,15 @@ export function emitJsScopeCaptures(sourceText, filePath, cachedTree) {
|
|
|
615
616
|
if (arrowNode !== null && isArrayMethodCallbackArrow(arrowNode)) {
|
|
616
617
|
continue;
|
|
617
618
|
}
|
|
619
|
+
if (arrowNode !== null && isBlockedDefaultExportHoc(arrowNode)) {
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
if (fnDeclAnchor !== undefined) {
|
|
624
|
+
const fnNode = findFunctionNode(tree.rootNode, fnDeclAnchor.range);
|
|
625
|
+
if (fnNode !== null && isDefaultExportHocFunctionNode(fnNode)) {
|
|
626
|
+
grouped['@declaration.name'] = syntheticCapture('@declaration.name', fnNode, deriveDefaultExportHocName(filePath));
|
|
627
|
+
}
|
|
618
628
|
}
|
|
619
629
|
// Synthesize arity metadata on function-like declarations.
|
|
620
630
|
const declAnchor = pickFirstDefined(grouped, FUNCTION_DECL_TAGS);
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
*/
|
|
48
48
|
import Parser from 'tree-sitter';
|
|
49
49
|
import JS from 'tree-sitter-javascript';
|
|
50
|
+
import { ARRAY_METHOD_NOT_ANY_OF_PREDICATE, DEFAULT_EXPORT_IDENTIFIER_NOT_ANY_OF_PREDICATE, } from '../../ts-js-hoc-utils.js';
|
|
50
51
|
const JS_GRAMMAR = JS;
|
|
51
52
|
/** True when the file should be parsed with the JSX-extended query. */
|
|
52
53
|
function isJsxFile(filePath) {
|
|
@@ -150,10 +151,13 @@ const JAVASCRIPT_SCOPE_QUERY = `
|
|
|
150
151
|
;; Those are filtered out emit-side in captures.ts via
|
|
151
152
|
;; isArrayMethodCallbackArrow (member-expression callee whose property
|
|
152
153
|
;; is a known Array method), so only the @declaration.const survives.
|
|
154
|
+
;; Excludes common array methods (map, filter, reduce, etc.) to avoid
|
|
155
|
+
;; false positives like \`const x = arr.map(a => ...)\`.
|
|
153
156
|
(lexical_declaration
|
|
154
157
|
(variable_declarator
|
|
155
158
|
name: (identifier) @declaration.name
|
|
156
159
|
value: (call_expression
|
|
160
|
+
function: (identifier)
|
|
157
161
|
arguments: (arguments
|
|
158
162
|
(arrow_function) @declaration.function))))
|
|
159
163
|
|
|
@@ -161,14 +165,36 @@ const JAVASCRIPT_SCOPE_QUERY = `
|
|
|
161
165
|
(variable_declarator
|
|
162
166
|
name: (identifier) @declaration.name
|
|
163
167
|
value: (call_expression
|
|
168
|
+
function: (identifier)
|
|
164
169
|
arguments: (arguments
|
|
165
170
|
(function_expression) @declaration.function))))
|
|
166
171
|
|
|
172
|
+
(lexical_declaration
|
|
173
|
+
(variable_declarator
|
|
174
|
+
name: (identifier) @declaration.name
|
|
175
|
+
value: (call_expression
|
|
176
|
+
function: (member_expression
|
|
177
|
+
property: (property_identifier) @callee)
|
|
178
|
+
arguments: (arguments
|
|
179
|
+
(arrow_function) @declaration.function)))
|
|
180
|
+
${ARRAY_METHOD_NOT_ANY_OF_PREDICATE})
|
|
181
|
+
|
|
182
|
+
(lexical_declaration
|
|
183
|
+
(variable_declarator
|
|
184
|
+
name: (identifier) @declaration.name
|
|
185
|
+
value: (call_expression
|
|
186
|
+
function: (member_expression
|
|
187
|
+
property: (property_identifier) @callee)
|
|
188
|
+
arguments: (arguments
|
|
189
|
+
(function_expression) @declaration.function)))
|
|
190
|
+
${ARRAY_METHOD_NOT_ANY_OF_PREDICATE})
|
|
191
|
+
|
|
167
192
|
(export_statement
|
|
168
193
|
declaration: (lexical_declaration
|
|
169
194
|
(variable_declarator
|
|
170
195
|
name: (identifier) @declaration.name
|
|
171
196
|
value: (call_expression
|
|
197
|
+
function: (identifier)
|
|
172
198
|
arguments: (arguments
|
|
173
199
|
(arrow_function) @declaration.function)))))
|
|
174
200
|
|
|
@@ -177,13 +203,37 @@ const JAVASCRIPT_SCOPE_QUERY = `
|
|
|
177
203
|
(variable_declarator
|
|
178
204
|
name: (identifier) @declaration.name
|
|
179
205
|
value: (call_expression
|
|
206
|
+
function: (identifier)
|
|
180
207
|
arguments: (arguments
|
|
181
208
|
(function_expression) @declaration.function)))))
|
|
182
209
|
|
|
210
|
+
(export_statement
|
|
211
|
+
declaration: (lexical_declaration
|
|
212
|
+
(variable_declarator
|
|
213
|
+
name: (identifier) @declaration.name
|
|
214
|
+
value: (call_expression
|
|
215
|
+
function: (member_expression
|
|
216
|
+
property: (property_identifier) @callee)
|
|
217
|
+
arguments: (arguments
|
|
218
|
+
(arrow_function) @declaration.function))))
|
|
219
|
+
${ARRAY_METHOD_NOT_ANY_OF_PREDICATE})
|
|
220
|
+
|
|
221
|
+
(export_statement
|
|
222
|
+
declaration: (lexical_declaration
|
|
223
|
+
(variable_declarator
|
|
224
|
+
name: (identifier) @declaration.name
|
|
225
|
+
value: (call_expression
|
|
226
|
+
function: (member_expression
|
|
227
|
+
property: (property_identifier) @callee)
|
|
228
|
+
arguments: (arguments
|
|
229
|
+
(function_expression) @declaration.function))))
|
|
230
|
+
${ARRAY_METHOD_NOT_ANY_OF_PREDICATE})
|
|
231
|
+
|
|
183
232
|
(variable_declaration
|
|
184
233
|
(variable_declarator
|
|
185
234
|
name: (identifier) @declaration.name
|
|
186
235
|
value: (call_expression
|
|
236
|
+
function: (identifier)
|
|
187
237
|
arguments: (arguments
|
|
188
238
|
(arrow_function) @declaration.function))))
|
|
189
239
|
|
|
@@ -191,9 +241,64 @@ const JAVASCRIPT_SCOPE_QUERY = `
|
|
|
191
241
|
(variable_declarator
|
|
192
242
|
name: (identifier) @declaration.name
|
|
193
243
|
value: (call_expression
|
|
244
|
+
function: (identifier)
|
|
194
245
|
arguments: (arguments
|
|
195
246
|
(function_expression) @declaration.function))))
|
|
196
247
|
|
|
248
|
+
(variable_declaration
|
|
249
|
+
(variable_declarator
|
|
250
|
+
name: (identifier) @declaration.name
|
|
251
|
+
value: (call_expression
|
|
252
|
+
function: (member_expression
|
|
253
|
+
property: (property_identifier) @callee)
|
|
254
|
+
arguments: (arguments
|
|
255
|
+
(arrow_function) @declaration.function)))
|
|
256
|
+
${ARRAY_METHOD_NOT_ANY_OF_PREDICATE})
|
|
257
|
+
|
|
258
|
+
(variable_declaration
|
|
259
|
+
(variable_declarator
|
|
260
|
+
name: (identifier) @declaration.name
|
|
261
|
+
value: (call_expression
|
|
262
|
+
function: (member_expression
|
|
263
|
+
property: (property_identifier) @callee)
|
|
264
|
+
arguments: (arguments
|
|
265
|
+
(function_expression) @declaration.function)))
|
|
266
|
+
${ARRAY_METHOD_NOT_ANY_OF_PREDICATE})
|
|
267
|
+
|
|
268
|
+
;; HOC-wrapped default exports (JS parity with TS patterns in
|
|
269
|
+
;; languages/typescript/query.ts). The emit phase rewrites
|
|
270
|
+
;; @declaration.name to a file-derived name so wrapper helpers do not
|
|
271
|
+
;; become the graph-visible symbol name.
|
|
272
|
+
((export_statement
|
|
273
|
+
value: (call_expression
|
|
274
|
+
function: (identifier) @hoc
|
|
275
|
+
arguments: (arguments
|
|
276
|
+
(arrow_function) @declaration.function)))
|
|
277
|
+
${DEFAULT_EXPORT_IDENTIFIER_NOT_ANY_OF_PREDICATE})
|
|
278
|
+
|
|
279
|
+
((export_statement
|
|
280
|
+
value: (call_expression
|
|
281
|
+
function: (identifier) @hoc
|
|
282
|
+
arguments: (arguments
|
|
283
|
+
(function_expression) @declaration.function)))
|
|
284
|
+
${DEFAULT_EXPORT_IDENTIFIER_NOT_ANY_OF_PREDICATE})
|
|
285
|
+
|
|
286
|
+
((export_statement
|
|
287
|
+
value: (call_expression
|
|
288
|
+
function: (member_expression
|
|
289
|
+
property: (property_identifier) @callee)
|
|
290
|
+
arguments: (arguments
|
|
291
|
+
(arrow_function) @declaration.function)))
|
|
292
|
+
${ARRAY_METHOD_NOT_ANY_OF_PREDICATE})
|
|
293
|
+
|
|
294
|
+
((export_statement
|
|
295
|
+
value: (call_expression
|
|
296
|
+
function: (member_expression
|
|
297
|
+
property: (property_identifier) @callee)
|
|
298
|
+
arguments: (arguments
|
|
299
|
+
(function_expression) @declaration.function)))
|
|
300
|
+
${ARRAY_METHOD_NOT_ANY_OF_PREDICATE})
|
|
301
|
+
|
|
197
302
|
;; Variable / constant declarations (non-function values).
|
|
198
303
|
(lexical_declaration
|
|
199
304
|
(variable_declarator
|
|
@@ -23,32 +23,6 @@
|
|
|
23
23
|
* Pure given the input node. No I/O, no globals.
|
|
24
24
|
*/
|
|
25
25
|
import type { SyntaxNode } from '../../utils/ast-helpers.js';
|
|
26
|
-
/**
|
|
27
|
-
* Array prototype higher-order methods whose result is a value, not a
|
|
28
|
-
* function. A callback passed to one of these is an anonymous callback,
|
|
29
|
-
* never a top-level function definition. Identifier-callee HOCs
|
|
30
|
-
* (`forwardRef(...)`, `useCallback(...)`, custom factories) are
|
|
31
|
-
* deliberately NOT listed — they keep their `Function` classification.
|
|
32
|
-
*
|
|
33
|
-
* Trade-off (unchanged from before #1876): a custom *fluent-API* member
|
|
34
|
-
* call with a callback whose method name is not in this set
|
|
35
|
-
* (`qb.where(x => …)`) still classifies as `Function`. There is no clean
|
|
36
|
-
* syntactic line beyond the well-known Array surface, so the set is
|
|
37
|
-
* intentionally closed and easy to extend.
|
|
38
|
-
*
|
|
39
|
-
* Receiver-blind, by design: the match keys on the method NAME only, never
|
|
40
|
-
* the receiver type (tree-sitter has no type information here). So an in-set
|
|
41
|
-
* name on a NON-array receiver — `Map`/`Set` `.forEach`, an RxJS
|
|
42
|
-
* `observable.map(…)`, a query builder `.sort(…)`, a lodash chain
|
|
43
|
-
* `.filter(…)` — is ALSO treated as a callback and has its
|
|
44
|
-
* `@declaration.function` dropped. This is an accepted limitation, not a
|
|
45
|
-
* regression: those bindings hold the call's *result value*, not a callable,
|
|
46
|
-
* so a value def is the correct classification anyway. The only genuine loss
|
|
47
|
-
* is a bespoke DSL whose in-set-named method returns something callable —
|
|
48
|
-
* rare enough to accept rather than guard with type inference. Pinned by the
|
|
49
|
-
* "in-set method on a non-array receiver" case in `*-captures.test.ts`.
|
|
50
|
-
*/
|
|
51
|
-
export declare const ARRAY_CALLBACK_METHODS: ReadonlySet<string>;
|
|
52
26
|
/**
|
|
53
27
|
* True when `node` (an `arrow_function` / `function_expression`) is the
|
|
54
28
|
* callback argument of an array higher-order-method call, i.e. the
|
|
@@ -60,8 +34,8 @@ export declare const ARRAY_CALLBACK_METHODS: ReadonlySet<string>;
|
|
|
60
34
|
* (`forwardRef(() => …)` — callee is an `identifier`, not a
|
|
61
35
|
* `member_expression`), so neither is ever suppressed.
|
|
62
36
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
37
|
+
* The helper itself only handles direct `member_expression` callees. Broader
|
|
38
|
+
* shapes like `(arr.map)(cb)` and `arr['map'](cb)` are now filtered at the
|
|
39
|
+
* query layer, so this emit-side check stays focused on the direct fallback.
|
|
66
40
|
*/
|
|
67
41
|
export declare function isArrayMethodCallbackArrow(node: SyntaxNode): boolean;
|
|
@@ -22,46 +22,7 @@
|
|
|
22
22
|
*
|
|
23
23
|
* Pure given the input node. No I/O, no globals.
|
|
24
24
|
*/
|
|
25
|
-
|
|
26
|
-
* Array prototype higher-order methods whose result is a value, not a
|
|
27
|
-
* function. A callback passed to one of these is an anonymous callback,
|
|
28
|
-
* never a top-level function definition. Identifier-callee HOCs
|
|
29
|
-
* (`forwardRef(...)`, `useCallback(...)`, custom factories) are
|
|
30
|
-
* deliberately NOT listed — they keep their `Function` classification.
|
|
31
|
-
*
|
|
32
|
-
* Trade-off (unchanged from before #1876): a custom *fluent-API* member
|
|
33
|
-
* call with a callback whose method name is not in this set
|
|
34
|
-
* (`qb.where(x => …)`) still classifies as `Function`. There is no clean
|
|
35
|
-
* syntactic line beyond the well-known Array surface, so the set is
|
|
36
|
-
* intentionally closed and easy to extend.
|
|
37
|
-
*
|
|
38
|
-
* Receiver-blind, by design: the match keys on the method NAME only, never
|
|
39
|
-
* the receiver type (tree-sitter has no type information here). So an in-set
|
|
40
|
-
* name on a NON-array receiver — `Map`/`Set` `.forEach`, an RxJS
|
|
41
|
-
* `observable.map(…)`, a query builder `.sort(…)`, a lodash chain
|
|
42
|
-
* `.filter(…)` — is ALSO treated as a callback and has its
|
|
43
|
-
* `@declaration.function` dropped. This is an accepted limitation, not a
|
|
44
|
-
* regression: those bindings hold the call's *result value*, not a callable,
|
|
45
|
-
* so a value def is the correct classification anyway. The only genuine loss
|
|
46
|
-
* is a bespoke DSL whose in-set-named method returns something callable —
|
|
47
|
-
* rare enough to accept rather than guard with type inference. Pinned by the
|
|
48
|
-
* "in-set method on a non-array receiver" case in `*-captures.test.ts`.
|
|
49
|
-
*/
|
|
50
|
-
export const ARRAY_CALLBACK_METHODS = new Set([
|
|
51
|
-
'map',
|
|
52
|
-
'filter',
|
|
53
|
-
'find',
|
|
54
|
-
'findIndex',
|
|
55
|
-
'findLast',
|
|
56
|
-
'findLastIndex',
|
|
57
|
-
'forEach',
|
|
58
|
-
'reduce',
|
|
59
|
-
'reduceRight',
|
|
60
|
-
'some',
|
|
61
|
-
'every',
|
|
62
|
-
'flatMap',
|
|
63
|
-
'sort',
|
|
64
|
-
]);
|
|
25
|
+
import { ARRAY_CALLBACK_METHODS } from '../../ts-js-hoc-utils.js';
|
|
65
26
|
/**
|
|
66
27
|
* True when `node` (an `arrow_function` / `function_expression`) is the
|
|
67
28
|
* callback argument of an array higher-order-method call, i.e. the
|
|
@@ -73,9 +34,9 @@ export const ARRAY_CALLBACK_METHODS = new Set([
|
|
|
73
34
|
* (`forwardRef(() => …)` — callee is an `identifier`, not a
|
|
74
35
|
* `member_expression`), so neither is ever suppressed.
|
|
75
36
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
37
|
+
* The helper itself only handles direct `member_expression` callees. Broader
|
|
38
|
+
* shapes like `(arr.map)(cb)` and `arr['map'](cb)` are now filtered at the
|
|
39
|
+
* query layer, so this emit-side check stays focused on the direct fallback.
|
|
79
40
|
*/
|
|
80
41
|
export function isArrayMethodCallbackArrow(node) {
|
|
81
42
|
const args = node.parent;
|
|
@@ -33,6 +33,7 @@ import { computeTsArityMetadata } from './arity-metadata.js';
|
|
|
33
33
|
import { isArrayMethodCallbackArrow } from './array-callback.js';
|
|
34
34
|
import { getTreeSitterBufferSize } from '../../constants.js';
|
|
35
35
|
import { parseSourceSafe } from '../../../tree-sitter/safe-parse.js';
|
|
36
|
+
import { deriveDefaultExportHocName, isBlockedDefaultExportHoc, isDefaultExportHocFunctionNode, } from '../../ts-js-hoc-utils.js';
|
|
36
37
|
/** tree-sitter-typescript node types for function-like scopes that may
|
|
37
38
|
* carry a synthesized `this` binding. Kept in sync with the
|
|
38
39
|
* `@scope.function` patterns in `query.ts`. */
|
|
@@ -242,6 +243,15 @@ export function emitTsScopeCaptures(sourceText, filePath, cachedTree) {
|
|
|
242
243
|
if (arrowNode !== null && isArrayMethodCallbackArrow(arrowNode)) {
|
|
243
244
|
continue;
|
|
244
245
|
}
|
|
246
|
+
if (arrowNode !== null && isBlockedDefaultExportHoc(arrowNode)) {
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (fnDeclAnchor !== undefined) {
|
|
251
|
+
const fnNode = findFunctionNode(tree.rootNode, fnDeclAnchor.range, groupedNodes['@declaration.function']);
|
|
252
|
+
if (fnNode !== null && isDefaultExportHocFunctionNode(fnNode)) {
|
|
253
|
+
grouped['@declaration.name'] = syntheticCapture('@declaration.name', fnNode, deriveDefaultExportHocName(filePath));
|
|
254
|
+
}
|
|
245
255
|
}
|
|
246
256
|
// Synthesize arity metadata on function-like declaration anchors
|
|
247
257
|
// before pushing the match. The registry uses these to narrow
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
*/
|
|
53
53
|
import Parser from 'tree-sitter';
|
|
54
54
|
import TS from 'tree-sitter-typescript';
|
|
55
|
+
import { ARRAY_METHOD_NOT_ANY_OF_PREDICATE, DEFAULT_EXPORT_IDENTIFIER_NOT_ANY_OF_PREDICATE, } from '../../ts-js-hoc-utils.js';
|
|
55
56
|
// tree-sitter-typescript exports both `typescript` and `tsx` grammars on
|
|
56
57
|
// the default export. The package's `.d.ts` types the default export
|
|
57
58
|
// loosely; we narrow at the use site. The two grammars are NOT
|
|
@@ -273,10 +274,16 @@ const TYPESCRIPT_SCOPE_QUERY = `
|
|
|
273
274
|
;; via \`(filePath, type, qualifiedName)\` — second wins. Acceptable;
|
|
274
275
|
;; multi-arrow-callback APIs are rare (\`new Promise(executor)\` is the
|
|
275
276
|
;; main one and takes a single executor).
|
|
277
|
+
;;
|
|
278
|
+
;; NOTE: Split into identifier vs member_expression patterns. Member
|
|
279
|
+
;; expressions are filtered with a blocklist of common array methods
|
|
280
|
+
;; (map, filter, reduce, etc.) to avoid false positives like
|
|
281
|
+
;; \`const x = arr.map(a => ...)\` being classified as Function.
|
|
276
282
|
(lexical_declaration
|
|
277
283
|
(variable_declarator
|
|
278
284
|
name: (identifier) @declaration.name
|
|
279
285
|
value: (call_expression
|
|
286
|
+
function: (identifier)
|
|
280
287
|
arguments: (arguments
|
|
281
288
|
(arrow_function) @declaration.function))))
|
|
282
289
|
|
|
@@ -284,13 +291,35 @@ const TYPESCRIPT_SCOPE_QUERY = `
|
|
|
284
291
|
(variable_declarator
|
|
285
292
|
name: (identifier) @declaration.name
|
|
286
293
|
value: (call_expression
|
|
294
|
+
function: (identifier)
|
|
287
295
|
arguments: (arguments
|
|
288
296
|
(function_expression) @declaration.function))))
|
|
289
297
|
|
|
298
|
+
(lexical_declaration
|
|
299
|
+
(variable_declarator
|
|
300
|
+
name: (identifier) @declaration.name
|
|
301
|
+
value: (call_expression
|
|
302
|
+
function: (member_expression
|
|
303
|
+
property: (property_identifier) @callee)
|
|
304
|
+
arguments: (arguments
|
|
305
|
+
(arrow_function) @declaration.function)))
|
|
306
|
+
${ARRAY_METHOD_NOT_ANY_OF_PREDICATE})
|
|
307
|
+
|
|
308
|
+
(lexical_declaration
|
|
309
|
+
(variable_declarator
|
|
310
|
+
name: (identifier) @declaration.name
|
|
311
|
+
value: (call_expression
|
|
312
|
+
function: (member_expression
|
|
313
|
+
property: (property_identifier) @callee)
|
|
314
|
+
arguments: (arguments
|
|
315
|
+
(function_expression) @declaration.function)))
|
|
316
|
+
${ARRAY_METHOD_NOT_ANY_OF_PREDICATE})
|
|
317
|
+
|
|
290
318
|
(variable_declaration
|
|
291
319
|
(variable_declarator
|
|
292
320
|
name: (identifier) @declaration.name
|
|
293
321
|
value: (call_expression
|
|
322
|
+
function: (identifier)
|
|
294
323
|
arguments: (arguments
|
|
295
324
|
(arrow_function) @declaration.function))))
|
|
296
325
|
|
|
@@ -298,9 +327,64 @@ const TYPESCRIPT_SCOPE_QUERY = `
|
|
|
298
327
|
(variable_declarator
|
|
299
328
|
name: (identifier) @declaration.name
|
|
300
329
|
value: (call_expression
|
|
330
|
+
function: (identifier)
|
|
301
331
|
arguments: (arguments
|
|
302
332
|
(function_expression) @declaration.function))))
|
|
303
333
|
|
|
334
|
+
(variable_declaration
|
|
335
|
+
(variable_declarator
|
|
336
|
+
name: (identifier) @declaration.name
|
|
337
|
+
value: (call_expression
|
|
338
|
+
function: (member_expression
|
|
339
|
+
property: (property_identifier) @callee)
|
|
340
|
+
arguments: (arguments
|
|
341
|
+
(arrow_function) @declaration.function)))
|
|
342
|
+
${ARRAY_METHOD_NOT_ANY_OF_PREDICATE})
|
|
343
|
+
|
|
344
|
+
(variable_declaration
|
|
345
|
+
(variable_declarator
|
|
346
|
+
name: (identifier) @declaration.name
|
|
347
|
+
value: (call_expression
|
|
348
|
+
function: (member_expression
|
|
349
|
+
property: (property_identifier) @callee)
|
|
350
|
+
arguments: (arguments
|
|
351
|
+
(function_expression) @declaration.function)))
|
|
352
|
+
${ARRAY_METHOD_NOT_ANY_OF_PREDICATE})
|
|
353
|
+
|
|
354
|
+
;; HOC-wrapped default exports: \`export default defineEventHandler(async (e) => { ... })\`.
|
|
355
|
+
;; The emit phase rewrites @declaration.name to a file-derived name so
|
|
356
|
+
;; wrappers like \`defineEventHandler\` / \`React.memo\` do not collapse
|
|
357
|
+
;; unrelated modules onto the same symbol name.
|
|
358
|
+
((export_statement
|
|
359
|
+
value: (call_expression
|
|
360
|
+
function: (identifier) @hoc
|
|
361
|
+
arguments: (arguments
|
|
362
|
+
(arrow_function) @declaration.function)))
|
|
363
|
+
${DEFAULT_EXPORT_IDENTIFIER_NOT_ANY_OF_PREDICATE})
|
|
364
|
+
|
|
365
|
+
((export_statement
|
|
366
|
+
value: (call_expression
|
|
367
|
+
function: (identifier) @hoc
|
|
368
|
+
arguments: (arguments
|
|
369
|
+
(function_expression) @declaration.function)))
|
|
370
|
+
${DEFAULT_EXPORT_IDENTIFIER_NOT_ANY_OF_PREDICATE})
|
|
371
|
+
|
|
372
|
+
((export_statement
|
|
373
|
+
value: (call_expression
|
|
374
|
+
function: (member_expression
|
|
375
|
+
property: (property_identifier) @callee)
|
|
376
|
+
arguments: (arguments
|
|
377
|
+
(arrow_function) @declaration.function)))
|
|
378
|
+
${ARRAY_METHOD_NOT_ANY_OF_PREDICATE})
|
|
379
|
+
|
|
380
|
+
((export_statement
|
|
381
|
+
value: (call_expression
|
|
382
|
+
function: (member_expression
|
|
383
|
+
property: (property_identifier) @callee)
|
|
384
|
+
arguments: (arguments
|
|
385
|
+
(function_expression) @declaration.function)))
|
|
386
|
+
${ARRAY_METHOD_NOT_ANY_OF_PREDICATE})
|
|
387
|
+
|
|
304
388
|
;; Method definitions — regular + private (#field) methods.
|
|
305
389
|
(method_definition
|
|
306
390
|
name: (property_identifier) @declaration.name) @declaration.method
|
|
@@ -26,6 +26,7 @@ import { typescriptVariableConfig, javascriptVariableConfig, } from '../variable
|
|
|
26
26
|
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
27
27
|
import { typescriptCallConfig, javascriptCallConfig, } from '../call-extractors/configs/typescript-javascript.js';
|
|
28
28
|
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
|
29
|
+
import { ARRAY_METHOD_HOC_BLOCKLIST_SET, DEFAULT_EXPORT_IDENTIFIER_BLOCKLIST_SET, deriveDefaultExportHocName, } from '../ts-js-hoc-utils.js';
|
|
29
30
|
import { emitTsScopeCaptures, interpretTsImport, interpretTsTypeBinding, tsBindingScopeFor, tsImportOwningScope, tsReceiverBinding, typescriptMergeBindings, typescriptArityCompatibility, resolveTsImportTarget, } from './typescript/index.js';
|
|
30
31
|
import { emitJsScopeCaptures, interpretJsImport, interpretJsTypeBinding, jsBindingScopeFor, jsImportOwningScope, jsReceiverBinding, jsMergeBindings, jsArityCompatibility, } from './javascript/index.js';
|
|
31
32
|
/**
|
|
@@ -54,7 +55,7 @@ import { emitJsScopeCaptures, interpretJsImport, interpretJsTypeBinding, jsBindi
|
|
|
54
55
|
* findEnclosingFunctionId then continues up to the next named ancestor
|
|
55
56
|
* (or to the file).
|
|
56
57
|
*/
|
|
57
|
-
const tsExtractFunctionName = (node) => {
|
|
58
|
+
const tsExtractFunctionName = (node, filePath) => {
|
|
58
59
|
if (node.type !== 'arrow_function' && node.type !== 'function_expression')
|
|
59
60
|
return null;
|
|
60
61
|
const parent = node.parent;
|
|
@@ -99,27 +100,56 @@ const tsExtractFunctionName = (node) => {
|
|
|
99
100
|
// `arguments`, grandparent is `call_expression`, great-grandparent is
|
|
100
101
|
// `variable_declarator`. Walk the chain up and take the variable's name
|
|
101
102
|
// — the meaningful identifier the developer wrote on the LHS. Mirrors
|
|
102
|
-
// the four registry-primary patterns in `typescript/query.ts`.
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
// calls (`const x = arr.find((y) => p(y))`) match too and produce a
|
|
108
|
-
// mostly-harmless `Function:x` (consumed as a value, never invoked),
|
|
109
|
-
// accepted as a small false-positive cost vs. the much larger gain of
|
|
110
|
-
// capturing the React UI-component idiom.
|
|
103
|
+
// the four registry-primary patterns in `typescript/query.ts`.
|
|
104
|
+
//
|
|
105
|
+
// NOTE: Excludes common array methods (map, filter, reduce, etc.) to avoid
|
|
106
|
+
// false positives like `const x = arr.map(a => ...)` being classified as
|
|
107
|
+
// Function when it's actually a Const holding an array.
|
|
111
108
|
if (parent.type === 'arguments') {
|
|
112
109
|
const callExpr = parent.parent;
|
|
113
110
|
if (!callExpr || callExpr.type !== 'call_expression') {
|
|
114
111
|
return { funcName: null, label: 'Function' };
|
|
115
112
|
}
|
|
113
|
+
// Check if callee is a member_expression calling an array method
|
|
114
|
+
const callee = callExpr.childForFieldName?.('function');
|
|
115
|
+
if (callee?.type === 'member_expression') {
|
|
116
|
+
const property = callee.childForFieldName?.('property');
|
|
117
|
+
if (property?.type === 'property_identifier' &&
|
|
118
|
+
ARRAY_METHOD_HOC_BLOCKLIST_SET.has(property.text)) {
|
|
119
|
+
return { funcName: null, label: 'Function' };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
116
122
|
const declarator = callExpr.parent;
|
|
117
|
-
|
|
123
|
+
// Existing path: const X = HOC(arrow)
|
|
124
|
+
if (declarator?.type === 'variable_declarator') {
|
|
125
|
+
const nameNode = declarator.childForFieldName?.('name');
|
|
126
|
+
if (nameNode?.type === 'identifier') {
|
|
127
|
+
return { funcName: nameNode.text, label: 'Function' };
|
|
128
|
+
}
|
|
118
129
|
return { funcName: null, label: 'Function' };
|
|
119
130
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
131
|
+
// export default HOC(arrow) — name it from the file, not the wrapper.
|
|
132
|
+
// This keeps route handlers and wrapped defaults navigable without
|
|
133
|
+
// collapsing every file onto names like `memo` or `defineEventHandler`.
|
|
134
|
+
if (declarator?.type === 'export_statement') {
|
|
135
|
+
if (callee?.type === 'identifier') {
|
|
136
|
+
if (DEFAULT_EXPORT_IDENTIFIER_BLOCKLIST_SET.has(callee.text)) {
|
|
137
|
+
return { funcName: null, label: 'Function' };
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
funcName: filePath ? deriveDefaultExportHocName(filePath) : null,
|
|
141
|
+
label: 'Function',
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
// Member-expression callees like React.memo keep the same file-derived
|
|
145
|
+
// name, with array-like helpers excluded above.
|
|
146
|
+
if (callee?.type === 'member_expression') {
|
|
147
|
+
return {
|
|
148
|
+
funcName: filePath ? deriveDefaultExportHocName(filePath) : null,
|
|
149
|
+
label: 'Function',
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return { funcName: null, label: 'Function' };
|
|
123
153
|
}
|
|
124
154
|
return { funcName: null, label: 'Function' };
|
|
125
155
|
}
|
|
@@ -50,7 +50,7 @@ export interface MethodExtractor {
|
|
|
50
50
|
* unwrapping, Swift init/deinit, Rust impl_item) provide this hook
|
|
51
51
|
* to replace the generic name-field lookup.
|
|
52
52
|
* Return null to fall through to the generic extractor. */
|
|
53
|
-
extractFunctionName?(node: SyntaxNode): {
|
|
53
|
+
extractFunctionName?(node: SyntaxNode, filePath?: string): {
|
|
54
54
|
funcName: string | null;
|
|
55
55
|
label: import('../../_shared/index.js').NodeLabel;
|
|
56
56
|
} | null;
|
|
@@ -84,7 +84,7 @@ export interface MethodExtractionConfig {
|
|
|
84
84
|
extractPrimaryConstructor?: (ownerNode: SyntaxNode, context: MethodExtractorContext) => MethodInfo | null;
|
|
85
85
|
/** Extract function name + label from an AST node during parent-walk.
|
|
86
86
|
* Passed through to the MethodExtractor by createMethodExtractor. */
|
|
87
|
-
extractFunctionName?: (node: SyntaxNode) => {
|
|
87
|
+
extractFunctionName?: (node: SyntaxNode, filePath?: string) => {
|
|
88
88
|
funcName: string | null;
|
|
89
89
|
label: import('../../_shared/index.js').NodeLabel;
|
|
90
90
|
} | null;
|