ucn 5.4.1 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/ucn/SKILL.md +58 -236
- package/.claude/skills/ucn/references/commands.md +144 -68
- package/.claude/skills/ucn/references/resolution-rules.md +171 -0
- package/.claude/skills/ucn/references/trust-contract.md +27 -33
- package/README.md +18 -12
- package/cli/index.js +42 -16
- package/core/accessors.js +118 -3
- package/core/account.js +30 -0
- package/core/analysis.js +508 -264
- package/core/ast-analysis.js +76 -117
- package/core/async-producers.js +1729 -0
- package/core/bridge.js +1877 -268
- package/core/brief.js +6 -0
- package/core/build-worker.js +78 -14
- package/core/cache.js +522 -31
- package/core/callers.js +12053 -1047
- package/core/check.js +30 -38
- package/core/class-identity.js +1028 -0
- package/core/command-contracts.js +6 -6
- package/core/configuration-items.js +86 -0
- package/core/contract-membership.js +2262 -0
- package/core/cpp-scope.js +532 -0
- package/core/deadcode.js +171 -49
- package/core/decorator-binding.js +260 -0
- package/core/discovery.js +281 -164
- package/core/errors.js +52 -0
- package/core/execute.js +21 -9
- package/core/external-macros.js +326 -0
- package/core/graph-build.js +277 -8
- package/core/graph.js +35 -2
- package/core/ignore-rules.js +472 -0
- package/core/imports.js +504 -42
- package/core/index-ir.js +94 -11
- package/core/inprocess-clients.js +636 -0
- package/core/ir.js +87 -8
- package/core/macro-expansion.js +1416 -0
- package/core/output/analysis-ext.js +6 -6
- package/core/output/analysis.js +18 -2
- package/core/output/brief.js +3 -0
- package/core/output/doctor.js +4 -0
- package/core/output/endpoints.js +15 -6
- package/core/output/find.js +11 -2
- package/core/output/lines.js +8 -2
- package/core/output/refactoring.js +121 -14
- package/core/output/reporting.js +31 -3
- package/core/output/shared.js +14 -0
- package/core/output-budget.js +316 -122
- package/core/parallel-build.js +124 -47
- package/core/parser.js +3 -2
- package/core/project.js +457 -60
- package/core/provenance-facts.js +156 -12
- package/core/provenance-overload.js +30 -3
- package/core/provenance.js +104 -30
- package/core/pytest-fixtures.js +434 -0
- package/core/python-fixture-flow.js +17 -3
- package/core/python-modules.js +173 -0
- package/core/reflection.js +877 -0
- package/core/registry.js +1 -1
- package/core/reporting.js +234 -52
- package/core/request-helpers.js +561 -0
- package/core/route-graph.js +1922 -0
- package/core/route-lists.js +2414 -0
- package/core/rust-macro-expansion.js +2113 -0
- package/core/rust-modules.js +185 -0
- package/core/search.js +121 -29
- package/core/shared.js +19 -1
- package/core/test-name-bindings.js +256 -0
- package/core/type-denotation.js +1161 -0
- package/core/type-references.js +3229 -0
- package/core/verify.js +3360 -672
- package/languages/adapter.js +31 -5
- package/languages/c-family.js +4061 -212
- package/languages/c-macro-invocations.js +380 -0
- package/languages/c-preprocessor.js +254 -0
- package/languages/csharp.js +1406 -49
- package/languages/go.js +1243 -70
- package/languages/index.js +728 -6
- package/languages/java.js +473 -61
- package/languages/javascript.js +1270 -197
- package/languages/lexical-scope.js +1280 -0
- package/languages/python.js +1557 -72
- package/languages/rust-macro-rules.js +1020 -0
- package/languages/rust-value-flow.js +416 -0
- package/languages/rust.js +2237 -115
- package/languages/utils.js +298 -4
- package/mcp/server.js +25 -11
- package/package.json +1 -1
package/core/graph-build.js
CHANGED
|
@@ -6,7 +6,23 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const path = require('path');
|
|
9
|
-
const { resolveImport } = require('./imports');
|
|
9
|
+
const { resolveImport, resolveRustModuleFile, rustModDeclarationFiles, jsWorkspacePackages } = require('./imports');
|
|
10
|
+
|
|
11
|
+
const JS_WORKSPACE_LANGUAGES = new Set(['javascript', 'typescript', 'tsx']);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The project's in-repository JS/TS packages by name (fix #397), computed
|
|
15
|
+
* once per build and on first use after a cache load.
|
|
16
|
+
*/
|
|
17
|
+
function jsWorkspacePackagesOf(index, refresh = false) {
|
|
18
|
+
if (!refresh && index._jsWorkspacePackages !== undefined) return index._jsWorkspacePackages;
|
|
19
|
+
const dirs = new Set();
|
|
20
|
+
for (const [fp, fe] of index.files) {
|
|
21
|
+
if (JS_WORKSPACE_LANGUAGES.has(fe.language)) dirs.add(path.dirname(fp));
|
|
22
|
+
}
|
|
23
|
+
index._jsWorkspacePackages = dirs.size > 0 ? jsWorkspacePackages(index.root, dirs) : null;
|
|
24
|
+
return index._jsWorkspacePackages;
|
|
25
|
+
}
|
|
10
26
|
const { langTraits, getParser, safeParse } = require('../languages');
|
|
11
27
|
|
|
12
28
|
function _javaPackageTypes(index) {
|
|
@@ -161,6 +177,32 @@ function _buildCSharpNamespaceIndex(index) {
|
|
|
161
177
|
return namespaces;
|
|
162
178
|
}
|
|
163
179
|
|
|
180
|
+
/**
|
|
181
|
+
* A C# file's using directives by the names C# resolves them to (fix #395):
|
|
182
|
+
* each written name resolved from the namespace its directive sits in,
|
|
183
|
+
* generic arguments of `using static T<X>` dropped. `written` collects the
|
|
184
|
+
* written spellings of names that resolved to something else.
|
|
185
|
+
*/
|
|
186
|
+
function _csharpResolvedImports(index, fileEntry, written) {
|
|
187
|
+
const { csharpResolveUsingName } = require('./type-denotation');
|
|
188
|
+
const details = (fileEntry.importDetails || []).filter(detail => detail.module);
|
|
189
|
+
if (details.length === 0) return fileEntry.imports || [];
|
|
190
|
+
const out = [];
|
|
191
|
+
for (const detail of details) {
|
|
192
|
+
if (!detail.namespace) {
|
|
193
|
+
out.push(String(detail.module).replace(/<.*$/s, ''));
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const resolved = csharpResolveUsingName(index, detail.module, detail.namespace).replace(/<.*$/s, '');
|
|
197
|
+
out.push(resolved);
|
|
198
|
+
if (resolved !== detail.module) {
|
|
199
|
+
if (!written.has(resolved)) written.set(resolved, []);
|
|
200
|
+
written.get(resolved).push(detail.module);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return out;
|
|
204
|
+
}
|
|
205
|
+
|
|
164
206
|
function _resolveCSharpUsing(index, importModule, namespaceIndex = null, opts = {}) {
|
|
165
207
|
const map = namespaceIndex || _buildCSharpNamespaceIndex(index);
|
|
166
208
|
const matches = map.get(importModule) || [];
|
|
@@ -171,6 +213,105 @@ function _resolveCSharpUsing(index, importModule, namespaceIndex = null, opts =
|
|
|
171
213
|
/**
|
|
172
214
|
* Build import/export relationship graphs
|
|
173
215
|
*/
|
|
216
|
+
/** basename -> C/C++ project files carrying it (for include suffix matching). */
|
|
217
|
+
function _buildCIncludeSuffixIndex(index) {
|
|
218
|
+
const byBasename = new Map();
|
|
219
|
+
for (const [filePath, fileEntry] of index.files) {
|
|
220
|
+
if (fileEntry.language !== 'c' && fileEntry.language !== 'cpp') continue;
|
|
221
|
+
const base = path.basename(filePath);
|
|
222
|
+
if (!byBasename.has(base)) byBasename.set(base, []);
|
|
223
|
+
byBasename.get(base).push(filePath);
|
|
224
|
+
}
|
|
225
|
+
return byBasename;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function _resolveCIncludeBySuffix(byBasename, includeName, fromFile) {
|
|
229
|
+
const normalized = String(includeName || '').replace(/\\/g, '/');
|
|
230
|
+
if (!normalized || normalized.startsWith('../') || normalized.includes('/../')) return null;
|
|
231
|
+
const candidates = (byBasename.get(path.posix.basename(normalized)) || [])
|
|
232
|
+
.filter(file => file !== fromFile &&
|
|
233
|
+
file.split(path.sep).join('/').endsWith(`/${normalized}`));
|
|
234
|
+
return candidates.length === 1 ? candidates[0] : null;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function _isRustCrateRootFile(file) {
|
|
238
|
+
const base = path.basename(file);
|
|
239
|
+
return base === 'lib.rs' || base === 'main.rs';
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Rust module files a `use` path names when a segment is reached through a
|
|
244
|
+
* glob re-export or a module re-export binding rather than a `mod` file
|
|
245
|
+
* (fix #369). Walks the module segments from the path root, at each module
|
|
246
|
+
* trying its child module file, then its `pub use m as seg` / `use x::seg`
|
|
247
|
+
* bindings, then its top-level glob imports (bounded depth). Returns the one
|
|
248
|
+
* module file that holds the final item (declared or re-exported), or null
|
|
249
|
+
* when the walk is ambiguous or leaves the project.
|
|
250
|
+
*/
|
|
251
|
+
function _rustWalkUsePath(index, fromFile, spec) {
|
|
252
|
+
const segments = String(spec).split('::').filter(Boolean);
|
|
253
|
+
if (segments.length < 2) return null;
|
|
254
|
+
const root = segments[0];
|
|
255
|
+
let start;
|
|
256
|
+
try {
|
|
257
|
+
start = resolveRustModuleFile(root, fromFile, index.root);
|
|
258
|
+
} catch { start = null; }
|
|
259
|
+
if (!start || !index.files.has(start)) return null;
|
|
260
|
+
const childModule = (file, name) => {
|
|
261
|
+
const base = path.basename(file);
|
|
262
|
+
const dir = base === 'lib.rs' || base === 'main.rs' || base === 'mod.rs'
|
|
263
|
+
? path.dirname(file) : path.join(path.dirname(file), path.basename(file, '.rs'));
|
|
264
|
+
for (const candidate of [path.join(dir, `${name}.rs`), path.join(dir, name, 'mod.rs')]) {
|
|
265
|
+
if (index.files.has(candidate)) {
|
|
266
|
+
const declares = (index.files.get(file)?.symbols || []).some(symbol =>
|
|
267
|
+
symbol.type === 'module' && symbol.name === name);
|
|
268
|
+
if (declares) return candidate;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return null;
|
|
272
|
+
};
|
|
273
|
+
const topLevel = (entry, line) => line == null || !(entry.symbols || []).some(symbol =>
|
|
274
|
+
symbol.type === 'module' && symbol.startLine < line && symbol.endLine >= line);
|
|
275
|
+
// Module files reachable as `name` inside module file `file`.
|
|
276
|
+
const lookup = (file, name, seen, depth) => {
|
|
277
|
+
const key = `${file}\0${name}`;
|
|
278
|
+
if (seen.has(key) || depth > 6) return [];
|
|
279
|
+
seen.add(key);
|
|
280
|
+
const entry = index.files.get(file);
|
|
281
|
+
if (!entry) return [];
|
|
282
|
+
const child = childModule(file, name);
|
|
283
|
+
if (child) return [child];
|
|
284
|
+
const out = [];
|
|
285
|
+
for (const binding of entry.importBindings || []) {
|
|
286
|
+
if ((binding.alias || binding.name) !== name || binding.name === '*') continue;
|
|
287
|
+
try {
|
|
288
|
+
const target = resolveRustModuleFile(binding.module, file, index.root);
|
|
289
|
+
if (target && index.files.has(target) && target !== file) out.push(target);
|
|
290
|
+
} catch { /* resolver gap */ }
|
|
291
|
+
}
|
|
292
|
+
if (out.length > 0) return out;
|
|
293
|
+
for (const detail of entry.importDetails || []) {
|
|
294
|
+
if (detail.type !== 'use-glob' || !detail.module || !topLevel(entry, detail.line)) continue;
|
|
295
|
+
let target;
|
|
296
|
+
try { target = resolveRustModuleFile(detail.module, file, index.root); } catch { target = null; }
|
|
297
|
+
if (!target || !index.files.has(target) || target === file) continue;
|
|
298
|
+
out.push(...lookup(target, name, seen, depth + 1));
|
|
299
|
+
}
|
|
300
|
+
return out;
|
|
301
|
+
};
|
|
302
|
+
let current = [start];
|
|
303
|
+
for (let i = 1; i < segments.length - 1; i++) {
|
|
304
|
+
const next = new Set();
|
|
305
|
+
for (const file of current) {
|
|
306
|
+
for (const hit of lookup(file, segments[i], new Set(), 0)) next.add(hit);
|
|
307
|
+
}
|
|
308
|
+
if (next.size !== 1) return null;
|
|
309
|
+
current = [...next];
|
|
310
|
+
}
|
|
311
|
+
const result = current[0];
|
|
312
|
+
return result && result !== fromFile ? result : null;
|
|
313
|
+
}
|
|
314
|
+
|
|
174
315
|
function buildImportGraph(index) {
|
|
175
316
|
index.importGraph.clear();
|
|
176
317
|
index.exportGraph.clear();
|
|
@@ -198,6 +339,9 @@ function buildImportGraph(index) {
|
|
|
198
339
|
}
|
|
199
340
|
}
|
|
200
341
|
|
|
342
|
+
let cIncludeSuffixIndex = null;
|
|
343
|
+
const includeProbeCache = new Map(); // include search results for this build (fix #365)
|
|
344
|
+
const workspacePackages = jsWorkspacePackagesOf(index, true);
|
|
201
345
|
for (const [filePath, fileEntry] of index.files) {
|
|
202
346
|
const importedFiles = new Set();
|
|
203
347
|
const seenModules = new Set();
|
|
@@ -208,10 +352,21 @@ function buildImportGraph(index) {
|
|
|
208
352
|
// importGraph edges can't (a file importing the target for OTHER
|
|
209
353
|
// names is not evidence about THIS name's module).
|
|
210
354
|
const moduleResolved = {};
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
355
|
+
const includeFallback = {};
|
|
356
|
+
|
|
357
|
+
// C# using directives name what C# resolves them to from the
|
|
358
|
+
// namespace each is written in (fix #395): `using Internal;` inside
|
|
359
|
+
// `namespace Acme.Tests` imports Acme.Internal.
|
|
360
|
+
const csharpWritten = fileEntry.language === 'csharp' ? new Map() : null;
|
|
361
|
+
const effectiveImports = csharpWritten
|
|
362
|
+
? [..._csharpResolvedImports(index, fileEntry, csharpWritten), ...csharpGlobalImports]
|
|
214
363
|
: (fileEntry.imports || []);
|
|
364
|
+
// Rust `mod NAME;` declarations load files by the module layout
|
|
365
|
+
// rules (fix #377: foo.rs + foo/ children, #[path], inline-module
|
|
366
|
+
// nesting), never by a plain name lookup from the declaring directory.
|
|
367
|
+
const rustModFiles = fileEntry.language === 'rust'
|
|
368
|
+
? rustModDeclarationFiles(filePath, fileEntry, candidate => index.files.has(candidate))
|
|
369
|
+
: null;
|
|
215
370
|
for (const importModule of effectiveImports) {
|
|
216
371
|
// Skip null modules (e.g., dynamic include! macros in Rust)
|
|
217
372
|
if (!importModule) continue;
|
|
@@ -221,11 +376,16 @@ function buildImportGraph(index) {
|
|
|
221
376
|
if (seenModules.has(importModule)) continue;
|
|
222
377
|
seenModules.add(importModule);
|
|
223
378
|
|
|
224
|
-
|
|
379
|
+
const rustModTargets = rustModFiles &&
|
|
380
|
+
(fileEntry.importDetails || []).some(detail => detail.type === 'mod' && detail.module === importModule)
|
|
381
|
+
? (rustModFiles.get(importModule) || []) : null;
|
|
382
|
+
let resolved = rustModTargets ? (rustModTargets[0] || null) : resolveImport(importModule, filePath, {
|
|
225
383
|
aliases: index.config.aliases,
|
|
226
384
|
includePaths: index.config.includePaths,
|
|
227
385
|
language: fileEntry.language,
|
|
228
|
-
root: index.root
|
|
386
|
+
root: index.root,
|
|
387
|
+
probeCache: includeProbeCache,
|
|
388
|
+
workspacePackages,
|
|
229
389
|
});
|
|
230
390
|
|
|
231
391
|
// Java package imports: resolve by progressive suffix matching
|
|
@@ -246,6 +406,22 @@ function buildImportGraph(index) {
|
|
|
246
406
|
}
|
|
247
407
|
}
|
|
248
408
|
|
|
409
|
+
// C/C++ quoted include that neither the including file's
|
|
410
|
+
// directory nor any known include directory resolves: the build
|
|
411
|
+
// adds an -I path the index cannot see (no compile database). A
|
|
412
|
+
// project header whose path ends with the include name, when
|
|
413
|
+
// exactly one such header exists, is the only file a compiler
|
|
414
|
+
// search could find inside the project. The edge is recorded as
|
|
415
|
+
// basename-resolved so consumers can weigh it below edges the
|
|
416
|
+
// resolver proved (fix #361).
|
|
417
|
+
if (!resolved && (fileEntry.language === 'c' || fileEntry.language === 'cpp') &&
|
|
418
|
+
importModule.startsWith('./')) {
|
|
419
|
+
if (!cIncludeSuffixIndex) cIncludeSuffixIndex = _buildCIncludeSuffixIndex(index);
|
|
420
|
+
resolved = _resolveCIncludeBySuffix(
|
|
421
|
+
cIncludeSuffixIndex, importModule.slice(2), filePath);
|
|
422
|
+
if (resolved) includeFallback[importModule] = true;
|
|
423
|
+
}
|
|
424
|
+
|
|
249
425
|
let csharpFiles = null;
|
|
250
426
|
if (!resolved && fileEntry.language === 'csharp') {
|
|
251
427
|
const all = _resolveCSharpUsing(
|
|
@@ -256,13 +432,24 @@ function buildImportGraph(index) {
|
|
|
256
432
|
}
|
|
257
433
|
}
|
|
258
434
|
|
|
435
|
+
// Rust module paths through glob re-exports (fix #369): `use
|
|
436
|
+
// cursive::views::LinearLayout` where cursive's root is `pub use
|
|
437
|
+
// cursive_core::*` resolves past the facade crate root instead
|
|
438
|
+
// of stopping at it.
|
|
439
|
+
if (fileEntry.language === 'rust' && importModule.includes('::') &&
|
|
440
|
+
(!resolved || _isRustCrateRootFile(resolved))) {
|
|
441
|
+
const walked = _rustWalkUsePath(index, filePath, importModule);
|
|
442
|
+
if (walked) resolved = walked;
|
|
443
|
+
}
|
|
444
|
+
|
|
259
445
|
if (resolved && index.files.has(resolved)) {
|
|
260
446
|
moduleResolved[importModule] = path.relative(index.root, resolved);
|
|
261
447
|
// For Go, a package import means all files in that directory are dependencies
|
|
262
448
|
// (Go packages span multiple files in the same directory)
|
|
263
449
|
const filesToLink = javaWildcardFiles
|
|
264
450
|
? [...javaWildcardFiles]
|
|
265
|
-
: csharpFiles ? [...csharpFiles]
|
|
451
|
+
: csharpFiles ? [...csharpFiles]
|
|
452
|
+
: rustModTargets?.length > 1 ? [...rustModTargets] : [resolved];
|
|
266
453
|
if (langTraits(fileEntry.language)?.packageScope === 'directory') {
|
|
267
454
|
const pkgDir = path.dirname(resolved);
|
|
268
455
|
const dirFiles = dirToGoFiles.get(pkgDir) || [];
|
|
@@ -286,6 +473,18 @@ function buildImportGraph(index) {
|
|
|
286
473
|
}
|
|
287
474
|
}
|
|
288
475
|
|
|
476
|
+
// The written spelling of a relative C# directive maps to the file
|
|
477
|
+
// its resolved name reaches (alias bindings keep the written text).
|
|
478
|
+
if (csharpWritten) {
|
|
479
|
+
for (const [resolvedName, writtens] of csharpWritten) {
|
|
480
|
+
const rel = moduleResolved[resolvedName];
|
|
481
|
+
if (!rel) continue;
|
|
482
|
+
for (const written of writtens) {
|
|
483
|
+
if (!moduleResolved[written]) moduleResolved[written] = rel;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
289
488
|
// Java same-package visibility needs no import declaration. Add only
|
|
290
489
|
// AST-proven type/static-qualifier references, not a package clique.
|
|
291
490
|
if (fileEntry.language === 'java') {
|
|
@@ -315,7 +514,8 @@ function buildImportGraph(index) {
|
|
|
315
514
|
const resolved = resolveImport(spec, filePath, {
|
|
316
515
|
aliases: index.config.aliases,
|
|
317
516
|
language: fileEntry.language,
|
|
318
|
-
root: index.root
|
|
517
|
+
root: index.root,
|
|
518
|
+
probeCache: includeProbeCache,
|
|
319
519
|
});
|
|
320
520
|
if (resolved && index.files.has(resolved)) {
|
|
321
521
|
moduleResolved[spec] = path.relative(index.root, resolved);
|
|
@@ -330,7 +530,74 @@ function buildImportGraph(index) {
|
|
|
330
530
|
|
|
331
531
|
index.importGraph.set(filePath, importedFiles);
|
|
332
532
|
fileEntry.moduleResolved = moduleResolved;
|
|
533
|
+
if (langTraits(fileEntry.language)?.hasReceiverPackageCalls) {
|
|
534
|
+
_applyGoImportNames(index, filePath, fileEntry, moduleResolved, dirToGoFiles);
|
|
535
|
+
}
|
|
536
|
+
if (Object.keys(includeFallback).length > 0) {
|
|
537
|
+
fileEntry.includeFallback = includeFallback;
|
|
538
|
+
} else {
|
|
539
|
+
delete fileEntry.includeFallback;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* The local name of a Go import that writes none is the package clause of
|
|
546
|
+
* the package it imports, which its path only suggests (fix #400):
|
|
547
|
+
* `example.com/m/internal/go-utils` may declare `package utils`. For a
|
|
548
|
+
* project package that clause is known; an outside package keeps the
|
|
549
|
+
* parser's path-derived name, which no query treats as proof (an
|
|
550
|
+
* identifier the file does not otherwise bind may name such an import).
|
|
551
|
+
* The file's call records were read with the parser's names: when the
|
|
552
|
+
* resolved names differ they are read again on first use (a build's callee
|
|
553
|
+
* index reads them). The parser's suggestion follows the Go tools'
|
|
554
|
+
* convention (goPathPackageName), so few project packages need this.
|
|
555
|
+
*/
|
|
556
|
+
function _applyGoImportNames(index, filePath, fileEntry, moduleResolved, dirToGoFiles) {
|
|
557
|
+
const details = fileEntry.importDetails || [];
|
|
558
|
+
const names = fileEntry.importNames || [];
|
|
559
|
+
if (details.length === 0 || names.length !== details.length) return;
|
|
560
|
+
let next = null;
|
|
561
|
+
for (let i = 0; i < details.length; i++) {
|
|
562
|
+
const detail = details[i];
|
|
563
|
+
let name = detail.names?.[0];
|
|
564
|
+
if (detail.implicitName && moduleResolved[detail.module]) {
|
|
565
|
+
const rel = moduleResolved[detail.module];
|
|
566
|
+
const dir = path.dirname(path.isAbsolute(rel) ? rel : path.join(index.root, rel));
|
|
567
|
+
const clause = (dirToGoFiles.get(dir) || []).map(file => index.files.get(file))
|
|
568
|
+
.find(entry => entry?.packageName && !entry.relativePath?.endsWith('_test.go') &&
|
|
569
|
+
!entry.packageName.endsWith('_test'))?.packageName;
|
|
570
|
+
if (clause) name = clause;
|
|
571
|
+
}
|
|
572
|
+
if (name && names[i] !== name) {
|
|
573
|
+
if (!next) next = names.slice();
|
|
574
|
+
next[i] = name;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
if (!next) return;
|
|
578
|
+
const changed = new Set();
|
|
579
|
+
for (let i = 0; i < next.length; i++) {
|
|
580
|
+
if (next[i] !== names[i]) {
|
|
581
|
+
if (names[i]) changed.add(names[i]);
|
|
582
|
+
changed.add(next[i]);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
fileEntry.importNames = next;
|
|
586
|
+
for (const binding of fileEntry.importBindings || []) {
|
|
587
|
+
const at = details.findIndex(detail => detail.module === binding.module && detail.line === binding.line);
|
|
588
|
+
if (at >= 0 && details[at].implicitName) binding.name = next[at];
|
|
589
|
+
}
|
|
590
|
+
// An import name is only ever written as a qualifier (`name.X`): records
|
|
591
|
+
// of a file that never writes one of the changed names are unchanged.
|
|
592
|
+
let content = '';
|
|
593
|
+
try { content = index._readFile(filePath); } catch { content = null; }
|
|
594
|
+
if (content != null && ![...changed].some(name => content.includes(`${name}.`))) return;
|
|
595
|
+
// Drop the records read with the old names (loading this file's shard
|
|
596
|
+
// first, so a later shard load cannot bring them back).
|
|
597
|
+
if (index._callsCachePrepared && !index._callsCacheLoaded) {
|
|
598
|
+
require('./cache').ensureCallsShardForFile(index, filePath);
|
|
333
599
|
}
|
|
600
|
+
if (index.callsCache.delete(filePath)) index.callsCacheDirty = true;
|
|
334
601
|
}
|
|
335
602
|
|
|
336
603
|
/**
|
|
@@ -504,8 +771,10 @@ function splitParentList(clause) {
|
|
|
504
771
|
module.exports = {
|
|
505
772
|
buildDirIndex,
|
|
506
773
|
buildImportGraph,
|
|
774
|
+
jsWorkspacePackagesOf,
|
|
507
775
|
buildInheritanceGraph,
|
|
508
776
|
splitParentList,
|
|
509
777
|
_resolveJavaPackageImport,
|
|
510
778
|
_resolveCSharpUsing,
|
|
779
|
+
_rustWalkUsePath,
|
|
511
780
|
};
|
package/core/graph.js
CHANGED
|
@@ -83,7 +83,8 @@ function imports(index, filePath) {
|
|
|
83
83
|
let resolvedPath = resolveImport(imp.module, normalizedPath, {
|
|
84
84
|
aliases: index.config.aliases,
|
|
85
85
|
language: fileEntry.language,
|
|
86
|
-
root: index.root
|
|
86
|
+
root: index.root,
|
|
87
|
+
workspacePackages: require('./graph-build').jsWorkspacePackagesOf(index),
|
|
87
88
|
});
|
|
88
89
|
|
|
89
90
|
// Java package imports: resolve by progressive suffix matching
|
|
@@ -358,7 +359,8 @@ function fileExports(index, filePath, _visited) {
|
|
|
358
359
|
const resolvedSrc = resolveImport(exp.source, absPath, {
|
|
359
360
|
language: fileEntry.language,
|
|
360
361
|
root: index.root,
|
|
361
|
-
extensions: index.extensions
|
|
362
|
+
extensions: index.extensions,
|
|
363
|
+
workspacePackages: require('./graph-build').jsWorkspacePackagesOf(index),
|
|
362
364
|
});
|
|
363
365
|
if (resolvedSrc) {
|
|
364
366
|
const sourceEntry = index.files.get(resolvedSrc);
|
|
@@ -367,6 +369,8 @@ function fileExports(index, filePath, _visited) {
|
|
|
367
369
|
if (exp.type === 're-export-all') {
|
|
368
370
|
const sourceExportsResult = fileExports(index, resolvedSrc, visited);
|
|
369
371
|
for (const srcExp of sourceExportsResult) {
|
|
372
|
+
// `export *` never re-exports a default export.
|
|
373
|
+
if (srcExp.isDefault) continue;
|
|
370
374
|
if (!matchedNames.has(srcExp.name)) {
|
|
371
375
|
matchedNames.add(srcExp.name);
|
|
372
376
|
// The entry belongs to the BARREL file —
|
|
@@ -482,6 +486,35 @@ function fileExports(index, filePath, _visited) {
|
|
|
482
486
|
}
|
|
483
487
|
}
|
|
484
488
|
|
|
489
|
+
// fix #367d: default exports of a VALUE that is not an indexed symbol
|
|
490
|
+
// (`const axios = createInstance(); export default axios;`,
|
|
491
|
+
// `module.exports = instance`, `export default { ... }`). Import sites
|
|
492
|
+
// bind these; leaving them out made `api` report no exports at all.
|
|
493
|
+
if (fileEntry.exportDetails) {
|
|
494
|
+
const matched = new Set(results.map(r => r.sourceName || r.name));
|
|
495
|
+
for (const exp of fileEntry.exportDetails) {
|
|
496
|
+
const defaultExport = exp.type === 'default' ||
|
|
497
|
+
(exp.type === 'module.exports' && exp.defaultLike);
|
|
498
|
+
if (!defaultExport || !exp.name || matched.has(exp.name)) continue;
|
|
499
|
+
if (fileEntry.symbols.some(symbol => symbol.name === exp.name && !symbol.className)) continue;
|
|
500
|
+
matched.add(exp.name);
|
|
501
|
+
const anonymous = exp.type === 'default' && exp.name === 'default';
|
|
502
|
+
results.push({
|
|
503
|
+
name: exp.name,
|
|
504
|
+
type: 'variable',
|
|
505
|
+
file: fileEntry.relativePath,
|
|
506
|
+
startLine: exp.line,
|
|
507
|
+
endLine: exp.line,
|
|
508
|
+
params: undefined,
|
|
509
|
+
returnType: null,
|
|
510
|
+
signature: exp.type === 'module.exports'
|
|
511
|
+
? `module.exports = ${exp.localName || exp.name}`
|
|
512
|
+
: anonymous ? 'export default' : `export default ${exp.name}`,
|
|
513
|
+
isDefault: true,
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
485
518
|
// Python __all__ re-exports: names listed in __all__ that come from imports
|
|
486
519
|
// e.g. __init__.py: `from .utils import helper` + `__all__ = ["helper"]`
|
|
487
520
|
// `helper` is in fileEntry.exports but not in fileEntry.symbols
|