gitnexus 1.6.13-rc.91 → 1.6.13-rc.92
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.d.ts +7 -7
- package/dist/core/ingestion/call-processor.js +38 -28
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +1 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +3 -1
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +2 -0
- package/dist/core/ingestion/pipeline-phases/routes.js +11 -6
- package/dist/core/ingestion/route-extractors/route-path.d.ts +5 -3
- package/dist/core/ingestion/route-extractors/route-path.js +9 -3
- package/dist/core/ingestion/workers/parse-worker.js +47 -11
- package/dist/storage/parse-cache.js +8 -1
- package/package.json +1 -1
|
@@ -91,12 +91,12 @@ export declare const processRoutesFromExtracted: (graph: KnowledgeGraph, extract
|
|
|
91
91
|
* `handlerName` (the decorated method, captured at extraction); resolve it
|
|
92
92
|
* directly in the route's own file.
|
|
93
93
|
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
94
|
+
* Production declarations take precedence over test declarations; equal-priority
|
|
95
|
+
* candidates remain first-writer-wins. The selected declaration reserves its key **even when its
|
|
96
|
+
* handler is unresolvable**, so a losing route can never donate its handler.
|
|
97
|
+
* When supplied, `selectedRoutes` receives the admitted winners for the routes
|
|
98
|
+
* phase, keeping node provenance and handler provenance identical.
|
|
99
|
+
* Keying is `routeNodeKey(method, url)` (#2289): a
|
|
100
100
|
* same-URL multi-verb pair (`GET /x` + `POST /x`) resolves two handlers, one per
|
|
101
101
|
* node; method-less / wildcard routes key by URL alone, byte-identical to the
|
|
102
102
|
* pre-#2289 behavior. Routes whose handler cannot be *uniquely* resolved (no
|
|
@@ -104,7 +104,7 @@ export declare const processRoutesFromExtracted: (graph: KnowledgeGraph, extract
|
|
|
104
104
|
* `handlerSymbolId`; the extractor then falls back to source scan for that route
|
|
105
105
|
* (fail-open, no regression, never a wrong handler).
|
|
106
106
|
*/
|
|
107
|
-
export declare function resolveRouteHandlerSymbols(model: SemanticModel, extractedRoutes: readonly ExtractedRoute[], decoratorRoutes: readonly ExtractedDecoratorRoute[], routeContext?: RouteHandlerResolutionContext): Map<string, string>;
|
|
107
|
+
export declare function resolveRouteHandlerSymbols(model: SemanticModel, extractedRoutes: readonly ExtractedRoute[], decoratorRoutes: readonly ExtractedDecoratorRoute[], routeContext?: RouteHandlerResolutionContext, selectedRoutes?: Set<ExtractedRoute | ExtractedDecoratorRoute>): Map<string, string>;
|
|
108
108
|
/**
|
|
109
109
|
* Extract property access keys from a consumer file's source code near fetch calls.
|
|
110
110
|
*
|
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
import { generateId } from '../../lib/utils.js';
|
|
18
18
|
import { yieldToEventLoop } from './utils/event-loop.js';
|
|
19
19
|
import { normalizeFetchURL, routeMatches } from './route-extractors/nextjs.js';
|
|
20
|
-
import { normalizeExtractedRoutePath, normalizeRouteMethod, routeNodeKey, } from './route-extractors/route-path.js';
|
|
20
|
+
import { isTestRouteFile, normalizeExtractedRoutePath, normalizeRouteMethod, routeNodeKey, } from './route-extractors/route-path.js';
|
|
21
21
|
import { extractReturnTypeName } from './type-extractors/shared.js';
|
|
22
22
|
import { DATA_ROUTE_TABLE_SOURCE } from './route-extractors/data-route-table.js';
|
|
23
|
+
import { reconcileDispatchGuardRoutes } from './route-extractors/dispatch-guard.js';
|
|
23
24
|
import { toZeroBasedLine } from './utils/line-base.js';
|
|
24
25
|
const MAX_EXPORTS_PER_FILE = 500;
|
|
25
26
|
const MAX_TYPE_NAME_LENGTH = 256;
|
|
@@ -292,12 +293,12 @@ export const processRoutesFromExtracted = async (graph, extractedRoutes, model,
|
|
|
292
293
|
* `handlerName` (the decorated method, captured at extraction); resolve it
|
|
293
294
|
* directly in the route's own file.
|
|
294
295
|
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
296
|
+
* Production declarations take precedence over test declarations; equal-priority
|
|
297
|
+
* candidates remain first-writer-wins. The selected declaration reserves its key **even when its
|
|
298
|
+
* handler is unresolvable**, so a losing route can never donate its handler.
|
|
299
|
+
* When supplied, `selectedRoutes` receives the admitted winners for the routes
|
|
300
|
+
* phase, keeping node provenance and handler provenance identical.
|
|
301
|
+
* Keying is `routeNodeKey(method, url)` (#2289): a
|
|
301
302
|
* same-URL multi-verb pair (`GET /x` + `POST /x`) resolves two handlers, one per
|
|
302
303
|
* node; method-less / wildcard routes key by URL alone, byte-identical to the
|
|
303
304
|
* pre-#2289 behavior. Routes whose handler cannot be *uniquely* resolved (no
|
|
@@ -305,12 +306,10 @@ export const processRoutesFromExtracted = async (graph, extractedRoutes, model,
|
|
|
305
306
|
* `handlerSymbolId`; the extractor then falls back to source scan for that route
|
|
306
307
|
* (fail-open, no regression, never a wrong handler).
|
|
307
308
|
*/
|
|
308
|
-
export function resolveRouteHandlerSymbols(model, extractedRoutes, decoratorRoutes, routeContext) {
|
|
309
|
+
export function resolveRouteHandlerSymbols(model, extractedRoutes, decoratorRoutes, routeContext, selectedRoutes) {
|
|
309
310
|
const out = new Map();
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
// stamp always belongs to the route that actually won the Route node.
|
|
313
|
-
const claimed = new Set();
|
|
311
|
+
const claimed = new Map();
|
|
312
|
+
const admittedDecoratorRoutes = reconcileDispatchGuardRoutes(decoratorRoutes);
|
|
314
313
|
// Resolve a single same-file symbol by name. Exactly one match → its nodeId.
|
|
315
314
|
// Zero or many matches → undefined (fail-open). tRPC same-name handlers
|
|
316
315
|
// use pickSameFileHandler at the controller-less call site instead.
|
|
@@ -386,21 +385,29 @@ export function resolveRouteHandlerSymbols(model, extractedRoutes, decoratorRout
|
|
|
386
385
|
? undefined
|
|
387
386
|
: uniqueById(model.methods.lookupAllByOwner(owner.nodeId, member))?.nodeId;
|
|
388
387
|
};
|
|
389
|
-
const claim = (
|
|
388
|
+
const claim = (route, symbolId) => {
|
|
390
389
|
// An empty path is a valid, pathless mapping and normalizes to either `/`
|
|
391
390
|
// or its class/router prefix. Only null means the extractor had no route.
|
|
392
|
-
if (routePath === null)
|
|
391
|
+
if (route.routePath === null)
|
|
393
392
|
return;
|
|
394
|
-
const url = normalizeExtractedRoutePath(routePath, prefix);
|
|
395
|
-
const key = routeNodeKey(normalizeRouteMethod(httpMethod), url);
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
393
|
+
const url = normalizeExtractedRoutePath(route.routePath, route.prefix ?? null);
|
|
394
|
+
const key = routeNodeKey(normalizeRouteMethod(route.httpMethod), url);
|
|
395
|
+
const previous = claimed.get(key);
|
|
396
|
+
if (previous && (!isTestRouteFile(previous.filePath) || isTestRouteFile(route.filePath)))
|
|
397
|
+
return;
|
|
398
|
+
if (previous)
|
|
399
|
+
selectedRoutes?.delete(previous);
|
|
400
|
+
claimed.set(key, route);
|
|
401
|
+
selectedRoutes?.add(route);
|
|
402
|
+
out.delete(key);
|
|
399
403
|
if (symbolId)
|
|
400
404
|
out.set(key, symbolId);
|
|
401
405
|
};
|
|
402
406
|
// Laravel framework routes — controller class + method name.
|
|
403
407
|
for (const route of extractedRoutes) {
|
|
408
|
+
// Match the routes phase's admission rule for extracted declarations.
|
|
409
|
+
if (!route.routePath)
|
|
410
|
+
continue;
|
|
404
411
|
let methodId;
|
|
405
412
|
if (route.controllerName && route.methodName) {
|
|
406
413
|
let controllerDef;
|
|
@@ -418,16 +425,21 @@ export function resolveRouteHandlerSymbols(model, extractedRoutes, decoratorRout
|
|
|
418
425
|
else if (!route.controllerName && route.methodName) {
|
|
419
426
|
methodId = pickSameFileHandler(model.symbols.lookupExactAll(route.filePath, route.methodName), route, routeContext?.nodeStartLine)?.nodeId;
|
|
420
427
|
}
|
|
421
|
-
claim(route
|
|
428
|
+
claim(route, methodId);
|
|
422
429
|
}
|
|
423
430
|
const dataHandlerByRoute = new Map();
|
|
424
431
|
const dataHandlersByIdentity = new Map();
|
|
425
|
-
|
|
432
|
+
const dataIdentity = (dr) => {
|
|
433
|
+
const url = normalizeExtractedRoutePath(dr.routePath, dr.prefix ?? null);
|
|
434
|
+
return `${Number(isTestRouteFile(dr.filePath))}:${routeNodeKey(normalizeRouteMethod(dr.httpMethod), url)}`;
|
|
435
|
+
};
|
|
436
|
+
for (const dr of admittedDecoratorRoutes) {
|
|
426
437
|
if (dr.source !== DATA_ROUTE_TABLE_SOURCE || !dr.handlerName || !dr.routePath)
|
|
427
438
|
continue;
|
|
428
439
|
const handlerId = resolveDataRouteHandler(dr.filePath, dr.handlerName);
|
|
429
|
-
|
|
430
|
-
|
|
440
|
+
// Conflicting test handlers must not invalidate a production table.
|
|
441
|
+
// Ambiguity is still rejected within each priority tier.
|
|
442
|
+
const key = dataIdentity(dr);
|
|
431
443
|
const state = dataHandlersByIdentity.get(key) ?? {
|
|
432
444
|
handlers: new Set(),
|
|
433
445
|
hasUnresolved: false,
|
|
@@ -463,20 +475,18 @@ export function resolveRouteHandlerSymbols(model, extractedRoutes, decoratorRout
|
|
|
463
475
|
// handlers. Data tables additionally suppress an identity when duplicate
|
|
464
476
|
// entries resolve to different handlers: recording either one would invent a
|
|
465
477
|
// single-winner dispatch that the loop does not prove.
|
|
466
|
-
for (const dr of
|
|
478
|
+
for (const dr of admittedDecoratorRoutes) {
|
|
467
479
|
const handlerId = decoratorHandlerId(dr);
|
|
468
480
|
// An unproven data-table entry never becomes a Route node, so it must not
|
|
469
481
|
// reserve the identity and suppress a later, valid framework declaration.
|
|
470
482
|
if (dr.source === DATA_ROUTE_TABLE_SOURCE && handlerId === undefined)
|
|
471
483
|
continue;
|
|
472
484
|
if (dr.source === DATA_ROUTE_TABLE_SOURCE && dr.routePath) {
|
|
473
|
-
const
|
|
474
|
-
const key = routeNodeKey(normalizeRouteMethod(dr.httpMethod), url);
|
|
475
|
-
const state = dataHandlersByIdentity.get(key);
|
|
485
|
+
const state = dataHandlersByIdentity.get(dataIdentity(dr));
|
|
476
486
|
if (state === undefined || state.hasUnresolved || state.handlers.size !== 1)
|
|
477
487
|
continue;
|
|
478
488
|
}
|
|
479
|
-
claim(dr
|
|
489
|
+
claim(dr, handlerId);
|
|
480
490
|
}
|
|
481
491
|
return out;
|
|
482
492
|
}
|
|
@@ -68,6 +68,7 @@ export declare function runChunkedParseAndResolve(graph: KnowledgeGraph, scanned
|
|
|
68
68
|
allFetchWrapperDefs: FetchWrapperDef[];
|
|
69
69
|
allExtractedRoutes: ExtractedRoute[];
|
|
70
70
|
allDecoratorRoutes: ExtractedDecoratorRoute[];
|
|
71
|
+
selectedRoutes: ReadonlySet<ExtractedRoute | ExtractedDecoratorRoute>;
|
|
71
72
|
allToolDefs: ExtractedToolDef[];
|
|
72
73
|
allORMQueries: ExtractedORMQuery[];
|
|
73
74
|
bindingAccumulator: BindingAccumulator;
|
|
@@ -1553,6 +1553,7 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
1553
1553
|
parsedImports: parsed.parsedImports.map((parsedImport) => resolveBinding(parsedImport, () => resolveRouteImportTargets(parsedImport, parsed.filePath), routeSourceTextFor)),
|
|
1554
1554
|
};
|
|
1555
1555
|
});
|
|
1556
|
+
const selectedRoutes = new Set();
|
|
1556
1557
|
const routeHandlerSymbols = resolveRouteHandlerSymbols(model, allExtractedRoutes, allDecoratorRoutes, {
|
|
1557
1558
|
files: routeResolutionFiles,
|
|
1558
1559
|
resolveImportTarget: resolveRouteImportTarget,
|
|
@@ -1563,13 +1564,14 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
1563
1564
|
const n = graph.getNode(id);
|
|
1564
1565
|
return typeof n?.properties.startLine === 'number' ? n.properties.startLine : undefined;
|
|
1565
1566
|
},
|
|
1566
|
-
});
|
|
1567
|
+
}, selectedRoutes);
|
|
1567
1568
|
return {
|
|
1568
1569
|
exportedTypeMap,
|
|
1569
1570
|
allFetchCalls,
|
|
1570
1571
|
allFetchWrapperDefs,
|
|
1571
1572
|
allExtractedRoutes,
|
|
1572
1573
|
allDecoratorRoutes,
|
|
1574
|
+
selectedRoutes,
|
|
1573
1575
|
allToolDefs,
|
|
1574
1576
|
allORMQueries,
|
|
1575
1577
|
bindingAccumulator,
|
|
@@ -38,6 +38,8 @@ export interface ParseOutput {
|
|
|
38
38
|
readonly allFetchWrapperDefs: readonly FetchWrapperDef[];
|
|
39
39
|
readonly allExtractedRoutes: readonly ExtractedRoute[];
|
|
40
40
|
readonly allDecoratorRoutes: readonly ExtractedDecoratorRoute[];
|
|
41
|
+
/** Exact admitted declarations that own Route nodes and handler attribution. */
|
|
42
|
+
readonly selectedRoutes: ReadonlySet<ExtractedRoute | ExtractedDecoratorRoute>;
|
|
41
43
|
readonly allToolDefs: readonly ExtractedToolDef[];
|
|
42
44
|
readonly allORMQueries: readonly ExtractedORMQuery[];
|
|
43
45
|
/** Route URL → resolved handler symbol UID (Part 2, #2138). Consumed by the
|
|
@@ -109,7 +109,7 @@ export const routesPhase = {
|
|
|
109
109
|
name: 'routes',
|
|
110
110
|
deps: ['parse'],
|
|
111
111
|
async execute(ctx, deps) {
|
|
112
|
-
const { allPaths, allFetchCalls: parseFetchCalls, allFetchWrapperDefs, allExtractedRoutes, allDecoratorRoutes, routeHandlerSymbols, } = getPhaseOutput(deps, 'parse');
|
|
112
|
+
const { allPaths, allFetchCalls: parseFetchCalls, allFetchWrapperDefs, allExtractedRoutes, allDecoratorRoutes, routeHandlerSymbols, selectedRoutes, } = getPhaseOutput(deps, 'parse');
|
|
113
113
|
// Local copy — routes phase must not mutate upstream ParseOutput
|
|
114
114
|
const allFetchCalls = [...parseFetchCalls];
|
|
115
115
|
const routeRegistry = new Map();
|
|
@@ -226,11 +226,14 @@ export const routesPhase = {
|
|
|
226
226
|
if (!route.routePath)
|
|
227
227
|
continue;
|
|
228
228
|
const routeUrl = normalizeExtractedRoutePath(route.routePath, route.prefix);
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
229
|
+
if (!selectedRoutes || selectedRoutes.has(route)) {
|
|
230
|
+
addRoute(routeUrl, {
|
|
231
|
+
filePath: route.filePath,
|
|
232
|
+
source: 'framework-route',
|
|
233
|
+
method: normalizeRouteMethod(route.httpMethod),
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
// Losing declarations may still provide distinct named aliases.
|
|
234
237
|
if (route.routeName && !namedRouteRegistry.has(route.routeName)) {
|
|
235
238
|
namedRouteRegistry.set(route.routeName, routeUrl);
|
|
236
239
|
}
|
|
@@ -240,6 +243,8 @@ export const routesPhase = {
|
|
|
240
243
|
// idiom, which no single file can reconcile. Framework routes are untouched;
|
|
241
244
|
// their verb-less form is a declaration, not a weaker observation.
|
|
242
245
|
for (const dr of reconcileDispatchGuardRoutes(allDecoratorRoutes)) {
|
|
246
|
+
if (selectedRoutes && !selectedRoutes.has(dr))
|
|
247
|
+
continue;
|
|
243
248
|
const url = normalizeExtractedRoutePath(dr.routePath, dr.prefix ?? null);
|
|
244
249
|
const method = normalizeRouteMethod(dr.httpMethod);
|
|
245
250
|
const routeKey = routeNodeKey(method, url);
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared route-path normalization.
|
|
2
|
+
* Shared route-path normalization, route identity, and test-file classification.
|
|
3
3
|
*
|
|
4
4
|
* Extracted from the routes phase so both the routes phase (which creates the
|
|
5
5
|
* `Route` graph node, keyed by `(method, url)` via `routeNodeKey` — #2289) and
|
|
6
6
|
* the parse phase (which resolves each route's handler symbol and needs the
|
|
7
7
|
* SAME key to associate the resolved id back to the route) can compute an
|
|
8
|
-
* identical route identity without a phase-to-phase import cycle.
|
|
9
|
-
*
|
|
8
|
+
* identical route identity without a phase-to-phase import cycle. Test-route
|
|
9
|
+
* classification reuses the shared test-file path classifier.
|
|
10
10
|
*/
|
|
11
11
|
/**
|
|
12
12
|
* Join a route's path with its (optional) prefix into a normalized,
|
|
@@ -37,3 +37,5 @@ export declare function normalizeRouteMethod(raw: string | null | undefined): st
|
|
|
37
37
|
* identically.
|
|
38
38
|
*/
|
|
39
39
|
export declare function routeNodeKey(method: string | undefined, url: string): string;
|
|
40
|
+
/** Keep test registrations available, but let production files win duplicate route identities. */
|
|
41
|
+
export declare function isTestRouteFile(filePath: string): boolean;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { isTestFilePath } from '../utils/test-file-path.js';
|
|
1
2
|
/**
|
|
2
|
-
* Shared route-path normalization.
|
|
3
|
+
* Shared route-path normalization, route identity, and test-file classification.
|
|
3
4
|
*
|
|
4
5
|
* Extracted from the routes phase so both the routes phase (which creates the
|
|
5
6
|
* `Route` graph node, keyed by `(method, url)` via `routeNodeKey` — #2289) and
|
|
6
7
|
* the parse phase (which resolves each route's handler symbol and needs the
|
|
7
8
|
* SAME key to associate the resolved id back to the route) can compute an
|
|
8
|
-
* identical route identity without a phase-to-phase import cycle.
|
|
9
|
-
*
|
|
9
|
+
* identical route identity without a phase-to-phase import cycle. Test-route
|
|
10
|
+
* classification reuses the shared test-file path classifier.
|
|
10
11
|
*/
|
|
11
12
|
/**
|
|
12
13
|
* Join a route's path with its (optional) prefix into a normalized,
|
|
@@ -64,3 +65,8 @@ export function normalizeRouteMethod(raw) {
|
|
|
64
65
|
export function routeNodeKey(method, url) {
|
|
65
66
|
return method && method !== '*' ? `${method} ${url}` : url;
|
|
66
67
|
}
|
|
68
|
+
/** Keep test registrations available, but let production files win duplicate route identities. */
|
|
69
|
+
export function isTestRouteFile(filePath) {
|
|
70
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
71
|
+
return isTestFilePath(normalized) || /(^|\/)e2e(\/|$)/i.test(normalized);
|
|
72
|
+
}
|
|
@@ -71,7 +71,7 @@ catch { }
|
|
|
71
71
|
import { getLanguageFromFilename } from '../../../_shared/index.js';
|
|
72
72
|
import { buildDefinitionPreScan, FUNCTION_NODE_TYPES, findAncestorBeforeBoundary, findSplitBodyCallableAncestor, SPLIT_SIGNATURE_NODE_TYPES, getDefinitionNodeFromCaptures, findEnclosingClassInfo, findObjectLiteralBindingInfo, isArrayContainedObjectLiteralMember, findReturnShapeOwnerInfo, isReturnShapeProperty, findMemberAssignmentOwnerInfo, isCjsDefaultExportAssignment, getLabelFromCaptures, genericFuncName, inferFunctionLabel, isSuppressedConcreteTypedefDuplicate, isValueDefinitionLabel, isQualifiableScopeLabel, MEMBER_OWNER_NODE_TYPES, qualifyByEnclosingModScope, qualifyRustImplTargetByModScope, CLASS_CONTAINER_TYPES, PARAMETER_LIST_NODE_TYPES, LOCAL_SCOPE_BODY_NODE_TYPES, } from '../utils/ast-helpers.js';
|
|
73
73
|
import { isPositionQualifiedLocalLabel } from '../utils/callable-labels.js';
|
|
74
|
-
import { extractCallArgTypes } from '../utils/call-analysis.js';
|
|
74
|
+
import { countCallArguments, extractCallArgTypes, } from '../utils/call-analysis.js';
|
|
75
75
|
import { buildTypeEnv } from '../type-env.js';
|
|
76
76
|
import { detectFrameworkFromAST } from '../framework-detection.js';
|
|
77
77
|
import { generateId } from '../../../lib/utils.js';
|
|
@@ -1404,6 +1404,11 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1404
1404
|
// HTTP client calls like axios.get('/api/users') that match the same pattern
|
|
1405
1405
|
// as Express route registrations.
|
|
1406
1406
|
const callNode = captureMap['express_route'];
|
|
1407
|
+
// route(path) returns a builder; verb registrations need a handler.
|
|
1408
|
+
// Count arguments without comments, which are also named AST children.
|
|
1409
|
+
const minimumArguments = method === 'route' ? 1 : 2;
|
|
1410
|
+
if ((countCallArguments(callNode) ?? 0) < minimumArguments)
|
|
1411
|
+
continue;
|
|
1407
1412
|
const funcNode = callNode.childForFieldName?.('function') ?? callNode.children?.[0];
|
|
1408
1413
|
// Walk through nested member_expressions and call_expressions to
|
|
1409
1414
|
// reach the innermost receiver identifier. Handles chains like:
|
|
@@ -1439,16 +1444,47 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1439
1444
|
// This is an HTTP client call, not a route definition u2014 skip it
|
|
1440
1445
|
continue;
|
|
1441
1446
|
}
|
|
1442
|
-
const
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1447
|
+
const registrations = [];
|
|
1448
|
+
if (method === 'route') {
|
|
1449
|
+
// A builder alone registers nothing. Follow only direct chained
|
|
1450
|
+
// verb calls, each of which must supply a non-comment handler arg.
|
|
1451
|
+
let builderNode = callNode;
|
|
1452
|
+
while (builderNode.parent?.type === 'member_expression') {
|
|
1453
|
+
const member = builderNode.parent;
|
|
1454
|
+
const registration = member.parent;
|
|
1455
|
+
const verb = member.childForFieldName('property')?.text;
|
|
1456
|
+
if (member.childForFieldName('object')?.id !== builderNode.id ||
|
|
1457
|
+
registration?.type !== 'call_expression' ||
|
|
1458
|
+
registration.childForFieldName('function')?.id !== member.id ||
|
|
1459
|
+
!verb ||
|
|
1460
|
+
!EXPRESS_ROUTE_METHODS.has(verb) ||
|
|
1461
|
+
verb === 'route' ||
|
|
1462
|
+
verb === 'use') {
|
|
1463
|
+
break;
|
|
1464
|
+
}
|
|
1465
|
+
if ((countCallArguments(registration) ?? 0) >= 1) {
|
|
1466
|
+
registrations.push({ method: verb, node: registration });
|
|
1467
|
+
}
|
|
1468
|
+
builderNode = registration;
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
else {
|
|
1472
|
+
registrations.push({ method, node: callNode });
|
|
1473
|
+
}
|
|
1474
|
+
for (const registration of registrations) {
|
|
1475
|
+
const httpMethod = registration.method === 'all'
|
|
1476
|
+
? '*'
|
|
1477
|
+
: registration.method === 'use'
|
|
1478
|
+
? 'GET'
|
|
1479
|
+
: registration.method.toUpperCase();
|
|
1480
|
+
result.decoratorRoutes.push({
|
|
1481
|
+
filePath: file.path,
|
|
1482
|
+
routePath,
|
|
1483
|
+
httpMethod,
|
|
1484
|
+
decoratorName: `express.${registration.method}`,
|
|
1485
|
+
lineNumber: registration.node.startPosition.row + lineOffset,
|
|
1486
|
+
});
|
|
1487
|
+
}
|
|
1452
1488
|
}
|
|
1453
1489
|
continue;
|
|
1454
1490
|
}
|
|
@@ -833,7 +833,14 @@ import { copyV8CacheIfPresent, tryLoadV8Cache, writeV8CacheFile } from './v8-sid
|
|
|
833
833
|
// v131 (#3504): Python namespace imports retain explicit alias syntax. Warm
|
|
834
834
|
// v130 ParsedFiles lack this fact and can bind a root-spelled alias to the
|
|
835
835
|
// package root instead of the imported module.
|
|
836
|
-
|
|
836
|
+
// v132 (#3487, #3505): merge the parallel Express route capture changes.
|
|
837
|
+
// Require semantic handler arguments, retain route(path) builders, and preserve
|
|
838
|
+
// app.all as method-agnostic. Both branches used v131 for different captures,
|
|
839
|
+
// so invalidate either branch's cache before reusing the combined schema.
|
|
840
|
+
// v133 (#3505): route(path) builders emit only chained verb registrations
|
|
841
|
+
// with semantic handler arguments. Warm v132 caches can retain phantom GET
|
|
842
|
+
// routes for bare builders and lose the actual methods of chained handlers.
|
|
843
|
+
const SCHEMA_BUMP = 133;
|
|
837
844
|
const GITNEXUS_PKG_VERSION = (() => {
|
|
838
845
|
try {
|
|
839
846
|
// package.json sits at gitnexus/package.json — two levels up from
|
package/package.json
CHANGED