ucn 3.8.26 → 4.0.1
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 +31 -17
- package/README.md +95 -28
- package/cli/index.js +32 -7
- package/core/account.js +354 -0
- package/core/analysis.js +335 -15
- package/core/build-worker.js +21 -1
- package/core/cache.js +52 -3
- package/core/callers.js +3421 -159
- package/core/confidence.js +82 -19
- package/core/deadcode.js +211 -21
- package/core/execute.js +6 -1
- package/core/graph-build.js +45 -3
- package/core/imports.js +118 -1
- package/core/output/analysis.js +345 -83
- package/core/output/reporting.js +19 -3
- package/core/output/shared.js +33 -2
- package/core/output/tracing.js +208 -10
- package/core/project.js +19 -2
- package/core/registry.js +15 -3
- package/core/shared.js +21 -0
- package/core/tracing.js +534 -190
- package/languages/go.js +317 -6
- package/languages/index.js +79 -0
- package/languages/java.js +243 -16
- package/languages/javascript.js +357 -24
- package/languages/python.js +423 -28
- package/languages/rust.js +377 -8
- package/languages/utils.js +72 -18
- package/mcp/server.js +5 -4
- package/package.json +9 -3
- package/.github/workflows/ci.yml +0 -45
- package/.github/workflows/publish.yml +0 -79
package/core/imports.js
CHANGED
|
@@ -120,6 +120,10 @@ function resolveImport(importPath, fromFile, config = {}) {
|
|
|
120
120
|
if (result) return result;
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
|
+
|
|
124
|
+
// Package self-reference (import own package by name)
|
|
125
|
+
const selfResolved = resolveSelfReference(importPath, fromDir, config);
|
|
126
|
+
if (selfResolved) return selfResolved;
|
|
123
127
|
}
|
|
124
128
|
|
|
125
129
|
// Check Go module imports
|
|
@@ -412,12 +416,124 @@ function resolveRustImport(importPath, fromFile, projectRoot) {
|
|
|
412
416
|
/**
|
|
413
417
|
* Try to resolve a path with various extensions
|
|
414
418
|
*/
|
|
419
|
+
// package.json lookup cache for self-reference resolution (dir -> info|null).
|
|
420
|
+
// Process-lifetime cache: package.json name/exports churn is rare enough that
|
|
421
|
+
// long-lived servers (MCP) tolerate it.
|
|
422
|
+
const _pkgCache = new Map();
|
|
423
|
+
|
|
424
|
+
function _findPackageJson(fromDir, stopDir) {
|
|
425
|
+
let current = fromDir;
|
|
426
|
+
for (let i = 0; i < 8; i++) {
|
|
427
|
+
let info;
|
|
428
|
+
if (_pkgCache.has(current)) {
|
|
429
|
+
info = _pkgCache.get(current);
|
|
430
|
+
} else {
|
|
431
|
+
info = null;
|
|
432
|
+
const candidate = path.join(current, 'package.json');
|
|
433
|
+
try {
|
|
434
|
+
if (fs.existsSync(candidate)) {
|
|
435
|
+
const pkg = JSON.parse(fs.readFileSync(candidate, 'utf-8'));
|
|
436
|
+
info = { dir: current, name: pkg.name, exports: pkg.exports, main: pkg.main };
|
|
437
|
+
}
|
|
438
|
+
} catch { /* unreadable or invalid JSON */ }
|
|
439
|
+
_pkgCache.set(current, info);
|
|
440
|
+
}
|
|
441
|
+
if (info) return info;
|
|
442
|
+
if (stopDir && current === stopDir) break;
|
|
443
|
+
const parent = path.dirname(current);
|
|
444
|
+
if (parent === current) break;
|
|
445
|
+
current = parent;
|
|
446
|
+
}
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/** Flatten an exports-map entry to candidate targets (condition objects in
|
|
451
|
+
* insertion order, arrays in order). 'types' conditions are skipped — they
|
|
452
|
+
* name declaration files, not runtime sources. */
|
|
453
|
+
function _collectExportTargets(entry, out = []) {
|
|
454
|
+
if (typeof entry === 'string') {
|
|
455
|
+
out.push(entry);
|
|
456
|
+
} else if (Array.isArray(entry)) {
|
|
457
|
+
for (const e of entry) _collectExportTargets(e, out);
|
|
458
|
+
} else if (entry && typeof entry === 'object') {
|
|
459
|
+
for (const [cond, v] of Object.entries(entry)) {
|
|
460
|
+
if (cond === 'types') continue;
|
|
461
|
+
_collectExportTargets(v, out);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return out;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Package self-reference: a file importing its own package by name
|
|
469
|
+
* (`import * as z from "zod/v3"` inside the zod repo) — standard in monorepo
|
|
470
|
+
* tests and benchmarks. Resolves through package.json "exports" (conditional
|
|
471
|
+
* objects, arrays, '*' wildcards), accepting the first condition target that
|
|
472
|
+
* lands on a real file.
|
|
473
|
+
*/
|
|
474
|
+
function resolveSelfReference(importPath, fromDir, config) {
|
|
475
|
+
const pkg = _findPackageJson(fromDir, config.root ? path.dirname(config.root) : null);
|
|
476
|
+
if (!pkg || !pkg.name) return null;
|
|
477
|
+
if (importPath !== pkg.name && !importPath.startsWith(pkg.name + '/')) return null;
|
|
478
|
+
const subpath = importPath === pkg.name ? '.' : './' + importPath.slice(pkg.name.length + 1);
|
|
479
|
+
const extensions = config.extensions || getExtensions(config.language);
|
|
480
|
+
const tryTargets = (entry, wildcard) => {
|
|
481
|
+
for (const target of _collectExportTargets(entry)) {
|
|
482
|
+
const concrete = wildcard != null ? target.replace(/\*/g, wildcard) : target;
|
|
483
|
+
const resolved = resolveFilePath(path.resolve(pkg.dir, concrete), extensions);
|
|
484
|
+
if (resolved) return resolved;
|
|
485
|
+
}
|
|
486
|
+
return null;
|
|
487
|
+
};
|
|
488
|
+
const exp = pkg.exports;
|
|
489
|
+
if (typeof exp === 'string') {
|
|
490
|
+
return subpath === '.' ? tryTargets(exp, null) : null;
|
|
491
|
+
}
|
|
492
|
+
if (exp && typeof exp === 'object') {
|
|
493
|
+
if (exp[subpath] !== undefined) {
|
|
494
|
+
const hit = tryTargets(exp[subpath], null);
|
|
495
|
+
if (hit) return hit;
|
|
496
|
+
}
|
|
497
|
+
for (const [key, val] of Object.entries(exp)) {
|
|
498
|
+
const star = key.indexOf('*');
|
|
499
|
+
if (star === -1) continue;
|
|
500
|
+
const pre = key.slice(0, star);
|
|
501
|
+
const post = key.slice(star + 1);
|
|
502
|
+
if (subpath.length > pre.length + post.length &&
|
|
503
|
+
subpath.startsWith(pre) && subpath.endsWith(post)) {
|
|
504
|
+
const wild = subpath.slice(pre.length, subpath.length - post.length);
|
|
505
|
+
const hit = tryTargets(val, wild);
|
|
506
|
+
if (hit) return hit;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
// No exports map: bare name -> main/index; subpath -> direct file
|
|
512
|
+
if (subpath === '.') {
|
|
513
|
+
return (pkg.main && resolveFilePath(path.resolve(pkg.dir, pkg.main), extensions)) ||
|
|
514
|
+
resolveFilePath(path.resolve(pkg.dir, 'index'), extensions);
|
|
515
|
+
}
|
|
516
|
+
return resolveFilePath(path.resolve(pkg.dir, subpath), extensions);
|
|
517
|
+
}
|
|
518
|
+
|
|
415
519
|
function resolveFilePath(basePath, extensions) {
|
|
416
520
|
// Check exact path
|
|
417
521
|
if (fs.existsSync(basePath) && fs.statSync(basePath).isFile()) {
|
|
418
522
|
return basePath;
|
|
419
523
|
}
|
|
420
524
|
|
|
525
|
+
// TS-ESM: explicit '.js'/'.mjs' specifiers refer to '.ts'/'.mts' sources
|
|
526
|
+
// (import specifiers name the compiled output). Remap before probing.
|
|
527
|
+
const esmRemap = { '.js': ['.ts', '.tsx'], '.jsx': ['.tsx'], '.mjs': ['.mts'], '.cjs': ['.cts'] };
|
|
528
|
+
const explicitExt = path.extname(basePath);
|
|
529
|
+
if (esmRemap[explicitExt]) {
|
|
530
|
+
const stem = basePath.slice(0, -explicitExt.length);
|
|
531
|
+
for (const tsExt of esmRemap[explicitExt]) {
|
|
532
|
+
const candidate = stem + tsExt;
|
|
533
|
+
try { if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) return candidate; } catch { /* skip */ }
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
421
537
|
// Try adding extensions
|
|
422
538
|
for (const ext of extensions) {
|
|
423
539
|
const withExt = basePath + ext;
|
|
@@ -633,5 +749,6 @@ module.exports = {
|
|
|
633
749
|
extractImports,
|
|
634
750
|
extractExports,
|
|
635
751
|
resolveImport,
|
|
636
|
-
resolveFilePath
|
|
752
|
+
resolveFilePath,
|
|
753
|
+
findGoModule
|
|
637
754
|
};
|