mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-09 03:17:54 +00:00
fix(ingestion): capture only LHS binding names in Nitro server-util exports
The Nuxt server-util export scanner ran a declarator regex over the whole
`export const …` right-hand side, so it registered RHS tokens as auto-import
names: arrow-function parameters (`export const f = (event) => …` → `event`),
object-literal keys (`export const c = { onError } ` → `onError`), and bare
operands. It also dropped generic-typed declarators
(`export const x: Map<a, b> = …`) because the type-annotation skip broke at the
comma inside the generic. Both produced wrong/missing auto-import CALLS edges.
Capture only the leading binding name of each top-level declarator via a
depth-aware comma splitter (tracks (), [], {}, <>), skipping destructuring
patterns. Nitro auto-imports only surface top-level binding names, so the RHS
is never parsed. Adds unit coverage for the param/object-key/operand/generic
and multi-declarator forms.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4W25WLfYD1JNy8icxeLPU
This commit is contained in:
parent
8743ff1d29
commit
2fc4069430
2 changed files with 66 additions and 6 deletions
|
|
@ -77,7 +77,9 @@ const IMPORTS_DTS_EXPORT_RE = /^export\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g
|
|||
const NITRO_DECLARATION_EXPORT_RE =
|
||||
/^export\s+(?:default\s+)?(?:async\s+)?function\s+([A-Za-z_$][A-Za-z0-9_$]*)|^export\s+(?:default\s+)?class\s+([A-Za-z_$][A-Za-z0-9_$]*)/gm;
|
||||
const NITRO_VARIABLE_EXPORT_RE = /^export\s+(?:const|let|var)\s+([^;\n]+)/gm;
|
||||
const VARIABLE_DECLARATOR_RE = /\b([A-Za-z_$][A-Za-z0-9_$]*)\s*(?::[^=,]+)?=/g;
|
||||
// Matches the binding name at the head of a single declarator (`name`, `name:`, `name =`).
|
||||
// A leading `{`/`[` (destructuring) does not match, so destructured exports are skipped.
|
||||
const DECLARATOR_NAME_RE = /^\s*([A-Za-z_$][A-Za-z0-9_$]*)/;
|
||||
|
||||
// ---- loader -----------------------------------------------------------------
|
||||
|
||||
|
|
@ -309,13 +311,41 @@ function extractNitroExportNames(content: string): string[] {
|
|||
NITRO_VARIABLE_EXPORT_RE.lastIndex = 0;
|
||||
let variableDeclaration: RegExpExecArray | null;
|
||||
while ((variableDeclaration = NITRO_VARIABLE_EXPORT_RE.exec(content)) !== null) {
|
||||
const declarationText = variableDeclaration[1]!;
|
||||
VARIABLE_DECLARATOR_RE.lastIndex = 0;
|
||||
let declarator: RegExpExecArray | null;
|
||||
while ((declarator = VARIABLE_DECLARATOR_RE.exec(declarationText)) !== null) {
|
||||
names.add(declarator[1]!);
|
||||
// Only the LHS binding name of each top-level declarator is a Nitro export.
|
||||
// Splitting on top-level commas and reading the leading identifier avoids
|
||||
// capturing RHS tokens (arrow params, object keys, operands) as export names,
|
||||
// and tolerates commas inside generic type annotations (`x: Map<a, b> = …`).
|
||||
for (const declarator of splitTopLevelDeclarators(variableDeclaration[1]!)) {
|
||||
const name = DECLARATOR_NAME_RE.exec(declarator);
|
||||
if (name) names.add(name[1]!);
|
||||
}
|
||||
}
|
||||
|
||||
return [...names];
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a `const`/`let`/`var` declarator list on top-level commas, tracking
|
||||
* `()`, `[]`, `{}`, and `<>` nesting so commas inside call args, object/array
|
||||
* literals, and generic type arguments do not split a declarator. Errs toward
|
||||
* under-splitting on pathological RHS (a missed binding name, never a spurious
|
||||
* one) — Nitro only auto-imports real top-level binding names.
|
||||
*/
|
||||
function splitTopLevelDeclarators(text: string): string[] {
|
||||
const parts: string[] = [];
|
||||
let depth = 0;
|
||||
let start = 0;
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const ch = text[i];
|
||||
if (ch === '(' || ch === '[' || ch === '{' || ch === '<') {
|
||||
depth++;
|
||||
} else if (ch === ')' || ch === ']' || ch === '}' || ch === '>') {
|
||||
if (depth > 0) depth--;
|
||||
} else if (ch === ',' && depth === 0) {
|
||||
parts.push(text.slice(start, i));
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
parts.push(text.slice(start));
|
||||
return parts;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,6 +118,36 @@ describe('loadNuxtAutoImports', () => {
|
|||
sourceFile: 'server/utils/jsHelper.js',
|
||||
});
|
||||
});
|
||||
|
||||
it('captures only LHS binding names from server/utils const exports', async () => {
|
||||
const root = makeRepo();
|
||||
writeFile(root, '.nuxt/imports.d.ts', '');
|
||||
writeFile(
|
||||
root,
|
||||
'server/utils/forms.ts',
|
||||
[
|
||||
'export const createUser = async (event: H3Event) => {};',
|
||||
'export const config = { onError: () => {} };',
|
||||
'export const eq = a === b;',
|
||||
'export const helper: Record<string, unknown> = {};',
|
||||
'export const first = 1, second = 2;',
|
||||
].join('\n'),
|
||||
);
|
||||
|
||||
const config = await loadNuxtAutoImports(root);
|
||||
|
||||
// Only declared binding names — never RHS arrow params (`event`), object
|
||||
// keys (`onError`), operands (`a`/`b`), nor a generic-typed name dropped at
|
||||
// the comma inside `Record<string, unknown>`.
|
||||
expect([...config!.serverByLocalName.keys()].sort()).toEqual([
|
||||
'config',
|
||||
'createUser',
|
||||
'eq',
|
||||
'first',
|
||||
'helper',
|
||||
'second',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isNitroServerRuntimeFile', () => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue