gitnexus 1.6.9-rc.15 → 1.6.9-rc.17

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 (32) hide show
  1. package/dist/core/ingestion/languages/typescript/nuxt-auto-imports.d.ts +72 -0
  2. package/dist/core/ingestion/languages/typescript/nuxt-auto-imports.js +315 -0
  3. package/dist/core/ingestion/languages/typescript/scope-resolver.js +170 -5
  4. package/package.json +1 -1
  5. package/web/assets/{agent-D3Xd7zZE.js → agent-C1yQhYA6.js} +2 -2
  6. package/web/assets/{architectureDiagram-UL44E2DR-ChH9xW-N.js → architectureDiagram-UL44E2DR-BEE5Tkn1.js} +1 -1
  7. package/web/assets/{chunk-LCXTWHL2-zoomRtWX.js → chunk-LCXTWHL2-CJFWRlb3.js} +1 -1
  8. package/web/assets/{chunk-RG4AUYOV-DD0B2N2B.js → chunk-RG4AUYOV-BnLGWH9h.js} +1 -1
  9. package/web/assets/{classDiagram-KGZ6W3CR-DJm9BgF-.js → classDiagram-KGZ6W3CR-CLQYoVUJ.js} +1 -1
  10. package/web/assets/{classDiagram-v2-72OJOZXJ-DJm9BgF-.js → classDiagram-v2-72OJOZXJ-CLQYoVUJ.js} +1 -1
  11. package/web/assets/{dagre-ND4H6XIP-BIkxmhcu.js → dagre-ND4H6XIP-Dr5QY4wD.js} +1 -1
  12. package/web/assets/{diagram-3NCE3AQN-BTa6iH0T.js → diagram-3NCE3AQN-BvWBILtA.js} +1 -1
  13. package/web/assets/{diagram-GF46GFSD-DZDV0vBj.js → diagram-GF46GFSD-Br3zOR7t.js} +1 -1
  14. package/web/assets/{diagram-QXG6HAR7-BzM--Jj0.js → diagram-QXG6HAR7-Cs3KWLM2.js} +1 -1
  15. package/web/assets/{diagram-WEQXMOUZ-DFFwz0k4.js → diagram-WEQXMOUZ-Cxjpe-cV.js} +1 -1
  16. package/web/assets/{erDiagram-L5TCEMPS-CfHMJJqo.js → erDiagram-L5TCEMPS-CJwkg3B3.js} +1 -1
  17. package/web/assets/{flowDiagram-H6V6AXG4-Be805QiJ.js → flowDiagram-H6V6AXG4-BgHq4Oef.js} +1 -1
  18. package/web/assets/{index-BvYev1UJ.js → index-BNHlZ4--.js} +5 -5
  19. package/web/assets/{infoDiagram-3YFTVSEB-Y7BKSoke.js → infoDiagram-3YFTVSEB-DDs1fvtj.js} +1 -1
  20. package/web/assets/{ishikawaDiagram-BNXS4ZKH-CT4SHFuI.js → ishikawaDiagram-BNXS4ZKH-zhdjhNj8.js} +1 -1
  21. package/web/assets/{kanban-definition-75IXJCU3-C6yDxr7N.js → kanban-definition-75IXJCU3-DzBwKL9_.js} +1 -1
  22. package/web/assets/{mindmap-definition-2TDM6QVE-BOc2WEne.js → mindmap-definition-2TDM6QVE-B6J7xmd_.js} +1 -1
  23. package/web/assets/{pieDiagram-CU6KROY3-Cvc0f_Pd.js → pieDiagram-CU6KROY3-CkAf4LWQ.js} +1 -1
  24. package/web/assets/{requirementDiagram-JXO7QTGE-BRcZvHs4.js → requirementDiagram-JXO7QTGE-DTxn3BGP.js} +1 -1
  25. package/web/assets/{sequenceDiagram-VS2MUI6T-PcSCuvrB.js → sequenceDiagram-VS2MUI6T-TkQjGTFD.js} +1 -1
  26. package/web/assets/{stateDiagram-7D4R322I-D6qYSV1E.js → stateDiagram-7D4R322I-BZ6GiE7S.js} +1 -1
  27. package/web/assets/{stateDiagram-v2-36443NZ5-Bg-ImOSX.js → stateDiagram-v2-36443NZ5-zudj8EhG.js} +1 -1
  28. package/web/assets/{timeline-definition-O6YCAMPW-QWkh01xw.js → timeline-definition-O6YCAMPW-wfNw45-O.js} +1 -1
  29. package/web/assets/{vennDiagram-MWXL3ELB-ak4DfwuM.js → vennDiagram-MWXL3ELB-9Jef__Cf.js} +1 -1
  30. package/web/assets/{wardleyDiagram-CUQ6CDDI-CTzR4Loq.js → wardleyDiagram-CUQ6CDDI-BJOzwKo8.js} +1 -1
  31. package/web/assets/{xychartDiagram-N2JHSOCM-DA1BEaQz.js → xychartDiagram-N2JHSOCM-BMP7fFl2.js} +1 -1
  32. package/web/index.html +1 -1
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Nuxt v4 auto-import resolution for the TypeScript scope-resolver.
3
+ *
4
+ * Nuxt and Nitro both make symbols available project-wide without explicit
5
+ * import statements. Two sources cover the common cases:
6
+ *
7
+ * 1. `.nuxt/imports.d.ts` - client/shared composables generated by Nuxt at
8
+ * build time. Only entries whose source is a project-local relative path
9
+ * (i.e. NOT a package in node_modules or a Nuxt runtime alias) are
10
+ * indexed. These are project utils and composables the developer wrote.
11
+ *
12
+ * 2. `server/utils/**` - Nitro auto-imports all exports from this directory
13
+ * tree into every `server/api/`, `server/routes/`, and
14
+ * `server/middleware/` file. These are not included in `imports.d.ts`
15
+ * because they are server-only.
16
+ *
17
+ * The two sources are kept in separate maps because their visibility scopes
18
+ * differ: app/client files do not see `server/utils`, while Nitro server
19
+ * handlers prefer same-named server utilities over client composables.
20
+ *
21
+ * Detection: if `.nuxt/imports.d.ts` is absent the repo is not a Nuxt project
22
+ * and this module returns null immediately, adding zero overhead to non-Nuxt
23
+ * analysis runs. The `server/utils` scan is only attempted when the Nuxt
24
+ * detection succeeds, so it also costs nothing for non-Nuxt repos.
25
+ *
26
+ * Limitations:
27
+ * - Auto-import edges are still heuristic rather than type-checked. The
28
+ * emitter tags them with confidence 0.75.
29
+ * - Re-exports with non-matching aliases (e.g. `flatUnwrap as unwrapSlot`
30
+ * where `flatUnwrap` is the graph node name) try both the original export
31
+ * name and the local alias when looking up the graph node.
32
+ * - Only direct exports are indexed; barrel re-exports that chain through
33
+ * multiple files are not followed.
34
+ */
35
+ export type NuxtAutoImportScope = 'client' | 'server';
36
+ /** A single auto-imported symbol and where it lives in the repo. */
37
+ export interface NuxtAutoImportEntry {
38
+ /** The name used at call sites (the alias when `export { X as Y }` form). */
39
+ readonly localName: string;
40
+ /** The original export name in the source file (before `as`). */
41
+ readonly exportName: string;
42
+ /** Repo-relative POSIX path to the source file (with extension). */
43
+ readonly sourceFile: string;
44
+ /** Which Nuxt/Nitro visibility scope supplied this entry. */
45
+ readonly scope: NuxtAutoImportScope;
46
+ }
47
+ /**
48
+ * Aggregated auto-import maps for one Nuxt workspace, keyed by the local
49
+ * name used in calling code. Client/shared entries come from
50
+ * `.nuxt/imports.d.ts`; server entries come from `server/utils`.
51
+ */
52
+ export interface NuxtAutoImportConfig {
53
+ readonly clientByLocalName: ReadonlyMap<string, NuxtAutoImportEntry>;
54
+ readonly serverByLocalName: ReadonlyMap<string, NuxtAutoImportEntry>;
55
+ }
56
+ /**
57
+ * Load the Nuxt auto-import map for `repoRoot`.
58
+ *
59
+ * Returns null when:
60
+ * - `.nuxt/imports.d.ts` does not exist (non-Nuxt project or pre-build), or
61
+ * - the file exists but yields zero project-local entries and `server/utils`
62
+ * is absent or empty.
63
+ *
64
+ * The `server/utils` scan is only attempted when `.nuxt/imports.d.ts` was
65
+ * successfully read, confirming this is an initialized Nuxt project. This
66
+ * avoids partial results from repos that have a `server/utils` directory but
67
+ * are not Nuxt projects.
68
+ */
69
+ export declare function loadNuxtAutoImports(repoRoot: string): Promise<NuxtAutoImportConfig | null>;
70
+ export declare function hasNuxtAutoImports(config: NuxtAutoImportConfig): boolean;
71
+ export declare function getNuxtAutoImportEntry(config: NuxtAutoImportConfig, localName: string, callerFile: string): NuxtAutoImportEntry | undefined;
72
+ export declare function isNitroServerRuntimeFile(filePath: string): boolean;
@@ -0,0 +1,315 @@
1
+ /**
2
+ * Nuxt v4 auto-import resolution for the TypeScript scope-resolver.
3
+ *
4
+ * Nuxt and Nitro both make symbols available project-wide without explicit
5
+ * import statements. Two sources cover the common cases:
6
+ *
7
+ * 1. `.nuxt/imports.d.ts` - client/shared composables generated by Nuxt at
8
+ * build time. Only entries whose source is a project-local relative path
9
+ * (i.e. NOT a package in node_modules or a Nuxt runtime alias) are
10
+ * indexed. These are project utils and composables the developer wrote.
11
+ *
12
+ * 2. `server/utils/**` - Nitro auto-imports all exports from this directory
13
+ * tree into every `server/api/`, `server/routes/`, and
14
+ * `server/middleware/` file. These are not included in `imports.d.ts`
15
+ * because they are server-only.
16
+ *
17
+ * The two sources are kept in separate maps because their visibility scopes
18
+ * differ: app/client files do not see `server/utils`, while Nitro server
19
+ * handlers prefer same-named server utilities over client composables.
20
+ *
21
+ * Detection: if `.nuxt/imports.d.ts` is absent the repo is not a Nuxt project
22
+ * and this module returns null immediately, adding zero overhead to non-Nuxt
23
+ * analysis runs. The `server/utils` scan is only attempted when the Nuxt
24
+ * detection succeeds, so it also costs nothing for non-Nuxt repos.
25
+ *
26
+ * Limitations:
27
+ * - Auto-import edges are still heuristic rather than type-checked. The
28
+ * emitter tags them with confidence 0.75.
29
+ * - Re-exports with non-matching aliases (e.g. `flatUnwrap as unwrapSlot`
30
+ * where `flatUnwrap` is the graph node name) try both the original export
31
+ * name and the local alias when looking up the graph node.
32
+ * - Only direct exports are indexed; barrel re-exports that chain through
33
+ * multiple files are not followed.
34
+ */
35
+ import fs from 'fs/promises';
36
+ import path from 'path';
37
+ const FILE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx'];
38
+ const GENERATED_DIR_NAMES = new Set([
39
+ '.git',
40
+ '.nuxt',
41
+ '.output',
42
+ '.next',
43
+ 'build',
44
+ 'coverage',
45
+ 'dist',
46
+ 'node_modules',
47
+ ]);
48
+ const IMPORTS_DTS_EXPORT_RE = /^export\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/gm;
49
+ 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;
50
+ const NITRO_VARIABLE_EXPORT_RE = /^export\s+(?:const|let|var)\s+([^;\n]+)/gm;
51
+ // Matches the binding name at the head of a single declarator (`name`, `name:`, `name =`).
52
+ // A leading `{`/`[` (destructuring) does not match, so destructured exports are skipped.
53
+ const DECLARATOR_NAME_RE = /^\s*([A-Za-z_$][A-Za-z0-9_$]*)/;
54
+ // ---- loader -----------------------------------------------------------------
55
+ /**
56
+ * Load the Nuxt auto-import map for `repoRoot`.
57
+ *
58
+ * Returns null when:
59
+ * - `.nuxt/imports.d.ts` does not exist (non-Nuxt project or pre-build), or
60
+ * - the file exists but yields zero project-local entries and `server/utils`
61
+ * is absent or empty.
62
+ *
63
+ * The `server/utils` scan is only attempted when `.nuxt/imports.d.ts` was
64
+ * successfully read, confirming this is an initialized Nuxt project. This
65
+ * avoids partial results from repos that have a `server/utils` directory but
66
+ * are not Nuxt projects.
67
+ */
68
+ export async function loadNuxtAutoImports(repoRoot) {
69
+ const clientByLocalName = new Map();
70
+ const serverByLocalName = new Map();
71
+ const nuxtInitialized = await collectImportsDts(repoRoot, clientByLocalName);
72
+ // Only scan server/utils when imports.d.ts was present, confirming this is
73
+ // an initialized Nuxt project. Without this gate, a non-Nuxt repo with a
74
+ // server/utils directory would get spurious Nitro auto-import edges.
75
+ if (nuxtInitialized) {
76
+ await collectNitroServerUtils(repoRoot, serverByLocalName);
77
+ }
78
+ const config = { clientByLocalName, serverByLocalName };
79
+ return hasNuxtAutoImports(config) ? config : null;
80
+ }
81
+ export function hasNuxtAutoImports(config) {
82
+ return config.clientByLocalName.size > 0 || config.serverByLocalName.size > 0;
83
+ }
84
+ export function getNuxtAutoImportEntry(config, localName, callerFile) {
85
+ if (isNitroServerRuntimeFile(callerFile)) {
86
+ // Nitro auto-imports only `server/utils/**` into the server context; app
87
+ // `composables/` are Vue-app-only. Server callers therefore resolve the
88
+ // server map only — no client fallback, which would emit cross-context
89
+ // CALLS edges Nitro never actually creates.
90
+ return config.serverByLocalName.get(localName);
91
+ }
92
+ return config.clientByLocalName.get(localName);
93
+ }
94
+ /**
95
+ * Directories whose files run in the Nitro server context and so receive
96
+ * `server/utils` auto-imports without an explicit import: API routes, server
97
+ * routes, middleware, plugins, and (Nitro 2.6+) tasks.
98
+ */
99
+ const NITRO_RUNTIME_DIR_PREFIXES = [
100
+ 'server/api/',
101
+ 'server/routes/',
102
+ 'server/middleware/',
103
+ 'server/plugins/',
104
+ 'server/tasks/',
105
+ ];
106
+ export function isNitroServerRuntimeFile(filePath) {
107
+ const normalized = filePath.replace(/\\/g, '/');
108
+ return NITRO_RUNTIME_DIR_PREFIXES.some((prefix) => normalized.startsWith(prefix));
109
+ }
110
+ // ---- .nuxt/imports.d.ts -----------------------------------------------------
111
+ /**
112
+ * Parse `.nuxt/imports.d.ts` and add project-local entries to `byLocalName`.
113
+ *
114
+ * The file contains lines of the form:
115
+ * export { name1, name2, origName as alias } from 'source'
116
+ *
117
+ * Only entries with a source that is a project-local relative path
118
+ * (starts with `./` or `../` AND does not contain `node_modules`) are
119
+ * included. Nuxt runtime paths (`#app/...`) and third-party packages are
120
+ * intentionally skipped because they have no graph nodes in the repo.
121
+ *
122
+ * @returns true when `.nuxt/imports.d.ts` was successfully read.
123
+ */
124
+ async function collectImportsDts(repoRoot, byLocalName) {
125
+ const importsPath = path.join(repoRoot, '.nuxt', 'imports.d.ts');
126
+ let content;
127
+ try {
128
+ content = await fs.readFile(importsPath, 'utf-8');
129
+ }
130
+ catch {
131
+ return false;
132
+ }
133
+ const nuxtDir = path.join(repoRoot, '.nuxt');
134
+ IMPORTS_DTS_EXPORT_RE.lastIndex = 0;
135
+ let m;
136
+ while ((m = IMPORTS_DTS_EXPORT_RE.exec(content)) !== null) {
137
+ const symbolsRaw = m[1];
138
+ const source = m[2];
139
+ if (!isProjectLocalPath(source))
140
+ continue;
141
+ // Containment guard: a crafted source (`from '../../../../etc/passwd'`)
142
+ // passes the relative-path check but escapes the repo. Skip anything that
143
+ // resolves outside repoRoot before touching the filesystem.
144
+ const resolvedBase = path.resolve(nuxtDir, source);
145
+ if (!isWithinRepo(repoRoot, resolvedBase))
146
+ continue;
147
+ const resolvedFile = await resolveExtension(resolvedBase);
148
+ if (resolvedFile === null)
149
+ continue;
150
+ const sourceFile = toRepoPosix(repoRoot, resolvedFile);
151
+ for (const raw of symbolsRaw.split(',')) {
152
+ const trimmed = raw.trim();
153
+ if (!trimmed)
154
+ continue;
155
+ const asIdx = trimmed.indexOf(' as ');
156
+ const exportName = asIdx >= 0 ? trimmed.slice(0, asIdx).trim() : trimmed;
157
+ const localName = asIdx >= 0 ? trimmed.slice(asIdx + 4).trim() : trimmed;
158
+ if (!localName || !exportName)
159
+ continue;
160
+ if (!byLocalName.has(localName)) {
161
+ byLocalName.set(localName, { localName, exportName, sourceFile, scope: 'client' });
162
+ }
163
+ }
164
+ }
165
+ return true;
166
+ }
167
+ // ---- server/utils (Nitro auto-imports) --------------------------------------
168
+ /**
169
+ * Scan `server/utils/**` and register every exported symbol as a Nitro
170
+ * auto-import. Nitro makes all exports from this directory tree available
171
+ * without an import statement in server routes and middleware.
172
+ */
173
+ async function collectNitroServerUtils(repoRoot, byLocalName) {
174
+ const serverUtilsDir = path.join(repoRoot, 'server', 'utils');
175
+ if (!(await dirExists(serverUtilsDir)))
176
+ return;
177
+ const tsFiles = await collectTsFiles(serverUtilsDir);
178
+ for (const absPath of tsFiles) {
179
+ let content;
180
+ try {
181
+ content = await fs.readFile(absPath, 'utf-8');
182
+ }
183
+ catch {
184
+ continue;
185
+ }
186
+ const sourceFile = toRepoPosix(repoRoot, absPath);
187
+ for (const name of extractNitroExportNames(content)) {
188
+ if (!byLocalName.has(name)) {
189
+ byLocalName.set(name, { localName: name, exportName: name, sourceFile, scope: 'server' });
190
+ }
191
+ }
192
+ }
193
+ }
194
+ // ---- helpers ----------------------------------------------------------------
195
+ function isProjectLocalPath(source) {
196
+ if (!source.startsWith('./') && !source.startsWith('../'))
197
+ return false;
198
+ if (source.includes('node_modules'))
199
+ return false;
200
+ return true;
201
+ }
202
+ /** True when `absPath` is `repoRoot` itself or lives beneath it. */
203
+ function isWithinRepo(repoRoot, absPath) {
204
+ const root = path.resolve(repoRoot);
205
+ return absPath === root || absPath.startsWith(root + path.sep);
206
+ }
207
+ async function resolveExtension(base) {
208
+ const directFile = await firstExistingFile([...FILE_EXTENSIONS.map((ext) => base + ext), base]);
209
+ if (directFile !== null)
210
+ return directFile;
211
+ return firstExistingFile(FILE_EXTENSIONS.map((ext) => path.join(base, `index${ext}`)));
212
+ }
213
+ async function firstExistingFile(candidates) {
214
+ const matches = await Promise.all(candidates.map(async (candidate) => ((await isFile(candidate)) ? candidate : null)));
215
+ return matches.find((candidate) => candidate !== null) ?? null;
216
+ }
217
+ async function isFile(filePath) {
218
+ try {
219
+ const stat = await fs.stat(filePath);
220
+ return stat.isFile();
221
+ }
222
+ catch {
223
+ return false;
224
+ }
225
+ }
226
+ function toRepoPosix(repoRoot, absPath) {
227
+ return path.relative(repoRoot, absPath).replace(/\\/g, '/');
228
+ }
229
+ async function dirExists(dirPath) {
230
+ try {
231
+ const stat = await fs.stat(dirPath);
232
+ return stat.isDirectory();
233
+ }
234
+ catch {
235
+ return false;
236
+ }
237
+ }
238
+ /** Recursively collect supported TypeScript/JavaScript files under a directory. */
239
+ async function collectTsFiles(dir) {
240
+ const results = [];
241
+ let entries;
242
+ try {
243
+ entries = await fs.readdir(dir, { withFileTypes: true });
244
+ }
245
+ catch {
246
+ return results;
247
+ }
248
+ for (const entry of entries) {
249
+ if (entry.isDirectory() && GENERATED_DIR_NAMES.has(entry.name))
250
+ continue;
251
+ const full = path.join(dir, entry.name);
252
+ if (entry.isDirectory()) {
253
+ results.push(...(await collectTsFiles(full)));
254
+ }
255
+ else if (entry.isFile() && hasSupportedSourceExtension(entry.name)) {
256
+ results.push(full);
257
+ }
258
+ }
259
+ return results;
260
+ }
261
+ function hasSupportedSourceExtension(fileName) {
262
+ return FILE_EXTENSIONS.some((ext) => fileName.endsWith(ext));
263
+ }
264
+ function extractNitroExportNames(content) {
265
+ const names = new Set();
266
+ NITRO_DECLARATION_EXPORT_RE.lastIndex = 0;
267
+ let declaration;
268
+ while ((declaration = NITRO_DECLARATION_EXPORT_RE.exec(content)) !== null) {
269
+ const name = declaration[1] ?? declaration[2];
270
+ if (name)
271
+ names.add(name);
272
+ }
273
+ NITRO_VARIABLE_EXPORT_RE.lastIndex = 0;
274
+ let variableDeclaration;
275
+ while ((variableDeclaration = NITRO_VARIABLE_EXPORT_RE.exec(content)) !== null) {
276
+ // Only the LHS binding name of each top-level declarator is a Nitro export.
277
+ // Splitting on top-level commas and reading the leading identifier avoids
278
+ // capturing RHS tokens (arrow params, object keys, operands) as export names,
279
+ // and tolerates commas inside generic type annotations (`x: Map<a, b> = …`).
280
+ for (const declarator of splitTopLevelDeclarators(variableDeclaration[1])) {
281
+ const name = DECLARATOR_NAME_RE.exec(declarator);
282
+ if (name)
283
+ names.add(name[1]);
284
+ }
285
+ }
286
+ return [...names];
287
+ }
288
+ /**
289
+ * Split a `const`/`let`/`var` declarator list on top-level commas, tracking
290
+ * `()`, `[]`, `{}`, and `<>` nesting so commas inside call args, object/array
291
+ * literals, and generic type arguments do not split a declarator. Errs toward
292
+ * under-splitting on pathological RHS (a missed binding name, never a spurious
293
+ * one) — Nitro only auto-imports real top-level binding names.
294
+ */
295
+ function splitTopLevelDeclarators(text) {
296
+ const parts = [];
297
+ let depth = 0;
298
+ let start = 0;
299
+ for (let i = 0; i < text.length; i++) {
300
+ const ch = text[i];
301
+ if (ch === '(' || ch === '[' || ch === '{' || ch === '<') {
302
+ depth++;
303
+ }
304
+ else if (ch === ')' || ch === ']' || ch === '}' || ch === '>') {
305
+ if (depth > 0)
306
+ depth--;
307
+ }
308
+ else if (ch === ',' && depth === 0) {
309
+ parts.push(text.slice(start, i));
310
+ start = i + 1;
311
+ }
312
+ }
313
+ parts.push(text.slice(start));
314
+ return parts;
315
+ }
@@ -12,12 +12,24 @@
12
12
  * ./query.ts (TYPESCRIPT_SCOPE_QUERY constant).
13
13
  */
14
14
  import { SupportedLanguages } from '../../../../_shared/index.js';
15
+ import { generateId } from '../../../../lib/utils.js';
15
16
  import { buildMro, defaultLinearize } from '../../scope-resolution/passes/mro.js';
16
17
  import { populateClassOwnedMembers } from '../../scope-resolution/scope/walkers.js';
18
+ import { simpleKey } from '../../scope-resolution/graph-bridge/node-lookup.js';
17
19
  import { typescriptProvider } from '../typescript.js';
18
20
  import { loadTsconfigPaths } from '../../language-config.js';
19
21
  import { buildSuffixIndex } from '../../import-resolvers/utils.js';
20
22
  import { typescriptArityCompatibility, typescriptMergeBindings, resolveTsTarget, } from './index.js';
23
+ import { getNuxtAutoImportEntry, hasNuxtAutoImports, loadNuxtAutoImports, } from './nuxt-auto-imports.js';
24
+ const TYPESCRIPT_TYPE_ONLY_BINDING_TYPES = new Set([
25
+ 'Interface',
26
+ 'Type',
27
+ 'TypeAlias',
28
+ 'Typedef',
29
+ 'Trait',
30
+ 'Annotation',
31
+ 'Decorator',
32
+ ]);
21
33
  /**
22
34
  * Build a `resolveImportTarget` adapter that memoizes the workspace
23
35
  * file list, the lower-cased file list, and the per-pass `resolveCache`
@@ -65,11 +77,14 @@ const typescriptScopeResolver = {
65
77
  importEdgeReason: 'typescript-scope: import',
66
78
  resolveImportTarget: makeTsResolveImportTarget(),
67
79
  // Threaded into `resolveImportTarget` so tsconfig path aliases
68
- // (`@/services/user`, `~/x`, ) resolve through the same standard
80
+ // (`@/services/user`, `~/x`, ...) resolve through the same standard
69
81
  // resolver branch the legacy DAG uses. One I/O round-trip per
70
82
  // workspace pass; the orchestrator awaits this once.
83
+ // `nuxtAutoImports` is null for non-Nuxt projects (no .nuxt/imports.d.ts),
84
+ // so this adds zero overhead to ordinary TypeScript repos.
71
85
  loadResolutionConfig: async (repoPath) => ({
72
86
  tsconfigPaths: await loadTsconfigPaths(repoPath),
87
+ nuxtAutoImports: await loadNuxtAutoImports(repoPath),
73
88
  }),
74
89
  // TypeScript declaration merging + LEGB: local > import > wildcard,
75
90
  // separated by declaration space (value / type / namespace). The
@@ -95,23 +110,173 @@ const typescriptScopeResolver = {
95
110
  fieldFallbackOnMethodLookup: false,
96
111
  propagatesReturnTypesAcrossImports: true,
97
112
  // TypeScript uses `.values()` / `.keys()` method-call syntax for
98
- // collection views no property-style accessors like C#'s
113
+ // collection views -- no property-style accessors like C#'s
99
114
  // `Dictionary<K,V>.Values`. Leave `unwrapCollectionAccessor`
100
115
  // undefined and let the regular member-call branch handle them.
101
116
  //
102
- // `collapseMemberCallsByCallerTarget` left undefined (= false)
117
+ // `collapseMemberCallsByCallerTarget` left undefined (= false) --
103
118
  // TypeScript legacy DAG emits one edge per call site, so
104
119
  // per-site dedup is the parity target.
105
120
  //
106
- // `populateNamespaceSiblings` left undefined TypeScript requires
121
+ // `populateNamespaceSiblings` left undefined -- TypeScript requires
107
122
  // an explicit `import` / namespace augmentation for cross-file
108
123
  // visibility; there's no implicit same-namespace sibling rule
109
124
  // like C#'s.
110
125
  //
111
- // `hoistTypeBindingsToModule` `tsBindingScopeFor` DOES hoist
126
+ // `hoistTypeBindingsToModule` -- `tsBindingScopeFor` DOES hoist
112
127
  // method return-type bindings to the enclosing Module scope
113
128
  // (mirrors C#), so enable the walk-up that lets the compound-
114
129
  // receiver resolver find them.
115
130
  hoistTypeBindingsToModule: true,
131
+ /**
132
+ * Emit CALLS edges for Nuxt/Nitro auto-imported symbols that are used
133
+ * without an explicit import statement.
134
+ *
135
+ * Nuxt makes composables and server utils available project-wide via its
136
+ * auto-import system. Because no `import` statement exists, the standard
137
+ * scope-resolution passes cannot create call-graph edges for these symbols.
138
+ * This hook recovers those edges after all normal resolution has run.
139
+ *
140
+ * For each TypeScript file the hook:
141
+ * 1. Builds the set of files already explicitly imported (to avoid
142
+ * creating duplicate edges for symbols imported conventionally).
143
+ * 2. Iterates parsed free-call reference sites and checks each against the
144
+ * auto-import map selected for the caller's Nuxt/Nitro scope.
145
+ * 3. For each hit that is not shadowed by a local binding or explicit
146
+ * import, emits a CALLS edge from the file's File node to the target
147
+ * function node, and an IMPORTS edge from the caller file to the source
148
+ * file (once per pair).
149
+ *
150
+ * Confidence is 0.75 (below the 0.9 used for fully resolved edges) to
151
+ * signal that these edges are heuristic rather than type-checked.
152
+ */
153
+ emitPostResolutionEdges(graph, parsedFiles, nodeLookup, indexes, ctx) {
154
+ const cfg = ctx.resolutionConfig;
155
+ const autoImports = cfg?.nuxtAutoImports;
156
+ if (!autoImports || !hasNuxtAutoImports(autoImports))
157
+ return;
158
+ // Pre-build a file -> explicit imported local names index so importing one
159
+ // symbol from a source does not suppress other auto-imported symbols from it.
160
+ const explicitImportNamesByFile = new Map();
161
+ for (const [scopeId, edges] of indexes.imports) {
162
+ const scope = indexes.scopeTree.getScope(scopeId);
163
+ if (!scope?.filePath)
164
+ continue;
165
+ let names = explicitImportNamesByFile.get(scope.filePath);
166
+ if (!names) {
167
+ names = new Set();
168
+ explicitImportNamesByFile.set(scope.filePath, names);
169
+ }
170
+ for (const edge of edges) {
171
+ // Record the local name whether or not the import resolved to a file.
172
+ // An explicit import of a name — even from an unresolved external
173
+ // package (`import { useAuto } from '@vueuse/core'`) — is authoritative
174
+ // shadowing intent and must suppress the auto-import for that name.
175
+ if (edge.localName)
176
+ names.add(edge.localName);
177
+ }
178
+ }
179
+ for (const parsedFile of parsedFiles) {
180
+ const { filePath } = parsedFile;
181
+ if (!filePath.endsWith('.ts') && !filePath.endsWith('.tsx'))
182
+ continue;
183
+ const fileId = generateId('File', filePath);
184
+ const explicitImports = explicitImportNamesByFile.get(filePath) ?? new Set();
185
+ // Track (sourceFile) pairs already handled for this caller to avoid
186
+ // emitting duplicate IMPORTS edges and duplicate CALLS edges per symbol.
187
+ const emittedImports = new Set();
188
+ const emittedCalls = new Set();
189
+ for (const site of parsedFile.referenceSites) {
190
+ if (site.kind !== 'call' || site.callForm !== 'free')
191
+ continue;
192
+ const localName = site.name;
193
+ const entry = getNuxtAutoImportEntry(autoImports, localName, filePath);
194
+ if (!entry)
195
+ continue;
196
+ const { exportName, sourceFile } = entry;
197
+ // Skip when the file already binds this name explicitly, when the file
198
+ // IS the source, or when a lexical same-file binding shadows it.
199
+ if (explicitImports.has(localName) ||
200
+ sourceFile === filePath ||
201
+ hasLocalBindingInScopeChain(site.inScope, localName, filePath, indexes)) {
202
+ continue;
203
+ }
204
+ // Emit one IMPORTS edge per (caller, sourceFile) pair.
205
+ if (!emittedImports.has(sourceFile)) {
206
+ emittedImports.add(sourceFile);
207
+ const targetFileId = generateId('File', sourceFile);
208
+ if (graph.getNode(targetFileId)) {
209
+ graph.addRelationship({
210
+ id: generateId('IMPORTS', `${fileId}->nuxt-auto-import->${targetFileId}`),
211
+ sourceId: fileId,
212
+ targetId: targetFileId,
213
+ type: 'IMPORTS',
214
+ confidence: 0.75,
215
+ reason: 'nuxt-auto-import-file',
216
+ });
217
+ }
218
+ }
219
+ // Emit one CALLS edge per (caller, symbol) pair.
220
+ const callKey = `${sourceFile}::${localName}`;
221
+ if (emittedCalls.has(callKey))
222
+ continue;
223
+ emittedCalls.add(callKey);
224
+ // Look up the graph node by export name first, fall back to local name.
225
+ // The fallback handles `default as X` where the function is named X.
226
+ const targetNodeId = nodeLookup.get(simpleKey(sourceFile, exportName)) ??
227
+ (exportName !== localName ? nodeLookup.get(simpleKey(sourceFile, localName)) : undefined);
228
+ if (!targetNodeId || !graph.getNode(targetNodeId))
229
+ continue;
230
+ graph.addRelationship({
231
+ id: generateId('CALLS', `${fileId}:nuxt-auto-import:${localName}->${targetNodeId}`),
232
+ sourceId: fileId,
233
+ targetId: targetNodeId,
234
+ type: 'CALLS',
235
+ confidence: 0.75,
236
+ reason: 'nuxt-auto-import',
237
+ });
238
+ }
239
+ }
240
+ },
116
241
  };
242
+ function occupiesTypeScriptValueSpace(type) {
243
+ return !TYPESCRIPT_TYPE_ONLY_BINDING_TYPES.has(type);
244
+ }
245
+ function hasLocalBindingInScopeChain(scopeId, name, filePath, indexes) {
246
+ const visited = new Set();
247
+ let cursor = scopeId;
248
+ while (cursor !== null && cursor !== undefined && !visited.has(cursor)) {
249
+ visited.add(cursor);
250
+ const scope = indexes.scopeTree.getScope(cursor);
251
+ if (!scope)
252
+ return false;
253
+ const localBindings = scope.bindings.get(name);
254
+ if (localBindings?.some((binding) => binding.origin === 'local' &&
255
+ binding.def.filePath === filePath &&
256
+ occupiesTypeScriptValueSpace(binding.def.type))) {
257
+ return true;
258
+ }
259
+ // Type-annotated function parameters — and other value-space type facts
260
+ // such as `self` and variable annotations — live in `scope.typeBindings`,
261
+ // not `scope.bindings`. A parameter named like a composable genuinely
262
+ // shadows the auto-import, and typeBindings never holds a pure type that
263
+ // belongs to callable space, so a same-file presence check here cannot
264
+ // over-suppress a legitimate auto-import.
265
+ //
266
+ // Residual (known limitation): this catches parameters whose annotation the
267
+ // TS scope query records as a type-binding (`p: Named`, generics, unions,
268
+ // predefined, arrays). Function-typed params (`p: () => void`), untyped
269
+ // params, destructured locals (`const { x } = …`), and catch-clause vars
270
+ // are captured by NEITHER map — the scope query emits no `@declaration` /
271
+ // `@type-binding` for them — so those shadow forms still leak an edge.
272
+ // Closing that needs shared TS scope-query/extractor changes that alter call
273
+ // resolution beyond Nuxt, so it is deferred to a follow-up rather than fixed
274
+ // here.
275
+ if (scope.filePath === filePath && scope.typeBindings.has(name)) {
276
+ return true;
277
+ }
278
+ cursor = scope.parent;
279
+ }
280
+ return false;
281
+ }
117
282
  export { typescriptScopeResolver };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.9-rc.15",
3
+ "version": "1.6.9-rc.17",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",