@nudojs/service 1.0.1 → 2.0.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/dist/{chunk-2VHITMTM.js → chunk-ADFCZP72.js} +70 -2
- package/dist/config-Cqj8zeZH.d.ts +117 -0
- package/dist/evaluator/evaluator-api.d.ts +3 -12
- package/dist/evaluator/evaluator-api.js +3 -1
- package/dist/index.d.ts +367 -16
- package/dist/index.js +1814 -228
- package/package.json +5 -5
- package/dist/config-NqDRWWpn.d.ts +0 -68
package/dist/index.js
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
|
+
DEFAULT_ANALYSIS_MODE,
|
|
3
|
+
analysisConfig,
|
|
2
4
|
builtinProtoMember,
|
|
3
5
|
builtinProtoMemberNames,
|
|
6
|
+
clearPathEnvCaches,
|
|
4
7
|
describeAbsMember,
|
|
8
|
+
diskCacheRoot,
|
|
5
9
|
findProjectConfig,
|
|
6
10
|
interfaceConfig,
|
|
7
11
|
loadEnvs,
|
|
8
12
|
matchesEmitAllowlist,
|
|
9
13
|
preloadPathEnvs
|
|
10
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-ADFCZP72.js";
|
|
11
15
|
|
|
12
16
|
// src/analyzer.ts
|
|
13
|
-
import { readFileSync as readFileSync5, existsSync as
|
|
14
|
-
import { resolve as
|
|
17
|
+
import { readFileSync as readFileSync5, existsSync as existsSync5, statSync as statSync5, realpathSync } from "fs";
|
|
18
|
+
import { resolve as resolve6, dirname as dirname7 } from "path";
|
|
15
19
|
import traverse from "@babel/traverse";
|
|
16
20
|
import {
|
|
17
21
|
createEnvironment as createEnvironment2,
|
|
@@ -34,6 +38,9 @@ import {
|
|
|
34
38
|
strLit,
|
|
35
39
|
boolLit,
|
|
36
40
|
abs as makeAbsVal,
|
|
41
|
+
formalParamsFromNodes,
|
|
42
|
+
formalParamDisplayNames,
|
|
43
|
+
runWithEvalMissingSlot,
|
|
37
44
|
stableAnalyzeKeySource as stableAnalyzeKeySource2,
|
|
38
45
|
fnFingerprints
|
|
39
46
|
} from "@nudojs/core";
|
|
@@ -383,9 +390,70 @@ function defaultLoadModule(spec, fromFile) {
|
|
|
383
390
|
}
|
|
384
391
|
}
|
|
385
392
|
|
|
393
|
+
// src/env-path-deps.ts
|
|
394
|
+
import { existsSync as existsSync2, statSync as statSync2 } from "fs";
|
|
395
|
+
import { dirname as dirname2, resolve as resolve2, join as join2 } from "path";
|
|
396
|
+
import { extractAllLoadSpecs } from "@nudojs/core";
|
|
397
|
+
var envDependents = /* @__PURE__ */ new Map();
|
|
398
|
+
function norm(p) {
|
|
399
|
+
return p.replace(/\\/g, "/");
|
|
400
|
+
}
|
|
401
|
+
function resolveLoadSpecPath(spec, fromFile) {
|
|
402
|
+
if (!spec.startsWith(".") && !spec.startsWith("/")) return void 0;
|
|
403
|
+
try {
|
|
404
|
+
const base = dirname2(resolve2(fromFile));
|
|
405
|
+
const p = resolve2(base, spec);
|
|
406
|
+
for (const cand of [
|
|
407
|
+
p,
|
|
408
|
+
`${p}.js`,
|
|
409
|
+
`${p}.mjs`,
|
|
410
|
+
`${p}.ts`,
|
|
411
|
+
join2(p, "index.js"),
|
|
412
|
+
join2(p, "index.mjs"),
|
|
413
|
+
join2(p, "index.ts")
|
|
414
|
+
]) {
|
|
415
|
+
if (existsSync2(cand) && !statSync2(cand).isDirectory()) return cand;
|
|
416
|
+
}
|
|
417
|
+
return void 0;
|
|
418
|
+
} catch {
|
|
419
|
+
return void 0;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
function noteEnvPathDeps(sourcePath, source) {
|
|
423
|
+
const src = norm(sourcePath);
|
|
424
|
+
for (const set of envDependents.values()) set.delete(src);
|
|
425
|
+
let specs = [];
|
|
426
|
+
try {
|
|
427
|
+
specs = extractAllLoadSpecs(source);
|
|
428
|
+
} catch {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
for (const spec of specs) {
|
|
432
|
+
if (!spec.startsWith(".") && !spec.startsWith("/")) continue;
|
|
433
|
+
const abs = resolveLoadSpecPath(spec, sourcePath);
|
|
434
|
+
if (!abs) continue;
|
|
435
|
+
const key = norm(abs);
|
|
436
|
+
if (!envDependents.has(key)) envDependents.set(key, /* @__PURE__ */ new Set());
|
|
437
|
+
envDependents.get(key).add(src);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
function envPathDependents(envPath) {
|
|
441
|
+
return [...envDependents.get(norm(envPath)) ?? []];
|
|
442
|
+
}
|
|
443
|
+
function clearEnvPathDeps() {
|
|
444
|
+
envDependents.clear();
|
|
445
|
+
}
|
|
446
|
+
function isEnvTemplatePath(path) {
|
|
447
|
+
const lower = norm(path).toLowerCase();
|
|
448
|
+
return lower.endsWith(".env.js") || lower.endsWith(".env.mjs") || lower.endsWith(".env.ts") || lower.endsWith(".env.cjs") || lower.endsWith(".env.cts") || lower.endsWith(".env.mts") || /\/mock-[^/]+\.(js|mjs|ts)$/.test(lower) || /\.mock\.(js|mjs|ts)$/.test(lower);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// src/analyzer.ts
|
|
452
|
+
import { loadModuleDepsFingerprint as loadModuleDepsFingerprint2, hashSource as hashSource2 } from "@nudojs/core";
|
|
453
|
+
|
|
386
454
|
// src/abs-modules-graph.ts
|
|
387
|
-
import { readFileSync as readFileSync4, statSync as
|
|
388
|
-
import { dirname as
|
|
455
|
+
import { readFileSync as readFileSync4, statSync as statSync4 } from "fs";
|
|
456
|
+
import { dirname as dirname6, resolve as resolve5 } from "path";
|
|
389
457
|
import { parse as parse3 } from "@nudojs/parser";
|
|
390
458
|
import {
|
|
391
459
|
evalProgramAbs,
|
|
@@ -398,19 +466,19 @@ import {
|
|
|
398
466
|
confJoin as confJoin2,
|
|
399
467
|
absFunction as absFunction2
|
|
400
468
|
} from "@nudojs/core";
|
|
401
|
-
import { dirname as
|
|
469
|
+
import { dirname as dirname5 } from "path";
|
|
402
470
|
|
|
403
471
|
// src/harvest-auto.ts
|
|
404
472
|
import { parse as parse2 } from "@nudojs/parser";
|
|
405
473
|
|
|
406
474
|
// src/static-imports.ts
|
|
407
475
|
import { parse } from "@nudojs/parser";
|
|
408
|
-
import { readFileSync as readFileSync2, existsSync as
|
|
409
|
-
import { resolve as
|
|
476
|
+
import { readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
|
|
477
|
+
import { resolve as resolve3, dirname as dirname3, join as join3 } from "path";
|
|
410
478
|
import { generalizeFromAst } from "@nudojs/core";
|
|
411
479
|
function resolveRelative(fromFile, spec) {
|
|
412
480
|
if (!spec.startsWith(".") && !spec.startsWith("/")) return void 0;
|
|
413
|
-
const base =
|
|
481
|
+
const base = resolve3(dirname3(fromFile), spec);
|
|
414
482
|
for (const cand of [
|
|
415
483
|
base,
|
|
416
484
|
base + ".js",
|
|
@@ -419,12 +487,12 @@ function resolveRelative(fromFile, spec) {
|
|
|
419
487
|
base + ".ts",
|
|
420
488
|
base + ".mts",
|
|
421
489
|
base + ".cts",
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
490
|
+
join3(base, "index.js"),
|
|
491
|
+
join3(base, "index.cjs"),
|
|
492
|
+
join3(base, "index.mjs"),
|
|
493
|
+
join3(base, "index.ts")
|
|
426
494
|
]) {
|
|
427
|
-
if (
|
|
495
|
+
if (existsSync3(cand)) return cand;
|
|
428
496
|
}
|
|
429
497
|
return void 0;
|
|
430
498
|
}
|
|
@@ -543,14 +611,14 @@ function collectCjsExports(stmt, named, setDefault) {
|
|
|
543
611
|
function collectStaticImports(entryFile, maxDepth = 8) {
|
|
544
612
|
const graph = /* @__PURE__ */ new Map();
|
|
545
613
|
const queue = [
|
|
546
|
-
{ file:
|
|
614
|
+
{ file: resolve3(entryFile), depth: 0 }
|
|
547
615
|
];
|
|
548
616
|
const seen = /* @__PURE__ */ new Set();
|
|
549
617
|
while (queue.length > 0) {
|
|
550
618
|
const { file, depth } = queue.shift();
|
|
551
619
|
if (seen.has(file) || depth > maxDepth) continue;
|
|
552
620
|
seen.add(file);
|
|
553
|
-
if (!
|
|
621
|
+
if (!existsSync3(file)) continue;
|
|
554
622
|
const source = readFileSync2(file, "utf8");
|
|
555
623
|
const mod = analyzeExportsFromSource(file, source);
|
|
556
624
|
graph.set(file, mod);
|
|
@@ -564,19 +632,19 @@ function collectStaticImports(entryFile, maxDepth = 8) {
|
|
|
564
632
|
}
|
|
565
633
|
|
|
566
634
|
// src/harvest-package.ts
|
|
567
|
-
import { existsSync as
|
|
568
|
-
import { join as
|
|
635
|
+
import { existsSync as existsSync4, readdirSync, statSync as statSync3, readFileSync as readFileSync3 } from "fs";
|
|
636
|
+
import { join as join4, resolve as resolve4, dirname as dirname4, basename } from "path";
|
|
569
637
|
import { harvestDts } from "@nudojs/harvester";
|
|
570
638
|
import { formatShape } from "@nudojs/core";
|
|
571
639
|
function resolvePackageRoot(pkg, fromDir = process.cwd()) {
|
|
572
|
-
let dir =
|
|
640
|
+
let dir = resolve4(fromDir);
|
|
573
641
|
const bare = pkg.replace(/^@/, "").replace(/\//g, "__");
|
|
574
642
|
for (let i = 0; i < 8; i++) {
|
|
575
|
-
const typesPath =
|
|
576
|
-
if (
|
|
577
|
-
const pkgPath =
|
|
578
|
-
if (
|
|
579
|
-
const parent =
|
|
643
|
+
const typesPath = join4(dir, "node_modules", "@types", bare);
|
|
644
|
+
if (existsSync4(typesPath)) return typesPath;
|
|
645
|
+
const pkgPath = join4(dir, "node_modules", pkg);
|
|
646
|
+
if (existsSync4(pkgPath)) return pkgPath;
|
|
647
|
+
const parent = resolve4(dir, "..");
|
|
580
648
|
if (parent === dir) break;
|
|
581
649
|
dir = parent;
|
|
582
650
|
}
|
|
@@ -606,10 +674,10 @@ function collectDtsFiles(root, maxFiles = 8) {
|
|
|
606
674
|
for (const name of entries) {
|
|
607
675
|
if (out.length >= maxFiles * 3) return;
|
|
608
676
|
if (SKIP_DIRS.has(name)) continue;
|
|
609
|
-
const p =
|
|
677
|
+
const p = join4(dir, name);
|
|
610
678
|
let st;
|
|
611
679
|
try {
|
|
612
|
-
st =
|
|
680
|
+
st = statSync3(p);
|
|
613
681
|
} catch {
|
|
614
682
|
continue;
|
|
615
683
|
}
|
|
@@ -625,15 +693,15 @@ function collectDtsFiles(root, maxFiles = 8) {
|
|
|
625
693
|
return out.slice(0, maxFiles);
|
|
626
694
|
}
|
|
627
695
|
function entryDtsFromPackageJson(root) {
|
|
628
|
-
const pj =
|
|
629
|
-
if (!
|
|
696
|
+
const pj = join4(root, "package.json");
|
|
697
|
+
if (!existsSync4(pj)) return void 0;
|
|
630
698
|
try {
|
|
631
699
|
const raw = JSON.parse(readFileSync3(pj, "utf8"));
|
|
632
700
|
const entry = raw.types ?? raw.typings;
|
|
633
701
|
if (typeof entry === "string" && entry.endsWith(".d.ts")) {
|
|
634
|
-
const p =
|
|
635
|
-
if (
|
|
636
|
-
const st =
|
|
702
|
+
const p = resolve4(root, entry);
|
|
703
|
+
if (existsSync4(p)) {
|
|
704
|
+
const st = statSync3(p);
|
|
637
705
|
if (st.size <= MAX_DTS_BYTES) return p;
|
|
638
706
|
}
|
|
639
707
|
}
|
|
@@ -649,7 +717,7 @@ function collectDtsFromEntry(entry, maxFiles = 200) {
|
|
|
649
717
|
const RELATIVE_FROM_REGEX = /\bfrom\s+["'](\.[^"']+)["']/g;
|
|
650
718
|
while (queue.length > 0 && files.length < maxFiles) {
|
|
651
719
|
const current = queue.shift();
|
|
652
|
-
if (seen.has(current) || !
|
|
720
|
+
if (seen.has(current) || !existsSync4(current)) continue;
|
|
653
721
|
seen.add(current);
|
|
654
722
|
if (!current.endsWith(".d.ts")) continue;
|
|
655
723
|
files.push(current);
|
|
@@ -659,14 +727,14 @@ function collectDtsFromEntry(entry, maxFiles = 200) {
|
|
|
659
727
|
} catch {
|
|
660
728
|
continue;
|
|
661
729
|
}
|
|
662
|
-
const dir =
|
|
730
|
+
const dir = dirname4(current);
|
|
663
731
|
for (const match of text.matchAll(REFERENCE_PATH_REGEX)) {
|
|
664
|
-
queue.push(
|
|
732
|
+
queue.push(resolve4(dir, match[1]));
|
|
665
733
|
}
|
|
666
734
|
for (const match of text.matchAll(RELATIVE_FROM_REGEX)) {
|
|
667
|
-
const base =
|
|
668
|
-
for (const candidate of [`${base}.d.ts`,
|
|
669
|
-
if (
|
|
735
|
+
const base = resolve4(dir, match[1]);
|
|
736
|
+
for (const candidate of [`${base}.d.ts`, join4(base, "index.d.ts")]) {
|
|
737
|
+
if (existsSync4(candidate)) {
|
|
670
738
|
queue.push(candidate);
|
|
671
739
|
break;
|
|
672
740
|
}
|
|
@@ -678,11 +746,11 @@ function collectDtsFromEntry(entry, maxFiles = 200) {
|
|
|
678
746
|
function harvestPackage(pkg, fromDir, maxFiles = 8) {
|
|
679
747
|
const root = resolvePackageRoot(pkg, fromDir);
|
|
680
748
|
if (!root) return { error: `package not found: ${pkg}` };
|
|
681
|
-
const entry = entryDtsFromPackageJson(root) ??
|
|
682
|
-
let dtsFiles =
|
|
749
|
+
const entry = entryDtsFromPackageJson(root) ?? join4(root, "index.d.ts");
|
|
750
|
+
let dtsFiles = existsSync4(entry) ? collectDtsFromEntry(entry, maxFiles) : [];
|
|
683
751
|
if (dtsFiles.length === 0) {
|
|
684
752
|
const collected = collectDtsFiles(root, maxFiles);
|
|
685
|
-
dtsFiles = entry &&
|
|
753
|
+
dtsFiles = entry && existsSync4(entry) && !collected.includes(entry) ? [entry, ...collected.filter((f) => f !== entry)].slice(0, maxFiles) : collected;
|
|
686
754
|
}
|
|
687
755
|
if (dtsFiles.length === 0) {
|
|
688
756
|
return { error: `no .d.ts under ${root}` };
|
|
@@ -840,7 +908,7 @@ function bareSpecToAbsModules(spec, fromFile) {
|
|
|
840
908
|
const parts = spec.split("/");
|
|
841
909
|
const pkg = spec.startsWith("@") ? parts.length >= 2 ? `${parts[0]}/${parts[1]}` : void 0 : parts[0];
|
|
842
910
|
if (!pkg) return void 0;
|
|
843
|
-
const table = harvestToAbsModules(pkg,
|
|
911
|
+
const table = harvestToAbsModules(pkg, dirname5(fromFile));
|
|
844
912
|
return table[spec] ?? table[pkg];
|
|
845
913
|
}
|
|
846
914
|
|
|
@@ -848,8 +916,8 @@ function bareSpecToAbsModules(spec, fromFile) {
|
|
|
848
916
|
function defaultAbsLoadModule(spec, fromFile) {
|
|
849
917
|
if (!spec.startsWith(".") && !spec.startsWith("/")) return void 0;
|
|
850
918
|
try {
|
|
851
|
-
const p =
|
|
852
|
-
for (const cand of [p, `${p}.js`, `${p}.mjs`, `${p}.ts`,
|
|
919
|
+
const p = resolve5(dirname6(resolve5(fromFile)), spec);
|
|
920
|
+
for (const cand of [p, `${p}.js`, `${p}.mjs`, `${p}.ts`, resolve5(p, "index.js")]) {
|
|
853
921
|
try {
|
|
854
922
|
return readFileSync4(cand, "utf-8");
|
|
855
923
|
} catch {
|
|
@@ -861,8 +929,8 @@ function defaultAbsLoadModule(spec, fromFile) {
|
|
|
861
929
|
}
|
|
862
930
|
}
|
|
863
931
|
function relCandidates(spec, fromFile) {
|
|
864
|
-
const p =
|
|
865
|
-
return [p, `${p}.js`, `${p}.mjs`, `${p}.ts`,
|
|
932
|
+
const p = resolve5(dirname6(resolve5(fromFile)), spec);
|
|
933
|
+
return [p, `${p}.js`, `${p}.mjs`, `${p}.ts`, resolve5(p, "index.js")];
|
|
866
934
|
}
|
|
867
935
|
function resolveRel(spec, fromFile) {
|
|
868
936
|
if (!spec.startsWith(".") && !spec.startsWith("/")) return null;
|
|
@@ -966,7 +1034,7 @@ function evalAbsModuleGraph(entrySource, entryFile, opts = {}) {
|
|
|
966
1034
|
const shared = absModuleCache.get(absPath);
|
|
967
1035
|
if (shared) {
|
|
968
1036
|
try {
|
|
969
|
-
const st =
|
|
1037
|
+
const st = statSync4(absPath);
|
|
970
1038
|
if (st.mtimeMs === shared.mtimeMs && st.size === shared.size) {
|
|
971
1039
|
for (const iss of shared.issues) pushIssue(iss.kind, iss.label, iss.reason);
|
|
972
1040
|
cache.set(absPath, shared.exports);
|
|
@@ -1031,7 +1099,7 @@ function evalAbsModuleGraph(entrySource, entryFile, opts = {}) {
|
|
|
1031
1099
|
cache.set(absPath, exports);
|
|
1032
1100
|
let fingerprint;
|
|
1033
1101
|
try {
|
|
1034
|
-
const st =
|
|
1102
|
+
const st = statSync4(absPath);
|
|
1035
1103
|
fingerprint = { mtimeMs: st.mtimeMs, size: st.size };
|
|
1036
1104
|
} catch {
|
|
1037
1105
|
}
|
|
@@ -1102,7 +1170,8 @@ import {
|
|
|
1102
1170
|
stableAnalyzeKeySource,
|
|
1103
1171
|
formatAbs as formatAbs3,
|
|
1104
1172
|
hashSource,
|
|
1105
|
-
getFnImpl as getFnImpl2
|
|
1173
|
+
getFnImpl as getFnImpl2,
|
|
1174
|
+
loadModuleDepsFingerprint
|
|
1106
1175
|
} from "@nudojs/core";
|
|
1107
1176
|
import { parse as parse4, extractInlineDirectives } from "@nudojs/parser";
|
|
1108
1177
|
|
|
@@ -1304,6 +1373,15 @@ function isBPathCapable(source, envNames = []) {
|
|
|
1304
1373
|
}
|
|
1305
1374
|
var bRunByFile = /* @__PURE__ */ new Map();
|
|
1306
1375
|
var MAX_B_RUN_CACHE = 32;
|
|
1376
|
+
function bPathDepKey(source, filePath) {
|
|
1377
|
+
try {
|
|
1378
|
+
const fp = loadModuleDepsFingerprint(source, defaultLoadModule, filePath);
|
|
1379
|
+
if (fp.truncated) return null;
|
|
1380
|
+
return hashSource(fp.fp);
|
|
1381
|
+
} catch {
|
|
1382
|
+
return null;
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1307
1385
|
function clearBPathCache() {
|
|
1308
1386
|
bRunByFile.clear();
|
|
1309
1387
|
clearAnalysisFileCache();
|
|
@@ -1316,12 +1394,12 @@ function evictBPathCacheForFiles(files) {
|
|
|
1316
1394
|
}
|
|
1317
1395
|
return n;
|
|
1318
1396
|
}
|
|
1319
|
-
function bPathCacheSet(filePath, stableSource, mode, envKey, mockKey, value) {
|
|
1397
|
+
function bPathCacheSet(filePath, stableSource, mode, envKey, mockKey, depKey, value) {
|
|
1320
1398
|
if (bRunByFile.size >= MAX_B_RUN_CACHE && !bRunByFile.has(filePath)) {
|
|
1321
1399
|
const oldest = bRunByFile.keys().next().value;
|
|
1322
1400
|
if (oldest !== void 0) bRunByFile.delete(oldest);
|
|
1323
1401
|
}
|
|
1324
|
-
bRunByFile.set(filePath, { stableSource, mode, envKey, mockKey, value });
|
|
1402
|
+
bRunByFile.set(filePath, { stableSource, mode, envKey, mockKey, depKey, value });
|
|
1325
1403
|
}
|
|
1326
1404
|
function tryRunBPath(source, filePath, opts = {}) {
|
|
1327
1405
|
if (!isBPathCapable(source, opts.envNames ?? [])) return void 0;
|
|
@@ -1329,11 +1407,15 @@ function tryRunBPath(source, filePath, opts = {}) {
|
|
|
1329
1407
|
const envKey = (opts.envNames ?? []).join(",");
|
|
1330
1408
|
const mockKey = mockSeedFingerprint(opts.mocks);
|
|
1331
1409
|
const stable = stableAnalyzeKeySource(source);
|
|
1332
|
-
const
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
bRunByFile.
|
|
1336
|
-
|
|
1410
|
+
const depKey = bPathDepKey(source, filePath);
|
|
1411
|
+
const canCache = depKey !== null;
|
|
1412
|
+
if (canCache) {
|
|
1413
|
+
const cached = bRunByFile.get(filePath);
|
|
1414
|
+
if (cached && cached.stableSource === stable && cached.mode === mode && cached.envKey === envKey && cached.mockKey === mockKey && cached.depKey === depKey) {
|
|
1415
|
+
bRunByFile.delete(filePath);
|
|
1416
|
+
bRunByFile.set(filePath, cached);
|
|
1417
|
+
return cached.value ?? void 0;
|
|
1418
|
+
}
|
|
1337
1419
|
}
|
|
1338
1420
|
let out = null;
|
|
1339
1421
|
try {
|
|
@@ -1378,7 +1460,9 @@ function tryRunBPath(source, filePath, opts = {}) {
|
|
|
1378
1460
|
} catch {
|
|
1379
1461
|
out = null;
|
|
1380
1462
|
}
|
|
1381
|
-
|
|
1463
|
+
if (canCache && depKey !== null) {
|
|
1464
|
+
bPathCacheSet(filePath, stable, mode, envKey, mockKey, depKey, out);
|
|
1465
|
+
}
|
|
1382
1466
|
return out ?? void 0;
|
|
1383
1467
|
}
|
|
1384
1468
|
function tryBPathCallFull(source, filePath, fnName, args, opts = {}) {
|
|
@@ -1520,6 +1604,9 @@ function collectDeclared(file) {
|
|
|
1520
1604
|
case "ImportNamespaceSpecifier":
|
|
1521
1605
|
addId(o.local);
|
|
1522
1606
|
break;
|
|
1607
|
+
case "CatchClause":
|
|
1608
|
+
addId(o.param);
|
|
1609
|
+
break;
|
|
1523
1610
|
default:
|
|
1524
1611
|
break;
|
|
1525
1612
|
}
|
|
@@ -1621,10 +1708,10 @@ function collectBPathDiagnostics(source, extraKnown) {
|
|
|
1621
1708
|
// src/analyzer.ts
|
|
1622
1709
|
import { setAbsTruncationCollector as setAbsTruncationCollector2 } from "@nudojs/core";
|
|
1623
1710
|
function resolveImportPath(specifier, fromDir) {
|
|
1624
|
-
const basePath =
|
|
1711
|
+
const basePath = resolve6(fromDir, specifier);
|
|
1625
1712
|
for (const ext of ["", ".js", ".ts", ".mjs"]) {
|
|
1626
1713
|
const candidate = basePath + ext;
|
|
1627
|
-
if (
|
|
1714
|
+
if (existsSync5(candidate)) return candidate;
|
|
1628
1715
|
}
|
|
1629
1716
|
return null;
|
|
1630
1717
|
}
|
|
@@ -1644,7 +1731,7 @@ function buildModuleGraph(files, cache) {
|
|
|
1644
1731
|
if (cache) {
|
|
1645
1732
|
let stat = null;
|
|
1646
1733
|
try {
|
|
1647
|
-
stat =
|
|
1734
|
+
stat = statSync5(file);
|
|
1648
1735
|
} catch {
|
|
1649
1736
|
}
|
|
1650
1737
|
const cached = stat ? cache.get(file) : void 0;
|
|
@@ -1673,7 +1760,7 @@ function extractImportEdges(file) {
|
|
|
1673
1760
|
if (stmt.type !== "ImportDeclaration") continue;
|
|
1674
1761
|
const specifier = stmt.source.value;
|
|
1675
1762
|
if (!specifier.startsWith(".") && !specifier.startsWith("/")) continue;
|
|
1676
|
-
const resolved = resolveImportPath(specifier,
|
|
1763
|
+
const resolved = resolveImportPath(specifier, dirname7(file));
|
|
1677
1764
|
if (resolved) edges.push(resolved);
|
|
1678
1765
|
}
|
|
1679
1766
|
return edges;
|
|
@@ -1748,23 +1835,13 @@ function locFromNode(node) {
|
|
|
1748
1835
|
}
|
|
1749
1836
|
function extractParamNames(node) {
|
|
1750
1837
|
const fn = node.type === "ExportDefaultDeclaration" ? node.declaration : node;
|
|
1751
|
-
if (fn.type === "FunctionDeclaration" || fn.type === "FunctionExpression" || fn.type === "ArrowFunctionExpression") {
|
|
1752
|
-
return fn.params
|
|
1753
|
-
if (p.type === "Identifier") return p.name;
|
|
1754
|
-
if (p.type === "AssignmentPattern" && p.left.type === "Identifier") return p.left.name;
|
|
1755
|
-
if (p.type === "RestElement" && p.argument.type === "Identifier") return `...${p.argument.name}`;
|
|
1756
|
-
return "_";
|
|
1757
|
-
});
|
|
1838
|
+
if (fn.type === "FunctionDeclaration" || fn.type === "FunctionExpression" || fn.type === "ArrowFunctionExpression" || fn.type === "ClassMethod" || fn.type === "ObjectMethod" || fn.type === "TSDeclareMethod") {
|
|
1839
|
+
return formalParamDisplayNames(formalParamsFromNodes(fn.params));
|
|
1758
1840
|
}
|
|
1759
1841
|
if (fn.type === "VariableDeclaration") {
|
|
1760
1842
|
const decl = fn.declarations[0];
|
|
1761
1843
|
if (decl.init?.type === "FunctionExpression" || decl.init?.type === "ArrowFunctionExpression") {
|
|
1762
|
-
return decl.init.params
|
|
1763
|
-
if (p.type === "Identifier") return p.name;
|
|
1764
|
-
if (p.type === "AssignmentPattern" && p.left.type === "Identifier") return p.left.name;
|
|
1765
|
-
if (p.type === "RestElement" && p.argument.type === "Identifier") return `...${p.argument.name}`;
|
|
1766
|
-
return "_";
|
|
1767
|
-
});
|
|
1844
|
+
return formalParamDisplayNames(formalParamsFromNodes(decl.init.params));
|
|
1768
1845
|
}
|
|
1769
1846
|
}
|
|
1770
1847
|
return [];
|
|
@@ -1828,12 +1905,103 @@ function assignmentChainName(expr) {
|
|
|
1828
1905
|
function collectTopLevelFunctions(ast) {
|
|
1829
1906
|
const results = [];
|
|
1830
1907
|
if (ast.type !== "File") return results;
|
|
1831
|
-
|
|
1908
|
+
const body = ast.program.body;
|
|
1909
|
+
const classDecls = /* @__PURE__ */ new Map();
|
|
1910
|
+
const exportedClassNames = /* @__PURE__ */ new Set();
|
|
1911
|
+
const collectClassMethods = (cname, decl, stmt) => {
|
|
1912
|
+
const members = decl?.body?.body ?? [];
|
|
1913
|
+
for (const m of members) {
|
|
1914
|
+
const mem = m;
|
|
1915
|
+
const isMethod = mem.type === "MethodDefinition" || mem.type === "ClassMethod" || mem.type === "TSDeclareMethod";
|
|
1916
|
+
if (!isMethod) continue;
|
|
1917
|
+
if (mem.kind && mem.kind !== "method") continue;
|
|
1918
|
+
if (mem.static) continue;
|
|
1919
|
+
const keyName = mem.key?.type === "Identifier" ? mem.key.name : void 0;
|
|
1920
|
+
if (!keyName) continue;
|
|
1921
|
+
const methodNode = mem.type === "MethodDefinition" ? mem.value : mem;
|
|
1922
|
+
if (!methodNode) continue;
|
|
1923
|
+
results.push({
|
|
1924
|
+
name: `${cname}.${keyName}`,
|
|
1925
|
+
node: methodNode,
|
|
1926
|
+
stmt,
|
|
1927
|
+
noDeclaration: true
|
|
1928
|
+
});
|
|
1929
|
+
}
|
|
1930
|
+
};
|
|
1931
|
+
for (const stmt of body) {
|
|
1932
|
+
if (stmt.type === "ClassDeclaration" && stmt.id?.name) {
|
|
1933
|
+
const n = stmt.id.name;
|
|
1934
|
+
classDecls.set(n, { node: stmt, stmt });
|
|
1935
|
+
continue;
|
|
1936
|
+
}
|
|
1937
|
+
if (stmt.type === "ExportNamedDeclaration") {
|
|
1938
|
+
const d = stmt.declaration;
|
|
1939
|
+
if (d?.type === "ClassDeclaration" && d.id?.name) {
|
|
1940
|
+
const n = d.id.name;
|
|
1941
|
+
classDecls.set(n, { node: d, stmt });
|
|
1942
|
+
exportedClassNames.add(n);
|
|
1943
|
+
continue;
|
|
1944
|
+
}
|
|
1945
|
+
if (!d && stmt.specifiers) {
|
|
1946
|
+
for (const spec of stmt.specifiers) {
|
|
1947
|
+
if (spec?.type !== "ExportSpecifier") continue;
|
|
1948
|
+
const local = spec.local?.name;
|
|
1949
|
+
const exported = spec.exported?.name ?? spec.exported?.value;
|
|
1950
|
+
if (!local) continue;
|
|
1951
|
+
if (classDecls.has(local) || body.some(
|
|
1952
|
+
(s) => s.type === "ClassDeclaration" && s.id?.name === local
|
|
1953
|
+
)) {
|
|
1954
|
+
exportedClassNames.add(local);
|
|
1955
|
+
}
|
|
1956
|
+
if (exported === "default") exportedClassNames.add(local);
|
|
1957
|
+
}
|
|
1958
|
+
continue;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
if (stmt.type === "ExportDefaultDeclaration") {
|
|
1962
|
+
const d = stmt.declaration;
|
|
1963
|
+
if (d?.type === "ClassDeclaration" && d.id?.name) {
|
|
1964
|
+
const n = d.id.name;
|
|
1965
|
+
classDecls.set(n, { node: d, stmt });
|
|
1966
|
+
exportedClassNames.add(n);
|
|
1967
|
+
} else if (d?.type === "Identifier" && typeof d.name === "string") {
|
|
1968
|
+
exportedClassNames.add(d.name);
|
|
1969
|
+
}
|
|
1970
|
+
continue;
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
for (const stmt of body) {
|
|
1832
1974
|
const decl = resolveFunctionNode(stmt);
|
|
1833
1975
|
if (decl.type === "FunctionDeclaration" && decl.id) {
|
|
1834
1976
|
results.push({ name: decl.id.name, node: decl, stmt, noDeclaration: false });
|
|
1835
1977
|
continue;
|
|
1836
1978
|
}
|
|
1979
|
+
if (decl.type === "ClassDeclaration" && decl.id?.name) {
|
|
1980
|
+
continue;
|
|
1981
|
+
}
|
|
1982
|
+
if (stmt.type === "ExportNamedDeclaration") {
|
|
1983
|
+
const d = resolveFunctionNode(stmt);
|
|
1984
|
+
if (d.type === "FunctionDeclaration" && d.id) {
|
|
1985
|
+
results.push({ name: d.id.name, node: d, stmt, noDeclaration: false });
|
|
1986
|
+
}
|
|
1987
|
+
if (d.type === "ClassDeclaration" && d.id?.name) {
|
|
1988
|
+
}
|
|
1989
|
+
continue;
|
|
1990
|
+
}
|
|
1991
|
+
if (stmt.type === "ExportDefaultDeclaration") {
|
|
1992
|
+
const d = stmt.declaration;
|
|
1993
|
+
if (d?.type === "FunctionDeclaration" && d.id) {
|
|
1994
|
+
results.push({ name: d.id.name, node: d, stmt, noDeclaration: false });
|
|
1995
|
+
} else if (d && (isFnExprValue(d) || d.type === "FunctionDeclaration" && !d.id)) {
|
|
1996
|
+
results.push({
|
|
1997
|
+
name: "default",
|
|
1998
|
+
node: d,
|
|
1999
|
+
stmt,
|
|
2000
|
+
noDeclaration: true
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
2003
|
+
continue;
|
|
2004
|
+
}
|
|
1837
2005
|
if (stmt.type === "VariableDeclaration") {
|
|
1838
2006
|
for (const declarator of stmt.declarations) {
|
|
1839
2007
|
if (declarator.id?.type === "Identifier" && isFnExprValue(declarator.init)) {
|
|
@@ -1851,6 +2019,11 @@ function collectTopLevelFunctions(ast) {
|
|
|
1851
2019
|
}
|
|
1852
2020
|
}
|
|
1853
2021
|
}
|
|
2022
|
+
for (const cname of exportedClassNames) {
|
|
2023
|
+
const hit = classDecls.get(cname);
|
|
2024
|
+
if (!hit) continue;
|
|
2025
|
+
collectClassMethods(cname, hit.node, hit.stmt);
|
|
2026
|
+
}
|
|
1854
2027
|
return results;
|
|
1855
2028
|
}
|
|
1856
2029
|
function findSingleModuleExportsFunction(ast) {
|
|
@@ -1877,7 +2050,7 @@ function findSingleModuleExportsFunction(ast) {
|
|
|
1877
2050
|
}
|
|
1878
2051
|
return found;
|
|
1879
2052
|
}
|
|
1880
|
-
var
|
|
2053
|
+
var DEFAULT_CALLSITE_BUDGET = 3;
|
|
1881
2054
|
var COLLAPSE_LITERAL_THRESHOLD = 4;
|
|
1882
2055
|
function dedupeCallRecords(records) {
|
|
1883
2056
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -1895,7 +2068,7 @@ function locFromCallLoc(loc) {
|
|
|
1895
2068
|
const p = loc ?? { line: 0, column: 0 };
|
|
1896
2069
|
return { start: { line: p.line, column: p.column }, end: { line: p.line, column: p.column } };
|
|
1897
2070
|
}
|
|
1898
|
-
function synthesizeExternalFunctions(records, currentFile) {
|
|
2071
|
+
function synthesizeExternalFunctions(records, currentFile, callSiteBudget = DEFAULT_CALLSITE_BUDGET) {
|
|
1899
2072
|
const groups = /* @__PURE__ */ new Map();
|
|
1900
2073
|
for (const rec of records) {
|
|
1901
2074
|
if (!rec.targetModule || !rec.targetExport) continue;
|
|
@@ -1919,7 +2092,7 @@ function synthesizeExternalFunctions(records, currentFile) {
|
|
|
1919
2092
|
cases: [],
|
|
1920
2093
|
fromModule: module
|
|
1921
2094
|
};
|
|
1922
|
-
const precise = deduped.slice(0,
|
|
2095
|
+
const precise = deduped.slice(0, callSiteBudget);
|
|
1923
2096
|
for (const rec of precise) {
|
|
1924
2097
|
analysis.cases.push({
|
|
1925
2098
|
name: `call@L${rec.callLoc?.line ?? 0}`,
|
|
@@ -1929,7 +2102,7 @@ function synthesizeExternalFunctions(records, currentFile) {
|
|
|
1929
2102
|
source: "callsite"
|
|
1930
2103
|
});
|
|
1931
2104
|
}
|
|
1932
|
-
const remaining = deduped.slice(
|
|
2105
|
+
const remaining = deduped.slice(callSiteBudget);
|
|
1933
2106
|
if (remaining.length > 0) {
|
|
1934
2107
|
const symArgsAbs = Array.from(
|
|
1935
2108
|
{ length: arity },
|
|
@@ -1970,16 +2143,17 @@ function collectEnvNames(filePath, source, includeProject) {
|
|
|
1970
2143
|
const fileDirectives = extractFileDirectives(ast);
|
|
1971
2144
|
const fileEnvNames = fileDirectives.filter((d) => d.kind === "env").flatMap((d) => d.envs);
|
|
1972
2145
|
if (!includeProject) return fileEnvNames;
|
|
1973
|
-
const projectConfig = findProjectConfig(
|
|
2146
|
+
const projectConfig = findProjectConfig(dirname7(filePath));
|
|
1974
2147
|
const projectEnvNames = projectConfig?.config.env ?? [];
|
|
1975
2148
|
return [.../* @__PURE__ */ new Set([...projectEnvNames, ...fileEnvNames])];
|
|
1976
2149
|
}
|
|
1977
|
-
async function analyzeFileAsync(filePath, source, activeCases, externalCallRecords) {
|
|
2150
|
+
async function analyzeFileAsync(filePath, source, activeCases, externalCallRecords, loadModule) {
|
|
1978
2151
|
const envNames = collectEnvNames(filePath, source, true);
|
|
2152
|
+
noteEnvPathDeps(filePath, source);
|
|
1979
2153
|
if (envNames.length > 0) {
|
|
1980
|
-
await preloadPathEnvs(envNames,
|
|
2154
|
+
await preloadPathEnvs(envNames, dirname7(filePath));
|
|
1981
2155
|
}
|
|
1982
|
-
return analyzeFile(filePath, source, activeCases, externalCallRecords);
|
|
2156
|
+
return analyzeFile(filePath, source, activeCases, externalCallRecords, loadModule);
|
|
1983
2157
|
}
|
|
1984
2158
|
function collectCallRecords(filePath, source) {
|
|
1985
2159
|
if (/\brequire\s*\(/.test(source) && filePath) {
|
|
@@ -2050,7 +2224,7 @@ function collectCallRecords(filePath, source) {
|
|
|
2050
2224
|
var TEST_CALLBACK_NAMES = /* @__PURE__ */ new Set(["it", "test", "describe"]);
|
|
2051
2225
|
var externalRecordIds = /* @__PURE__ */ new WeakMap();
|
|
2052
2226
|
var nextExternalRecordId = 1;
|
|
2053
|
-
function analysisFileCacheKey(filePath, source, activeCases, externalCallRecords) {
|
|
2227
|
+
function analysisFileCacheKey(filePath, source, activeCases, externalCallRecords, analysisCfg, loadModule, projectEnvNames, autoBind) {
|
|
2054
2228
|
let cases = "-";
|
|
2055
2229
|
if (activeCases && activeCases.size > 0) {
|
|
2056
2230
|
cases = [...activeCases.entries()].sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0).map(([k, v]) => `${k}=${v}`).join(",");
|
|
@@ -2064,11 +2238,31 @@ function analysisFileCacheKey(filePath, source, activeCases, externalCallRecords
|
|
|
2064
2238
|
}
|
|
2065
2239
|
ext = `n${externalCallRecords.length}#id${id}`;
|
|
2066
2240
|
}
|
|
2241
|
+
const cfg = analysisCfg ? `m=${analysisCfg.mode}|e=${analysisCfg.evalMissingSlot}|b=${analysisCfg.callSiteBudget}|d=${analysisCfg.diagnostics}` : "-";
|
|
2242
|
+
const lm = loadModule !== void 0 && loadModule !== defaultLoadModule ? "lm1" : "lm0";
|
|
2243
|
+
const envSeg = projectEnvNames && projectEnvNames.length > 0 ? projectEnvNames.join(",") : "-";
|
|
2244
|
+
const abSeg = autoBind === false ? "ab0" : autoBind === true ? "ab1" : "ab?";
|
|
2245
|
+
const effectiveLoader = loadModule ?? defaultLoadModule;
|
|
2246
|
+
let depSeg = "-";
|
|
2247
|
+
let noCache = false;
|
|
2248
|
+
try {
|
|
2249
|
+
const fp = loadModuleDepsFingerprint2(source, effectiveLoader, filePath);
|
|
2250
|
+
if (fp.truncated) {
|
|
2251
|
+
noCache = true;
|
|
2252
|
+
depSeg = `trunc:${fp.paths.length}`;
|
|
2253
|
+
} else {
|
|
2254
|
+
depSeg = hashSource2(fp.fp);
|
|
2255
|
+
}
|
|
2256
|
+
} catch {
|
|
2257
|
+
noCache = true;
|
|
2258
|
+
depSeg = "fperr";
|
|
2259
|
+
}
|
|
2067
2260
|
return {
|
|
2068
2261
|
filePath,
|
|
2069
2262
|
// 尾部无 @nudo 注释/空行不进键:comment-only 编辑命中 AnalysisResult
|
|
2070
2263
|
source: stableAnalyzeKeySource2(source),
|
|
2071
|
-
auxKey: `${cases}\0${ext}
|
|
2264
|
+
auxKey: `${cases}\0${ext}\0${cfg}\0${lm}\0${envSeg}\0${abSeg}\0${depSeg}`,
|
|
2265
|
+
noCache
|
|
2072
2266
|
};
|
|
2073
2267
|
}
|
|
2074
2268
|
function cloneAnalysisResult(r) {
|
|
@@ -2086,12 +2280,20 @@ function cloneFunctionAnalysis(a) {
|
|
|
2086
2280
|
return {
|
|
2087
2281
|
...a,
|
|
2088
2282
|
paramNames: [...a.paramNames],
|
|
2283
|
+
...a.formals ? { formals: a.formals.map((f) => ({ ...f })) } : {},
|
|
2089
2284
|
cases: a.cases.map((c) => ({
|
|
2090
2285
|
...c,
|
|
2091
2286
|
argAbs: [...c.argAbs],
|
|
2092
2287
|
...c.intension ? { intension: { ...c.intension } } : {}
|
|
2093
2288
|
})),
|
|
2094
|
-
loc: { start: { ...a.loc.start }, end: { ...a.loc.end } }
|
|
2289
|
+
loc: { start: { ...a.loc.start }, end: { ...a.loc.end } },
|
|
2290
|
+
...a.hof ? {
|
|
2291
|
+
hof: {
|
|
2292
|
+
...a.hof.fnRels ? { fnRels: a.hof.fnRels.map((r) => ({ ...r })) } : {},
|
|
2293
|
+
...a.hof.entryShapes ? { entryShapes: a.hof.entryShapes.map((s) => ({ ...s })) } : {},
|
|
2294
|
+
...a.hof.symbolic ? { symbolic: a.hof.symbolic } : {}
|
|
2295
|
+
}
|
|
2296
|
+
} : {}
|
|
2095
2297
|
};
|
|
2096
2298
|
}
|
|
2097
2299
|
function shiftSourceLoc(loc, lineDelta) {
|
|
@@ -2115,17 +2317,42 @@ function shiftCallRecordLines(r, lineDelta) {
|
|
|
2115
2317
|
callLoc: { line: r.callLoc.line + lineDelta, column: r.callLoc.column }
|
|
2116
2318
|
};
|
|
2117
2319
|
}
|
|
2118
|
-
function analyzeFile(filePath, source, activeCases, externalCallRecords) {
|
|
2119
|
-
const
|
|
2120
|
-
const
|
|
2121
|
-
|
|
2122
|
-
|
|
2320
|
+
function analyzeFile(filePath, source, activeCases, externalCallRecords, loadModule) {
|
|
2321
|
+
const projectConfig = findProjectConfig(dirname7(filePath));
|
|
2322
|
+
const cfg = analysisConfig(projectConfig?.config);
|
|
2323
|
+
const projectEnvNames = projectConfig?.config.env ?? [];
|
|
2324
|
+
const autoBind = interfaceConfig(projectConfig?.config).autoBind;
|
|
2325
|
+
const k = analysisFileCacheKey(
|
|
2326
|
+
filePath,
|
|
2327
|
+
source,
|
|
2328
|
+
activeCases,
|
|
2329
|
+
externalCallRecords,
|
|
2330
|
+
cfg,
|
|
2331
|
+
loadModule,
|
|
2332
|
+
projectEnvNames,
|
|
2333
|
+
autoBind !== false
|
|
2334
|
+
);
|
|
2335
|
+
if (!k.noCache) {
|
|
2336
|
+
const hit = analysisCacheGet(k.filePath, k.source, k.auxKey);
|
|
2337
|
+
if (hit !== void 0) {
|
|
2338
|
+
return cloneAnalysisResult(hit);
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
const result = analyzeFileUncached(filePath, source, activeCases, externalCallRecords, loadModule);
|
|
2342
|
+
if (!k.noCache) {
|
|
2343
|
+
analysisCacheSet(k.filePath, k.source, k.auxKey, result);
|
|
2123
2344
|
}
|
|
2124
|
-
const result = analyzeFileUncached(filePath, source, activeCases, externalCallRecords);
|
|
2125
|
-
analysisCacheSet(k.filePath, k.source, k.auxKey, result);
|
|
2126
2345
|
return cloneAnalysisResult(result);
|
|
2127
2346
|
}
|
|
2128
|
-
function analyzeFileUncached(filePath, source, activeCases, externalCallRecords) {
|
|
2347
|
+
function analyzeFileUncached(filePath, source, activeCases, externalCallRecords, loadModule) {
|
|
2348
|
+
const projectConfig = findProjectConfig(dirname7(filePath));
|
|
2349
|
+
const missingSlotOn = analysisConfig(projectConfig?.config).evalMissingSlot === "warning";
|
|
2350
|
+
return runWithEvalMissingSlot(
|
|
2351
|
+
missingSlotOn,
|
|
2352
|
+
() => analyzeFileUncachedInner(filePath, source, activeCases, externalCallRecords, loadModule)
|
|
2353
|
+
);
|
|
2354
|
+
}
|
|
2355
|
+
function analyzeFileUncachedInner(filePath, source, activeCases, externalCallRecords, loadModule) {
|
|
2129
2356
|
const ast = parse5(source);
|
|
2130
2357
|
const functions = extractDirectives(ast);
|
|
2131
2358
|
const diagnostics = [];
|
|
@@ -2135,18 +2362,33 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2135
2362
|
const caseHints = [];
|
|
2136
2363
|
const fileDirectives = extractFileDirectives(ast);
|
|
2137
2364
|
const fileEnvNames = fileDirectives.filter((d) => d.kind === "env").flatMap((d) => d.envs);
|
|
2138
|
-
const projectConfig = findProjectConfig(
|
|
2365
|
+
const projectConfig = findProjectConfig(dirname7(filePath));
|
|
2139
2366
|
const projectEnvNames = projectConfig?.config.env ?? [];
|
|
2140
2367
|
const envNames = [.../* @__PURE__ */ new Set([...projectEnvNames, ...fileEnvNames])];
|
|
2368
|
+
const analysisCfg = analysisConfig(projectConfig?.config);
|
|
2369
|
+
const callSiteBudget = analysisCfg.callSiteBudget;
|
|
2141
2370
|
const callRecords = [];
|
|
2142
2371
|
const globalEnv = createEnvironment2();
|
|
2143
2372
|
const bMemberDiagNames = /* @__PURE__ */ new Set();
|
|
2144
2373
|
const bMemberDiagSeen = /* @__PURE__ */ new Set();
|
|
2145
2374
|
const pushBMemberDiag = (d, fallbackLine) => {
|
|
2146
2375
|
bMemberDiagNames.add(d.name);
|
|
2147
|
-
const key = `${d.kind}:${d.name}:${d.receiver}:${d.line ?? fallbackLine}:${d.column ?? 0}`;
|
|
2376
|
+
const key = `${d.kind}:${d.name}:${d.receiver}:${d.line ?? fallbackLine}:${d.column ?? 0}:${d.code ?? ""}`;
|
|
2148
2377
|
if (bMemberDiagSeen.has(key)) return;
|
|
2149
2378
|
bMemberDiagSeen.add(key);
|
|
2379
|
+
if (d.code === "nudo:missing-slot") {
|
|
2380
|
+
diagnostics.push({
|
|
2381
|
+
range: {
|
|
2382
|
+
start: { line: d.line ?? fallbackLine, column: d.column ?? 0 },
|
|
2383
|
+
end: { line: d.line ?? fallbackLine, column: (d.column ?? 0) + d.name.length }
|
|
2384
|
+
},
|
|
2385
|
+
severity: "warning",
|
|
2386
|
+
message: `Field '${d.name}' is missing on the evaluated object shape`,
|
|
2387
|
+
code: "nudo:missing-slot",
|
|
2388
|
+
...d.origin ? { origin: d.origin } : {}
|
|
2389
|
+
});
|
|
2390
|
+
return;
|
|
2391
|
+
}
|
|
2150
2392
|
if (d.receiver === "unknown") {
|
|
2151
2393
|
diagnostics.push({
|
|
2152
2394
|
range: {
|
|
@@ -2204,7 +2446,8 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2204
2446
|
try {
|
|
2205
2447
|
const g = evalAbsModuleGraph(source, filePath, {
|
|
2206
2448
|
seedVars: seeds.seedVars,
|
|
2207
|
-
seedFns: seeds.seedFns
|
|
2449
|
+
seedFns: seeds.seedFns,
|
|
2450
|
+
...loadModule ? { loadModule } : {}
|
|
2208
2451
|
});
|
|
2209
2452
|
absGraphModules = { ...collectEnvModules(envNames), ...g.modules };
|
|
2210
2453
|
pushBModuleIssues(g.issues);
|
|
@@ -2235,7 +2478,7 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2235
2478
|
if (selfContained || canAbsModules) {
|
|
2236
2479
|
setAbsTruncationCollector2((label) => bTruncatedFns.add(label));
|
|
2237
2480
|
try {
|
|
2238
|
-
absCallRecords = collectAbsCallRecords(source, seeds, filePath, absGraphModules, envNames);
|
|
2481
|
+
absCallRecords = collectAbsCallRecords(source, seeds, filePath, absGraphModules, envNames, loadModule);
|
|
2239
2482
|
} finally {
|
|
2240
2483
|
setAbsTruncationCollector2(null);
|
|
2241
2484
|
}
|
|
@@ -2257,7 +2500,10 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2257
2500
|
} catch {
|
|
2258
2501
|
}
|
|
2259
2502
|
}
|
|
2260
|
-
if (
|
|
2503
|
+
if (bHostedEval && bTopCallRecords.length > 0) {
|
|
2504
|
+
callRecords.length = 0;
|
|
2505
|
+
callRecords.push(...bTopCallRecords);
|
|
2506
|
+
} else if ((selfContained || canAbsModules) && absCallRecords.length > 0) {
|
|
2261
2507
|
callRecords.length = 0;
|
|
2262
2508
|
callRecords.push(...absCallRecords);
|
|
2263
2509
|
} else if (bTopCallRecords.length > 0) {
|
|
@@ -2345,7 +2591,16 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2345
2591
|
const skipDirective = fn.directives.find((d) => d.kind === "skip");
|
|
2346
2592
|
const fnLoc = locFromNode(fn.node);
|
|
2347
2593
|
const paramNames = extractParamNames(fn.node);
|
|
2348
|
-
const
|
|
2594
|
+
const formals = formalParamsFromNodes(
|
|
2595
|
+
fn.node.params ?? []
|
|
2596
|
+
);
|
|
2597
|
+
const analysis = {
|
|
2598
|
+
name: fn.name,
|
|
2599
|
+
loc: fnLoc,
|
|
2600
|
+
paramNames,
|
|
2601
|
+
...formals.length > 0 ? { formals } : {},
|
|
2602
|
+
cases: []
|
|
2603
|
+
};
|
|
2349
2604
|
if (skipDirective && skipDirective.kind === "skip") {
|
|
2350
2605
|
analysis.skipped = true;
|
|
2351
2606
|
if (skipDirective.returns) {
|
|
@@ -2357,14 +2612,31 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2357
2612
|
const caseDirectives = fn.directives.filter((d) => d.kind === "case");
|
|
2358
2613
|
const activeCaseIdx = activeCases?.get(fn.name) ?? 0;
|
|
2359
2614
|
const fp = fnFpMap?.get(fn.name);
|
|
2360
|
-
const
|
|
2615
|
+
const analysisFnKey = `m=${analysisCfg.mode}|e=${analysisCfg.evalMissingSlot}|b=${analysisCfg.callSiteBudget}`;
|
|
2616
|
+
let fnDepSeg = "-";
|
|
2617
|
+
let fnDepFailClosed = false;
|
|
2618
|
+
try {
|
|
2619
|
+
const dfp = loadModuleDepsFingerprint2(source, loadModule ?? defaultLoadModule, filePath);
|
|
2620
|
+
if (dfp.truncated) {
|
|
2621
|
+
fnDepFailClosed = true;
|
|
2622
|
+
fnDepSeg = null;
|
|
2623
|
+
} else {
|
|
2624
|
+
fnDepSeg = hashSource2(dfp.fp);
|
|
2625
|
+
}
|
|
2626
|
+
} catch {
|
|
2627
|
+
fnDepFailClosed = true;
|
|
2628
|
+
fnDepSeg = null;
|
|
2629
|
+
}
|
|
2630
|
+
const fnCacheKey = !fnDepFailClosed && fp && caseDirectives.length > 0 ? [
|
|
2361
2631
|
filePath,
|
|
2362
2632
|
fp.own,
|
|
2363
2633
|
fp.deps,
|
|
2364
2634
|
String(activeCaseIdx),
|
|
2365
2635
|
caseDirectiveKey(caseDirectives, formatAbs4),
|
|
2366
2636
|
envKeyFn,
|
|
2367
|
-
mockKeyFn
|
|
2637
|
+
mockKeyFn,
|
|
2638
|
+
analysisFnKey,
|
|
2639
|
+
fnDepSeg ?? "-"
|
|
2368
2640
|
].join("\0") : void 0;
|
|
2369
2641
|
const dLen0 = diagnostics.length;
|
|
2370
2642
|
const hLen0 = caseHints.length;
|
|
@@ -2538,6 +2810,7 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2538
2810
|
COLLAPSE_LITERAL_THRESHOLD
|
|
2539
2811
|
);
|
|
2540
2812
|
}
|
|
2813
|
+
attachHofSnapshot(analysis, source);
|
|
2541
2814
|
if (fnCacheKey) {
|
|
2542
2815
|
fnAnalysisCacheSet(fnCacheKey, {
|
|
2543
2816
|
analysis: cloneFunctionAnalysis(analysis),
|
|
@@ -2571,7 +2844,7 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2571
2844
|
}
|
|
2572
2845
|
return n;
|
|
2573
2846
|
};
|
|
2574
|
-
const currentModulePath = normalizeModulePath(
|
|
2847
|
+
const currentModulePath = normalizeModulePath(resolve6(filePath));
|
|
2575
2848
|
const singleExportFn = findSingleModuleExportsFunction(ast);
|
|
2576
2849
|
const reportInjectedDomainExceeds = (name, node, fallbackLoc) => {
|
|
2577
2850
|
if (!externalCallRecords || externalCallRecords.length === 0) return;
|
|
@@ -2586,10 +2859,10 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2586
2859
|
if (injected.length === 0) return;
|
|
2587
2860
|
const nameLoc = fnNameLoc(node, fallbackLoc);
|
|
2588
2861
|
const fnNode = resolveFunctionNode(node);
|
|
2589
|
-
const autoBind = interfaceConfig(findProjectConfig(
|
|
2862
|
+
const autoBind = interfaceConfig(findProjectConfig(dirname7(filePath))?.config).autoBind;
|
|
2590
2863
|
const domainIssues = checkInjectedDomainEvidence(name, source, injected, {
|
|
2591
2864
|
paramNames: extractParamNames(fnNode),
|
|
2592
|
-
loadModule: defaultLoadModule,
|
|
2865
|
+
loadModule: loadModule ?? defaultLoadModule,
|
|
2593
2866
|
fromFile: filePath,
|
|
2594
2867
|
loc: { line: nameLoc.start.line, column: nameLoc.start.column },
|
|
2595
2868
|
...autoBind === false ? { autoBind: false } : {}
|
|
@@ -2639,7 +2912,7 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2639
2912
|
return 0;
|
|
2640
2913
|
};
|
|
2641
2914
|
const ordered = records.map((r, i) => ({ r, i })).sort((a, b) => informativeness(a.r) - informativeness(b.r) || a.i - b.i).map(({ r }) => r);
|
|
2642
|
-
const precise = ordered.slice(0,
|
|
2915
|
+
const precise = ordered.slice(0, callSiteBudget);
|
|
2643
2916
|
for (const rec of precise) {
|
|
2644
2917
|
let absRaw;
|
|
2645
2918
|
let absResult;
|
|
@@ -2666,7 +2939,7 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2666
2939
|
if (absRaw) attachAbsToIntension(caseResult2, absRaw, candidate.name);
|
|
2667
2940
|
candidate.analysis.cases.push(caseResult2);
|
|
2668
2941
|
}
|
|
2669
|
-
const remaining = ordered.slice(
|
|
2942
|
+
const remaining = ordered.slice(callSiteBudget).filter((rec) => !rec.argAbs.some((a) => a.shape.k === "unknown" && !a.term));
|
|
2670
2943
|
if (remaining.length > 0) {
|
|
2671
2944
|
const fnNode2 = resolveFunctionNode(candidate.node);
|
|
2672
2945
|
const paramCount = extractParamNames(fnNode2).length;
|
|
@@ -2707,7 +2980,8 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2707
2980
|
const symCase = {
|
|
2708
2981
|
name: "call@symbolic",
|
|
2709
2982
|
argAbs: widenedArgsAbs,
|
|
2710
|
-
|
|
2983
|
+
// B4:超预算聚合必须 #widened(可解释降级)
|
|
2984
|
+
abs: symAbs ? { ...symAbs, conf: symAbs.conf === "exact" ? "widened" : symAbs.conf } : absUnknown2,
|
|
2711
2985
|
throwsAbs: neverAbs,
|
|
2712
2986
|
source: "callsite",
|
|
2713
2987
|
aggregatedFrom: remaining.length
|
|
@@ -2771,11 +3045,16 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2771
3045
|
candidate.analysis.cases.push(caseResult);
|
|
2772
3046
|
candidate.analysis.entryOnly = true;
|
|
2773
3047
|
candidate.analysis.combinedAbs = entryAbs;
|
|
3048
|
+
attachHofSnapshot(candidate.analysis, source);
|
|
2774
3049
|
}
|
|
2775
3050
|
if (!bHostedEval) {
|
|
2776
3051
|
buildNodeTypeMap(ast, globalEnv, nodeAbsMap);
|
|
2777
3052
|
}
|
|
2778
|
-
const externalFunctions = synthesizeExternalFunctions(
|
|
3053
|
+
const externalFunctions = synthesizeExternalFunctions(
|
|
3054
|
+
callRecords,
|
|
3055
|
+
filePath,
|
|
3056
|
+
analysisConfig(projectConfig?.config).callSiteBudget
|
|
3057
|
+
);
|
|
2779
3058
|
return {
|
|
2780
3059
|
functions: functionResults,
|
|
2781
3060
|
diagnostics,
|
|
@@ -2870,7 +3149,7 @@ function absModulesOk(source, envNames) {
|
|
|
2870
3149
|
void envNames;
|
|
2871
3150
|
return true;
|
|
2872
3151
|
}
|
|
2873
|
-
function collectAbsCallRecords(source, seeds, filePath, precomputedModules, envNames = []) {
|
|
3152
|
+
function collectAbsCallRecords(source, seeds, filePath, precomputedModules, envNames = [], loadModule) {
|
|
2874
3153
|
const absCalls = [];
|
|
2875
3154
|
let modules = precomputedModules;
|
|
2876
3155
|
let importLocals = /* @__PURE__ */ new Map();
|
|
@@ -2879,7 +3158,8 @@ function collectAbsCallRecords(source, seeds, filePath, precomputedModules, envN
|
|
|
2879
3158
|
try {
|
|
2880
3159
|
const graph = evalAbsModuleGraph(source, filePath, {
|
|
2881
3160
|
seedVars: seeds?.seedVars,
|
|
2882
|
-
seedFns: seeds?.seedFns
|
|
3161
|
+
seedFns: seeds?.seedFns,
|
|
3162
|
+
...loadModule ? { loadModule } : {}
|
|
2883
3163
|
});
|
|
2884
3164
|
modules = graph.modules;
|
|
2885
3165
|
} catch {
|
|
@@ -2986,10 +3266,10 @@ function buildAbsImportLocalMap(source, fromFile) {
|
|
|
2986
3266
|
return out;
|
|
2987
3267
|
}
|
|
2988
3268
|
function resolveImportAbs(spec, fromFile) {
|
|
2989
|
-
const base =
|
|
2990
|
-
const p =
|
|
2991
|
-
for (const cand of [p, `${p}.js`, `${p}.mjs`, `${p}.ts`,
|
|
2992
|
-
if (
|
|
3269
|
+
const base = dirname7(resolve6(fromFile));
|
|
3270
|
+
const p = resolve6(base, spec);
|
|
3271
|
+
for (const cand of [p, `${p}.js`, `${p}.mjs`, `${p}.ts`, resolve6(p, "index.js")]) {
|
|
3272
|
+
if (existsSync5(cand) && !statSync5(cand).isDirectory()) return cand;
|
|
2993
3273
|
}
|
|
2994
3274
|
return null;
|
|
2995
3275
|
}
|
|
@@ -3063,6 +3343,22 @@ function tryAttachIntension(caseResult, source, fnName) {
|
|
|
3063
3343
|
} catch {
|
|
3064
3344
|
}
|
|
3065
3345
|
}
|
|
3346
|
+
function attachHofSnapshot(analysis, source) {
|
|
3347
|
+
if (analysis.hof) return;
|
|
3348
|
+
try {
|
|
3349
|
+
const g = generalizeFromAst2(analysis.name, source);
|
|
3350
|
+
if (!g) return;
|
|
3351
|
+
const fnRels = g.fnRels ? [...g.fnRels.entries()].map(([param, rec]) => ({ param, abs: rec.abs })) : void 0;
|
|
3352
|
+
const entryShapes = g.entryShapes ? [...g.entryShapes.entries()].map(([param, rec]) => ({ param, abs: rec.abs })) : void 0;
|
|
3353
|
+
if (!fnRels?.length && !entryShapes?.length) return;
|
|
3354
|
+
analysis.hof = {
|
|
3355
|
+
...fnRels?.length ? { fnRels } : {},
|
|
3356
|
+
...entryShapes?.length ? { entryShapes } : {},
|
|
3357
|
+
symbolic: g.symbolic
|
|
3358
|
+
};
|
|
3359
|
+
} catch {
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3066
3362
|
function attachAbsToIntension(caseResult, absVal, label) {
|
|
3067
3363
|
const prev = caseResult.intension ?? {};
|
|
3068
3364
|
caseResult.abs = absVal;
|
|
@@ -3074,10 +3370,27 @@ function attachAbsToIntension(caseResult, absVal, label) {
|
|
|
3074
3370
|
};
|
|
3075
3371
|
}
|
|
3076
3372
|
function safeAbsOrUnknown(a) {
|
|
3077
|
-
if (
|
|
3078
|
-
return
|
|
3373
|
+
if (a && typeof a === "object" && "shape" in a && "conf" in a) {
|
|
3374
|
+
return a;
|
|
3375
|
+
}
|
|
3376
|
+
if (typeof a === "function") {
|
|
3377
|
+
const fn = a;
|
|
3378
|
+
const n = Math.max(0, fn.length);
|
|
3379
|
+
const params = Array.from({ length: n }, (_, i) => `arg${i}`);
|
|
3380
|
+
return absFunction4(params, {
|
|
3381
|
+
body: { type: "BlockStatement", body: [], directives: [] },
|
|
3382
|
+
apply: (args) => {
|
|
3383
|
+
try {
|
|
3384
|
+
const r = fn(...args);
|
|
3385
|
+
if (r && typeof r === "object" && "shape" in r) return r;
|
|
3386
|
+
return absUnknown2;
|
|
3387
|
+
} catch {
|
|
3388
|
+
return absUnknown2;
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
});
|
|
3079
3392
|
}
|
|
3080
|
-
return
|
|
3393
|
+
return absUnknown2;
|
|
3081
3394
|
}
|
|
3082
3395
|
function callRecordFromAbsCall(r, impMap) {
|
|
3083
3396
|
const argAbs = r.args.map(safeAbsOrUnknown);
|
|
@@ -3099,7 +3412,7 @@ function callRecordFromAbsCall(r, impMap) {
|
|
|
3099
3412
|
}
|
|
3100
3413
|
|
|
3101
3414
|
// src/lsp-surface.ts
|
|
3102
|
-
import { dirname as
|
|
3415
|
+
import { dirname as dirname8 } from "path";
|
|
3103
3416
|
import traverse2 from "@babel/traverse";
|
|
3104
3417
|
import {
|
|
3105
3418
|
generalizeFromAst as generalizeFromAst3,
|
|
@@ -3109,7 +3422,8 @@ import {
|
|
|
3109
3422
|
collectAbsNodeTypes,
|
|
3110
3423
|
findAbsAtPosition,
|
|
3111
3424
|
evalSource,
|
|
3112
|
-
setAbsNodeCollector as setAbsNodeCollector2
|
|
3425
|
+
setAbsNodeCollector as setAbsNodeCollector2,
|
|
3426
|
+
interfaceTierOf
|
|
3113
3427
|
} from "@nudojs/core";
|
|
3114
3428
|
import { parse as parse6, extractDirectives as extractDirectives2, extractFileDirectives as extractFileDirectives2 } from "@nudojs/parser";
|
|
3115
3429
|
function getCasesForFile(filePath, source) {
|
|
@@ -3123,14 +3437,14 @@ function getCasesForFile(filePath, source) {
|
|
|
3123
3437
|
async function getTypeAtPositionAsync(filePath, source, line, column, activeCases) {
|
|
3124
3438
|
const envNames = collectEnvNames(filePath, source, false);
|
|
3125
3439
|
if (envNames.length > 0) {
|
|
3126
|
-
await preloadPathEnvs(envNames,
|
|
3440
|
+
await preloadPathEnvs(envNames, dirname8(filePath));
|
|
3127
3441
|
}
|
|
3128
3442
|
return getTypeAtPosition(filePath, source, line, column, activeCases);
|
|
3129
3443
|
}
|
|
3130
3444
|
async function getAbsAtPositionAsync(filePath, source, line, column, activeCases) {
|
|
3131
3445
|
const envNames = collectEnvNames(filePath, source, false);
|
|
3132
3446
|
if (envNames.length > 0) {
|
|
3133
|
-
await preloadPathEnvs(envNames,
|
|
3447
|
+
await preloadPathEnvs(envNames, dirname8(filePath));
|
|
3134
3448
|
}
|
|
3135
3449
|
return getAbsAtPosition(filePath, source, line, column, activeCases);
|
|
3136
3450
|
}
|
|
@@ -3218,7 +3532,7 @@ function getAbsAtPosition(filePath, source, line, column, activeCases) {
|
|
|
3218
3532
|
function getTypeAtPosition(filePath, source, line, column, activeCases) {
|
|
3219
3533
|
return getAbsAtPosition(filePath, source, line, column, activeCases);
|
|
3220
3534
|
}
|
|
3221
|
-
function getHoverAtPosition(filePath, source, line, column, activeCases) {
|
|
3535
|
+
function getHoverAtPosition(filePath, source, line, column, activeCases, opts) {
|
|
3222
3536
|
let file;
|
|
3223
3537
|
try {
|
|
3224
3538
|
file = parse6(source);
|
|
@@ -3227,6 +3541,7 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
|
|
|
3227
3541
|
}
|
|
3228
3542
|
const envNames = collectEnvNames(filePath, source, false);
|
|
3229
3543
|
const fnName = findFunctionNameAtPosition(source, line, column, file);
|
|
3544
|
+
const tier = fnName !== void 0 ? interfaceTierOf(source, fnName, filePath, opts ?? {}) : void 0;
|
|
3230
3545
|
let gDisplay;
|
|
3231
3546
|
let gAbs;
|
|
3232
3547
|
let gMulti;
|
|
@@ -3241,18 +3556,26 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
|
|
|
3241
3556
|
} catch {
|
|
3242
3557
|
}
|
|
3243
3558
|
}
|
|
3559
|
+
const withTier = (info) => {
|
|
3560
|
+
if (!info || !tier) return info;
|
|
3561
|
+
return {
|
|
3562
|
+
...info,
|
|
3563
|
+
interfaceSource: tier.source,
|
|
3564
|
+
...tier.display !== void 0 ? { interfaceDisplay: tier.display } : {}
|
|
3565
|
+
};
|
|
3566
|
+
};
|
|
3244
3567
|
const attachIntension = (info) => {
|
|
3245
|
-
if (!gDisplay) return info;
|
|
3568
|
+
if (!gDisplay) return withTier(info);
|
|
3246
3569
|
if (!info) {
|
|
3247
|
-
return { typeText: gDisplay, intension: gDisplay, abs: gAbs, absMultiline: gMulti };
|
|
3570
|
+
return withTier({ typeText: gDisplay, intension: gDisplay, abs: gAbs, absMultiline: gMulti });
|
|
3248
3571
|
}
|
|
3249
|
-
return {
|
|
3572
|
+
return withTier({
|
|
3250
3573
|
...info,
|
|
3251
3574
|
intension: gDisplay,
|
|
3252
3575
|
// 外延侧已有更准 Abs 时保留;否则用 symbolic 兜底
|
|
3253
3576
|
abs: info.abs ?? gAbs,
|
|
3254
3577
|
absMultiline: info.absMultiline ?? gMulti
|
|
3255
|
-
};
|
|
3578
|
+
});
|
|
3256
3579
|
};
|
|
3257
3580
|
const insideCaseFn = positionInsideCaseFunction(
|
|
3258
3581
|
source,
|
|
@@ -3333,6 +3656,14 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
|
|
|
3333
3656
|
}
|
|
3334
3657
|
} catch {
|
|
3335
3658
|
}
|
|
3659
|
+
if (tier && gDisplay) {
|
|
3660
|
+
return withTier({
|
|
3661
|
+
typeText: gDisplay,
|
|
3662
|
+
intension: gDisplay,
|
|
3663
|
+
abs: gAbs,
|
|
3664
|
+
absMultiline: gMulti
|
|
3665
|
+
});
|
|
3666
|
+
}
|
|
3336
3667
|
return null;
|
|
3337
3668
|
}
|
|
3338
3669
|
function findIdentNameAtPosition(source, line, column, fileAst) {
|
|
@@ -3657,10 +3988,111 @@ function serializeInferJson(result, file) {
|
|
|
3657
3988
|
function isNudoTargetPath(path) {
|
|
3658
3989
|
const lower = path.toLowerCase();
|
|
3659
3990
|
if (lower.endsWith(".d.ts")) return false;
|
|
3660
|
-
if (lower.endsWith(".nudo.js") || lower.endsWith(".nudo.ts"))
|
|
3991
|
+
if (lower.endsWith(".nudo.js") || lower.endsWith(".nudo.mjs") || lower.endsWith(".nudo.ts") || lower.endsWith(".nudo.draft.js") || lower.endsWith(".nudo.draft.mjs") || lower.endsWith(".nudo.draft.ts")) {
|
|
3992
|
+
return false;
|
|
3993
|
+
}
|
|
3661
3994
|
return lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".ts");
|
|
3662
3995
|
}
|
|
3663
3996
|
|
|
3997
|
+
// src/watch-paths.ts
|
|
3998
|
+
var PROJECT_CONFIG_BASENAMES = /* @__PURE__ */ new Set([
|
|
3999
|
+
"package.json",
|
|
4000
|
+
"nudo.json",
|
|
4001
|
+
"nudo.config.js",
|
|
4002
|
+
"nudo.config.mjs",
|
|
4003
|
+
"nudo.config.ts",
|
|
4004
|
+
".nudorc",
|
|
4005
|
+
".nudorc.json"
|
|
4006
|
+
]);
|
|
4007
|
+
function isSidecarPath(path) {
|
|
4008
|
+
const lower = path.toLowerCase().replace(/\\/g, "/");
|
|
4009
|
+
if (lower.includes(".nudo.draft.")) return false;
|
|
4010
|
+
return lower.endsWith(".nudo.js") || lower.endsWith(".nudo.mjs") || lower.endsWith(".nudo.ts");
|
|
4011
|
+
}
|
|
4012
|
+
function isProjectConfigPath(path) {
|
|
4013
|
+
const norm2 = path.replace(/\\/g, "/");
|
|
4014
|
+
if (/\/node_modules\//.test(`/${norm2}`)) return false;
|
|
4015
|
+
const base = norm2.split("/").pop() ?? path;
|
|
4016
|
+
return PROJECT_CONFIG_BASENAMES.has(base.toLowerCase());
|
|
4017
|
+
}
|
|
4018
|
+
function isWatchRelevantPath(path) {
|
|
4019
|
+
return isNudoTargetPath(path) || isSidecarPath(path) || isProjectConfigPath(path) || isEnvTemplatePath(path);
|
|
4020
|
+
}
|
|
4021
|
+
function ambientSourcesOfSidecar(sidecarPath) {
|
|
4022
|
+
const norm2 = sidecarPath.replace(/\\/g, "/");
|
|
4023
|
+
const m = norm2.match(/^(.*)\.nudo\.(js|ts)$/i);
|
|
4024
|
+
if (!m || norm2.toLowerCase().includes(".nudo.draft.")) return [];
|
|
4025
|
+
const base = m[1];
|
|
4026
|
+
const ext = m[2].toLowerCase();
|
|
4027
|
+
return ext === "js" ? [`${base}.js`, `${base}.mjs`] : [`${base}.ts`, `${base}.mts`];
|
|
4028
|
+
}
|
|
4029
|
+
|
|
4030
|
+
// src/analysis-scope.ts
|
|
4031
|
+
import { dirname as dirname9 } from "path";
|
|
4032
|
+
import { existsSync as existsSync6 } from "fs";
|
|
4033
|
+
import { sidecarPathOf } from "@nudojs/core";
|
|
4034
|
+
var NOISY_WARNING_CODES = /* @__PURE__ */ new Set([
|
|
4035
|
+
"nudo:unknown-recv",
|
|
4036
|
+
"nudo:builtin-unknown",
|
|
4037
|
+
"nudo:no-signature"
|
|
4038
|
+
]);
|
|
4039
|
+
function filterDiagnosticsByLevel(diags, level) {
|
|
4040
|
+
if (level === "verbose") return diags;
|
|
4041
|
+
if (level === "off") return [];
|
|
4042
|
+
if (level === "errors") return diags.filter((d) => d.severity === "error");
|
|
4043
|
+
return diags.filter((d) => {
|
|
4044
|
+
if (d.severity === "error") return true;
|
|
4045
|
+
if (d.severity === "info") return false;
|
|
4046
|
+
if (d.severity === "warning" && d.code && NOISY_WARNING_CODES.has(d.code)) return false;
|
|
4047
|
+
return d.severity === "warning";
|
|
4048
|
+
});
|
|
4049
|
+
}
|
|
4050
|
+
function diagnosticsLevelForFile(filePath) {
|
|
4051
|
+
return analysisConfig(findProjectConfig(dirname9(filePath))?.config).diagnostics;
|
|
4052
|
+
}
|
|
4053
|
+
function hasNudoDirectives(source) {
|
|
4054
|
+
return /@nudo:(case|mock|pure|skip|sample|refine|interface|import|env|mock-module|as|replace)\b/.test(source);
|
|
4055
|
+
}
|
|
4056
|
+
function stripCommentsAndStrings(source) {
|
|
4057
|
+
return source.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\/\/[^\n]*/g, " ").replace(/'(?:\\.|[^'\\])*'|"(?:\\.|[^"\\])*"|`(?:\\.|[^`\\])*`/g, '""');
|
|
4058
|
+
}
|
|
4059
|
+
function hasExport(source) {
|
|
4060
|
+
const s = stripCommentsAndStrings(source);
|
|
4061
|
+
return /(^|[\s;}])export\b/.test(s) || /\bmodule\.exports\b/.test(s) || /\bexports\s*[.[]/.test(s) || /Object\.assign\s*\(\s*(module\.)?exports\b/.test(s);
|
|
4062
|
+
}
|
|
4063
|
+
function shouldAnalyzeFile(filePath, source, config) {
|
|
4064
|
+
if (!isNudoTargetPath(filePath)) return false;
|
|
4065
|
+
const proj = findProjectConfig(dirname9(filePath));
|
|
4066
|
+
const cfg = config ?? analysisConfig(proj?.config);
|
|
4067
|
+
const projectDir = proj?.projectDir;
|
|
4068
|
+
if (cfg.exclude.length > 0 && projectDir) {
|
|
4069
|
+
if (matchesEmitAllowlist(filePath, projectDir, cfg.exclude)) return false;
|
|
4070
|
+
} else if (cfg.exclude.length > 0 && !projectDir) {
|
|
4071
|
+
const norm2 = filePath.replace(/\\/g, "/");
|
|
4072
|
+
if (cfg.exclude.some((p) => simpleExcludeHit(p, norm2))) return false;
|
|
4073
|
+
}
|
|
4074
|
+
if (cfg.include.length > 0 && projectDir) {
|
|
4075
|
+
if (!matchesEmitAllowlist(filePath, projectDir, cfg.include)) return false;
|
|
4076
|
+
}
|
|
4077
|
+
if (cfg.mode === "all") return true;
|
|
4078
|
+
const text = source;
|
|
4079
|
+
if (text !== void 0 && hasNudoDirectives(text)) return true;
|
|
4080
|
+
if (cfg.mode === "directives") return false;
|
|
4081
|
+
if (text !== void 0 && hasExport(text)) return true;
|
|
4082
|
+
try {
|
|
4083
|
+
if (existsSync6(sidecarPathOf(filePath))) return true;
|
|
4084
|
+
} catch {
|
|
4085
|
+
}
|
|
4086
|
+
return false;
|
|
4087
|
+
}
|
|
4088
|
+
function simpleExcludeHit(pattern, path) {
|
|
4089
|
+
const bare = pattern.replace(/^\*\*/, "").replace(/^\//, "").replace(/\/\*\*$/, "").replace(/^\*\//, "");
|
|
4090
|
+
if (!bare || bare.includes("*")) {
|
|
4091
|
+
return /\/(node_modules|dist|coverage)\//.test("/" + path + "/");
|
|
4092
|
+
}
|
|
4093
|
+
return path.includes(`/${bare}/`) || path.endsWith(`/${bare}`);
|
|
4094
|
+
}
|
|
4095
|
+
|
|
3664
4096
|
// src/session-cache.ts
|
|
3665
4097
|
import {
|
|
3666
4098
|
resetGeneralizeMemo,
|
|
@@ -3674,10 +4106,12 @@ function evictAnalysisCachesForFiles(files) {
|
|
|
3674
4106
|
evictAnalysisFileCacheForFiles(files);
|
|
3675
4107
|
evictFnAnalysisCacheForFiles(files);
|
|
3676
4108
|
evictAbsModuleCacheFiles(files);
|
|
4109
|
+
clearPathEnvCaches();
|
|
3677
4110
|
}
|
|
3678
4111
|
function clearAnalysisSessionCaches() {
|
|
3679
4112
|
clearBPathCache();
|
|
3680
4113
|
clearAbsModuleCache();
|
|
4114
|
+
clearPathEnvCaches();
|
|
3681
4115
|
resetGeneralizeMemo();
|
|
3682
4116
|
resetCheckSourceMemo();
|
|
3683
4117
|
resetNudoModuleExecCache();
|
|
@@ -3687,12 +4121,183 @@ function resetAllAnalysisCaches() {
|
|
|
3687
4121
|
resetParseSourceCache();
|
|
3688
4122
|
}
|
|
3689
4123
|
|
|
4124
|
+
// src/analysis-session.ts
|
|
4125
|
+
function createDefaultSession() {
|
|
4126
|
+
return {
|
|
4127
|
+
evictForDependents(files) {
|
|
4128
|
+
evictAnalysisCachesForFiles(files);
|
|
4129
|
+
},
|
|
4130
|
+
clear() {
|
|
4131
|
+
clearAnalysisSessionCaches();
|
|
4132
|
+
},
|
|
4133
|
+
reset() {
|
|
4134
|
+
resetAllAnalysisCaches();
|
|
4135
|
+
},
|
|
4136
|
+
analyze(filePath, source, activeCases, externalCallRecords) {
|
|
4137
|
+
return analyzeFile(
|
|
4138
|
+
filePath,
|
|
4139
|
+
source,
|
|
4140
|
+
activeCases,
|
|
4141
|
+
externalCallRecords
|
|
4142
|
+
);
|
|
4143
|
+
}
|
|
4144
|
+
};
|
|
4145
|
+
}
|
|
4146
|
+
var defaultSession = void 0;
|
|
4147
|
+
function getAnalysisSession() {
|
|
4148
|
+
if (!defaultSession) defaultSession = createDefaultSession();
|
|
4149
|
+
return defaultSession;
|
|
4150
|
+
}
|
|
4151
|
+
function setAnalysisSession(session) {
|
|
4152
|
+
const prev = defaultSession;
|
|
4153
|
+
defaultSession = session;
|
|
4154
|
+
return prev;
|
|
4155
|
+
}
|
|
4156
|
+
|
|
4157
|
+
// src/disk-cache.ts
|
|
4158
|
+
import { createHash } from "crypto";
|
|
4159
|
+
import { createRequire } from "module";
|
|
4160
|
+
import { mkdirSync, readFileSync as readFileSync6, writeFileSync, existsSync as existsSync7, rmSync } from "fs";
|
|
4161
|
+
import { join as join5, dirname as dirname10, relative, sep, isAbsolute } from "path";
|
|
4162
|
+
function readServiceVersion() {
|
|
4163
|
+
try {
|
|
4164
|
+
const require2 = createRequire(import.meta.url);
|
|
4165
|
+
for (const p of ["../package.json", "./package.json", "../../package.json"]) {
|
|
4166
|
+
try {
|
|
4167
|
+
const pkg = require2(p);
|
|
4168
|
+
if (pkg?.name === "@nudojs/service" && pkg.version) return pkg.version;
|
|
4169
|
+
if (pkg?.version && p.includes("service")) return pkg.version;
|
|
4170
|
+
} catch {
|
|
4171
|
+
}
|
|
4172
|
+
}
|
|
4173
|
+
} catch {
|
|
4174
|
+
}
|
|
4175
|
+
return "0";
|
|
4176
|
+
}
|
|
4177
|
+
var ANALYSIS_ABI = `nudo-check-cache-v2+${readServiceVersion()}`;
|
|
4178
|
+
function sha256Hex(data) {
|
|
4179
|
+
return createHash("sha256").update(data).digest("hex");
|
|
4180
|
+
}
|
|
4181
|
+
function relativizePath(p, root) {
|
|
4182
|
+
const norm2 = p.split(sep).join("/");
|
|
4183
|
+
if (!root) return norm2;
|
|
4184
|
+
const r = relative(root, p).split(sep).join("/");
|
|
4185
|
+
if (!r.startsWith("..") && !isAbsolute(r)) return r;
|
|
4186
|
+
return `ext:${createHash("sha256").update(norm2).digest("hex").slice(0, 16)}`;
|
|
4187
|
+
}
|
|
4188
|
+
function sanitizeCacheNamespace(ns) {
|
|
4189
|
+
return ns.replace(/[^a-z0-9_-]/gi, "_").replace(/^_+|_+$/g, "") || "cache";
|
|
4190
|
+
}
|
|
4191
|
+
var DiskCache = class {
|
|
4192
|
+
root;
|
|
4193
|
+
ns;
|
|
4194
|
+
enabled = false;
|
|
4195
|
+
constructor(opts) {
|
|
4196
|
+
this.root = opts.root;
|
|
4197
|
+
this.ns = sanitizeCacheNamespace(opts.namespace);
|
|
4198
|
+
this.enabled = !!opts.root;
|
|
4199
|
+
}
|
|
4200
|
+
pathFor(key) {
|
|
4201
|
+
if (!/^[a-f0-9]{16,128}$/i.test(key)) {
|
|
4202
|
+
throw new Error("DiskCache key must be a hex digest");
|
|
4203
|
+
}
|
|
4204
|
+
return join5(this.root, this.ns, key.slice(0, 2), `${key}.json`);
|
|
4205
|
+
}
|
|
4206
|
+
get(key) {
|
|
4207
|
+
if (!this.enabled || !this.root) return void 0;
|
|
4208
|
+
try {
|
|
4209
|
+
const p = this.pathFor(key);
|
|
4210
|
+
if (!existsSync7(p)) return void 0;
|
|
4211
|
+
const raw = readFileSync6(p, "utf8");
|
|
4212
|
+
const parsed = JSON.parse(raw);
|
|
4213
|
+
if (parsed?.abi !== ANALYSIS_ABI) return void 0;
|
|
4214
|
+
return parsed.value;
|
|
4215
|
+
} catch {
|
|
4216
|
+
return void 0;
|
|
4217
|
+
}
|
|
4218
|
+
}
|
|
4219
|
+
set(key, value) {
|
|
4220
|
+
if (!this.enabled || !this.root) return;
|
|
4221
|
+
try {
|
|
4222
|
+
const p = this.pathFor(key);
|
|
4223
|
+
mkdirSync(dirname10(p), { recursive: true });
|
|
4224
|
+
writeFileSync(p, JSON.stringify({ abi: ANALYSIS_ABI, value }), "utf8");
|
|
4225
|
+
} catch {
|
|
4226
|
+
}
|
|
4227
|
+
}
|
|
4228
|
+
clearNamespace() {
|
|
4229
|
+
if (!this.enabled || !this.root) return;
|
|
4230
|
+
try {
|
|
4231
|
+
rmSync(join5(this.root, this.ns), { recursive: true, force: true });
|
|
4232
|
+
} catch {
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
};
|
|
4236
|
+
function checkCacheKey(filePath, source, opts) {
|
|
4237
|
+
const rel = relativizePath(filePath, opts.projectDir);
|
|
4238
|
+
const sidecarSha = opts.sidecarContent != null ? sha256Hex(opts.sidecarContent) : "nosidecar";
|
|
4239
|
+
const depSeg = (opts.depContents ?? []).map(
|
|
4240
|
+
(d) => `${relativizePath(d.path, opts.projectDir)}\0${d.content != null ? sha256Hex(d.content) : "miss"}`
|
|
4241
|
+
).join("\n");
|
|
4242
|
+
const envSeg = (opts.projectEnvNames ?? []).length > 0 ? [...opts.projectEnvNames ?? []].sort().join(",") : "-";
|
|
4243
|
+
const cfgSeg = opts.analysisCfg ? `${opts.analysisCfg.mode ?? "-"}|${opts.analysisCfg.evalMissingSlot ?? "-"}|${opts.analysisCfg.callSiteBudget ?? "-"}` : "-";
|
|
4244
|
+
return sha256Hex(
|
|
4245
|
+
[
|
|
4246
|
+
ANALYSIS_ABI,
|
|
4247
|
+
rel,
|
|
4248
|
+
opts.autoBind ? "ab1" : "ab0",
|
|
4249
|
+
sha256Hex(source),
|
|
4250
|
+
sidecarSha,
|
|
4251
|
+
depSeg,
|
|
4252
|
+
envSeg,
|
|
4253
|
+
cfgSeg
|
|
4254
|
+
].join("\0")
|
|
4255
|
+
);
|
|
4256
|
+
}
|
|
4257
|
+
function ifaceCacheKey(filePath, source, opts) {
|
|
4258
|
+
const rel = relativizePath(filePath, opts.projectDir);
|
|
4259
|
+
const sidecarSeg = opts.autoBind && opts.sidecarSource !== void 0 ? `sc:${sha256Hex(opts.sidecarSource)}` : "sc0";
|
|
4260
|
+
const depSeg = (opts.depContents ?? []).map(
|
|
4261
|
+
(d) => `${relativizePath(d.path, opts.projectDir)}\0${d.content != null ? sha256Hex(d.content) : "miss"}`
|
|
4262
|
+
).join("\n");
|
|
4263
|
+
const envSeg = (opts.projectEnvNames ?? []).length > 0 ? [...opts.projectEnvNames ?? []].sort().join(",") : "-";
|
|
4264
|
+
return sha256Hex(
|
|
4265
|
+
[
|
|
4266
|
+
ANALYSIS_ABI,
|
|
4267
|
+
"iface",
|
|
4268
|
+
rel,
|
|
4269
|
+
opts.autoBind ? "ab1" : "ab0",
|
|
4270
|
+
sha256Hex(source),
|
|
4271
|
+
sidecarSeg,
|
|
4272
|
+
depSeg,
|
|
4273
|
+
envSeg
|
|
4274
|
+
].join("\0")
|
|
4275
|
+
);
|
|
4276
|
+
}
|
|
4277
|
+
function extractNudoImportSpecs(source) {
|
|
4278
|
+
const specs = /* @__PURE__ */ new Set();
|
|
4279
|
+
const named = /@nudo:import\s*\{[^}]*\}\s*from\s*["']([^"']+)["']/g;
|
|
4280
|
+
const ns = /@nudo:import\s+\*\s+as\s+\w+\s+from\s*["']([^"']+)["']/g;
|
|
4281
|
+
let m;
|
|
4282
|
+
while (m = named.exec(source)) specs.add(m[1]);
|
|
4283
|
+
while (m = ns.exec(source)) specs.add(m[1]);
|
|
4284
|
+
return [...specs];
|
|
4285
|
+
}
|
|
4286
|
+
|
|
4287
|
+
// src/dep-contents.ts
|
|
4288
|
+
import { loadModuleDepsFingerprint as loadModuleDepsFingerprint3 } from "@nudojs/core";
|
|
4289
|
+
function collectLoadDepContents(filePath, source, loadModule) {
|
|
4290
|
+
const fp = loadModuleDepsFingerprint3(source, loadModule, filePath);
|
|
4291
|
+
const depContents = fp.contents.length > 0 ? fp.contents : fp.paths.map((path) => ({ path, content: null }));
|
|
4292
|
+
return { depContents, truncated: fp.truncated };
|
|
4293
|
+
}
|
|
4294
|
+
|
|
3690
4295
|
// src/harvest-node.ts
|
|
3691
|
-
import { existsSync as
|
|
4296
|
+
import { existsSync as existsSync8 } from "fs";
|
|
3692
4297
|
import { harvestDts as harvestDts2 } from "@nudojs/harvester";
|
|
3693
4298
|
function harvestNodeTypes(fromDir, maxFiles = 12, maxMs = 2500) {
|
|
3694
4299
|
const root = resolvePackageRoot("@types/node", fromDir) ?? resolvePackageRoot("node", fromDir);
|
|
3695
|
-
if (!root || !
|
|
4300
|
+
if (!root || !existsSync8(root)) {
|
|
3696
4301
|
return { ok: false, error: "@types/node not found" };
|
|
3697
4302
|
}
|
|
3698
4303
|
const dts = collectDtsFiles(root, maxFiles);
|
|
@@ -3712,6 +4317,9 @@ function summarizeNodeEnv(env) {
|
|
|
3712
4317
|
|
|
3713
4318
|
// src/semantic-tokens.ts
|
|
3714
4319
|
import { parse as parse7 } from "@nudojs/parser";
|
|
4320
|
+
import {
|
|
4321
|
+
interfaceTierOf as interfaceTierOf2
|
|
4322
|
+
} from "@nudojs/core";
|
|
3715
4323
|
var SEMANTIC_TOKEN_TYPES = [
|
|
3716
4324
|
"function",
|
|
3717
4325
|
"variable",
|
|
@@ -3729,7 +4337,13 @@ var SEMANTIC_TOKEN_MODIFIERS = [
|
|
|
3729
4337
|
"declaration",
|
|
3730
4338
|
"readonly",
|
|
3731
4339
|
"deprecated",
|
|
3732
|
-
"unreachable"
|
|
4340
|
+
"unreachable",
|
|
4341
|
+
/** handwritten:显式契约(侧车手写 / @nudo:refine) */
|
|
4342
|
+
"contract",
|
|
4343
|
+
/** generated:侧车 @generated 段 */
|
|
4344
|
+
"generated",
|
|
4345
|
+
/** derived:implicit 展示档(非义务契约) */
|
|
4346
|
+
"derived"
|
|
3733
4347
|
];
|
|
3734
4348
|
function encodeSemanticTokens(tokens) {
|
|
3735
4349
|
const result = [];
|
|
@@ -3750,6 +4364,12 @@ var TYPE_PARAMETER = SEMANTIC_TOKEN_TYPES.indexOf("parameter");
|
|
|
3750
4364
|
var TYPE_PROPERTY = SEMANTIC_TOKEN_TYPES.indexOf("property");
|
|
3751
4365
|
var TYPE_METHOD = SEMANTIC_TOKEN_TYPES.indexOf("method");
|
|
3752
4366
|
var MOD_DECLARATION = 1 << SEMANTIC_TOKEN_MODIFIERS.indexOf("declaration");
|
|
4367
|
+
var MOD_CONTRACT = 1 << SEMANTIC_TOKEN_MODIFIERS.indexOf("contract");
|
|
4368
|
+
var MOD_GENERATED = 1 << SEMANTIC_TOKEN_MODIFIERS.indexOf("generated");
|
|
4369
|
+
var MOD_DERIVED = 1 << SEMANTIC_TOKEN_MODIFIERS.indexOf("derived");
|
|
4370
|
+
function interfaceTierModifierBit(src) {
|
|
4371
|
+
return src === "handwritten" ? MOD_CONTRACT : src === "generated" ? MOD_GENERATED : MOD_DERIVED;
|
|
4372
|
+
}
|
|
3753
4373
|
function collectFunctionBindingNames(filePath, source, _ast) {
|
|
3754
4374
|
try {
|
|
3755
4375
|
const binds = collectAbsBindingsFromGraph(source, filePath);
|
|
@@ -3764,7 +4384,7 @@ function collectFunctionBindingNames(filePath, source, _ast) {
|
|
|
3764
4384
|
}
|
|
3765
4385
|
return /* @__PURE__ */ new Set();
|
|
3766
4386
|
}
|
|
3767
|
-
function buildSemanticTokens(filePath, source) {
|
|
4387
|
+
function buildSemanticTokens(filePath, source, opts) {
|
|
3768
4388
|
let ast;
|
|
3769
4389
|
try {
|
|
3770
4390
|
ast = parse7(source);
|
|
@@ -3774,6 +4394,7 @@ function buildSemanticTokens(filePath, source) {
|
|
|
3774
4394
|
const functionNames = collectFunctionBindingNames(filePath, source, ast);
|
|
3775
4395
|
const program = ast.program ?? ast;
|
|
3776
4396
|
const topLevelDeclarators = /* @__PURE__ */ new Set();
|
|
4397
|
+
const topLevelFnDeclNodes = /* @__PURE__ */ new Set();
|
|
3777
4398
|
for (const stmt of program.body ?? []) {
|
|
3778
4399
|
const decl = stmt.type === "ExportNamedDeclaration" || stmt.type === "ExportDefaultDeclaration" ? stmt.declaration ?? null : stmt;
|
|
3779
4400
|
if (decl && decl.type === "VariableDeclaration") {
|
|
@@ -3781,9 +4402,30 @@ function buildSemanticTokens(filePath, source) {
|
|
|
3781
4402
|
topLevelDeclarators.add(d);
|
|
3782
4403
|
}
|
|
3783
4404
|
}
|
|
4405
|
+
if (decl && decl.type === "FunctionDeclaration" && decl.id) {
|
|
4406
|
+
topLevelFnDeclNodes.add(decl);
|
|
4407
|
+
}
|
|
3784
4408
|
}
|
|
4409
|
+
const tierOpts = {
|
|
4410
|
+
...opts?.loadModule ? { loadModule: opts.loadModule } : {},
|
|
4411
|
+
...opts?.autoBind !== void 0 ? { autoBind: opts.autoBind } : {}
|
|
4412
|
+
};
|
|
4413
|
+
const tierModCache = /* @__PURE__ */ new Map();
|
|
4414
|
+
const tierModFor = (name) => {
|
|
4415
|
+
const cached = tierModCache.get(name);
|
|
4416
|
+
if (cached !== void 0) return cached;
|
|
4417
|
+
let bit = 0;
|
|
4418
|
+
try {
|
|
4419
|
+
const tier = interfaceTierOf2(source, name, filePath, tierOpts);
|
|
4420
|
+
if (tier) bit = interfaceTierModifierBit(tier.source);
|
|
4421
|
+
} catch {
|
|
4422
|
+
bit = 0;
|
|
4423
|
+
}
|
|
4424
|
+
tierModCache.set(name, bit);
|
|
4425
|
+
return bit;
|
|
4426
|
+
};
|
|
3785
4427
|
const tokens = [];
|
|
3786
|
-
const pushIdentifier = (id, typeIndex) => {
|
|
4428
|
+
const pushIdentifier = (id, typeIndex, extraMod = 0) => {
|
|
3787
4429
|
const loc = id.loc;
|
|
3788
4430
|
const name = id.name;
|
|
3789
4431
|
if (!loc || typeof name !== "string") return;
|
|
@@ -3792,7 +4434,7 @@ function buildSemanticTokens(filePath, source) {
|
|
|
3792
4434
|
char: loc.start.column,
|
|
3793
4435
|
length: name.length,
|
|
3794
4436
|
typeIndex,
|
|
3795
|
-
modifierBitmask: MOD_DECLARATION
|
|
4437
|
+
modifierBitmask: MOD_DECLARATION | extraMod
|
|
3796
4438
|
});
|
|
3797
4439
|
};
|
|
3798
4440
|
const collectParams = (params) => {
|
|
@@ -3815,15 +4457,23 @@ function buildSemanticTokens(filePath, source) {
|
|
|
3815
4457
|
const id = n.id;
|
|
3816
4458
|
if (id?.type === "Identifier") {
|
|
3817
4459
|
const isTopLevel = topLevelDeclarators.has(node);
|
|
3818
|
-
const
|
|
3819
|
-
|
|
4460
|
+
const name = id.name;
|
|
4461
|
+
const isFn = isTopLevel && isFunctionValue(name);
|
|
4462
|
+
const typeIndex = isFn ? TYPE_FUNCTION : TYPE_VARIABLE;
|
|
4463
|
+
const extraMod = isFn ? tierModFor(name) : 0;
|
|
4464
|
+
pushIdentifier(id, typeIndex, extraMod);
|
|
3820
4465
|
}
|
|
3821
4466
|
break;
|
|
3822
4467
|
}
|
|
3823
4468
|
case "FunctionDeclaration":
|
|
3824
4469
|
case "FunctionExpression": {
|
|
3825
4470
|
const id = n.id;
|
|
3826
|
-
if (id?.type === "Identifier")
|
|
4471
|
+
if (id?.type === "Identifier") {
|
|
4472
|
+
const name = id.name;
|
|
4473
|
+
const isTopLevelFnDecl = n.type === "FunctionDeclaration" && topLevelFnDeclNodes.has(node);
|
|
4474
|
+
const extraMod = isTopLevelFnDecl ? tierModFor(name) : 0;
|
|
4475
|
+
pushIdentifier(id, TYPE_FUNCTION, extraMod);
|
|
4476
|
+
}
|
|
3827
4477
|
collectParams(n.params ?? []);
|
|
3828
4478
|
break;
|
|
3829
4479
|
}
|
|
@@ -3871,14 +4521,102 @@ import {
|
|
|
3871
4521
|
litValue as litValue3,
|
|
3872
4522
|
abs as makeAbs2,
|
|
3873
4523
|
isTemplateLike,
|
|
3874
|
-
templatePartsOf
|
|
4524
|
+
templatePartsOf,
|
|
4525
|
+
collectAbsFreeVars
|
|
3875
4526
|
} from "@nudojs/core";
|
|
3876
|
-
function
|
|
3877
|
-
|
|
3878
|
-
if (
|
|
4527
|
+
function tsTypeParamName(id) {
|
|
4528
|
+
let n = id.replace(/[^A-Za-z0-9_$]/g, "_");
|
|
4529
|
+
if (!/^[A-Za-z_$]/.test(n)) n = `T_${n}`;
|
|
4530
|
+
if (n.length === 0) n = "T";
|
|
4531
|
+
return n;
|
|
4532
|
+
}
|
|
4533
|
+
function wrapComplexAbs(a, typeVars) {
|
|
4534
|
+
const ts = absToTSType(a, typeVars);
|
|
4535
|
+
if (a.shape.k === "sum" || a.shape.k === "fn") return `(${ts})`;
|
|
3879
4536
|
return ts;
|
|
3880
4537
|
}
|
|
3881
|
-
function
|
|
4538
|
+
function wrapUnionMember(a, typeVars) {
|
|
4539
|
+
const ts = absToTSType(a, typeVars);
|
|
4540
|
+
if (a.shape.k === "fn") return `(${ts})`;
|
|
4541
|
+
return ts;
|
|
4542
|
+
}
|
|
4543
|
+
var TS_PARAM_RESERVED = /* @__PURE__ */ new Set([
|
|
4544
|
+
"break",
|
|
4545
|
+
"case",
|
|
4546
|
+
"catch",
|
|
4547
|
+
"class",
|
|
4548
|
+
"const",
|
|
4549
|
+
"continue",
|
|
4550
|
+
"debugger",
|
|
4551
|
+
"default",
|
|
4552
|
+
"delete",
|
|
4553
|
+
"do",
|
|
4554
|
+
"else",
|
|
4555
|
+
"enum",
|
|
4556
|
+
"export",
|
|
4557
|
+
"extends",
|
|
4558
|
+
"false",
|
|
4559
|
+
"finally",
|
|
4560
|
+
"for",
|
|
4561
|
+
"function",
|
|
4562
|
+
"if",
|
|
4563
|
+
"import",
|
|
4564
|
+
"in",
|
|
4565
|
+
"instanceof",
|
|
4566
|
+
"new",
|
|
4567
|
+
"null",
|
|
4568
|
+
"return",
|
|
4569
|
+
"super",
|
|
4570
|
+
"switch",
|
|
4571
|
+
"this",
|
|
4572
|
+
"throw",
|
|
4573
|
+
"true",
|
|
4574
|
+
"try",
|
|
4575
|
+
"typeof",
|
|
4576
|
+
"var",
|
|
4577
|
+
"void",
|
|
4578
|
+
"while",
|
|
4579
|
+
"with",
|
|
4580
|
+
"yield",
|
|
4581
|
+
"let",
|
|
4582
|
+
"static",
|
|
4583
|
+
"await",
|
|
4584
|
+
"implements",
|
|
4585
|
+
"interface",
|
|
4586
|
+
"package",
|
|
4587
|
+
"private",
|
|
4588
|
+
"protected",
|
|
4589
|
+
"public",
|
|
4590
|
+
"arguments",
|
|
4591
|
+
"eval",
|
|
4592
|
+
"constructor"
|
|
4593
|
+
]);
|
|
4594
|
+
function isTsIdent(name) {
|
|
4595
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) && !TS_PARAM_RESERVED.has(name);
|
|
4596
|
+
}
|
|
4597
|
+
function sanitizeParamName(name, index) {
|
|
4598
|
+
if (name.startsWith("...")) {
|
|
4599
|
+
const rest = name.slice(3);
|
|
4600
|
+
if (isTsIdent(rest)) return name;
|
|
4601
|
+
return `...arg${index}`;
|
|
4602
|
+
}
|
|
4603
|
+
if (isTsIdent(name)) return name;
|
|
4604
|
+
return `arg${index}`;
|
|
4605
|
+
}
|
|
4606
|
+
function formatPropKey(k) {
|
|
4607
|
+
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(k)) return k;
|
|
4608
|
+
if (/^\d+$/.test(k)) return k;
|
|
4609
|
+
return JSON.stringify(k);
|
|
4610
|
+
}
|
|
4611
|
+
function absToTSType(a, typeVars) {
|
|
4612
|
+
if (a.shape.k === "any" && a.term?.op === "var" && typeVars) {
|
|
4613
|
+
const mapped = typeVars.get(a.term.id);
|
|
4614
|
+
if (mapped) return mapped;
|
|
4615
|
+
}
|
|
4616
|
+
if (a.shape.k === "arr" && a.shape.element.shape.k === "any" && a.shape.element.term?.op === "var" && typeVars) {
|
|
4617
|
+
const mapped = typeVars.get(a.shape.element.term.id);
|
|
4618
|
+
if (mapped) return `${mapped}[]`;
|
|
4619
|
+
}
|
|
3882
4620
|
if (a.term?.op === "lit") {
|
|
3883
4621
|
const v = a.term.value;
|
|
3884
4622
|
if (v === null) return "null";
|
|
@@ -3894,7 +4632,7 @@ function absToTSType(a) {
|
|
|
3894
4632
|
const inner = parts.map((p) => {
|
|
3895
4633
|
const lv = litValue3(p);
|
|
3896
4634
|
if (typeof lv === "string") return lv;
|
|
3897
|
-
return `\${${absToTSType(p)}}`;
|
|
4635
|
+
return `\${${absToTSType(p, typeVars)}}`;
|
|
3898
4636
|
}).join("");
|
|
3899
4637
|
return `\`${inner}\``;
|
|
3900
4638
|
}
|
|
@@ -3908,34 +4646,50 @@ function absToTSType(a) {
|
|
|
3908
4646
|
return a.shape.type;
|
|
3909
4647
|
case "obj": {
|
|
3910
4648
|
const entries = Object.entries(a.shape.slots).map(([k, slot]) => {
|
|
3911
|
-
const inner = absToTSType(slot.value);
|
|
4649
|
+
const inner = absToTSType(slot.value, typeVars);
|
|
3912
4650
|
if (slot.optional) {
|
|
3913
|
-
const t = slot.value
|
|
3914
|
-
return `${k}: ${t} | undefined`;
|
|
4651
|
+
const t = wrapComplexAbs(slot.value, typeVars);
|
|
4652
|
+
return `${formatPropKey(k)}: ${t} | undefined`;
|
|
3915
4653
|
}
|
|
3916
|
-
return `${k}: ${inner}`;
|
|
4654
|
+
return `${formatPropKey(k)}: ${inner}`;
|
|
3917
4655
|
});
|
|
3918
4656
|
if (entries.length === 0) return "{}";
|
|
3919
4657
|
return `{ ${entries.join("; ")} }`;
|
|
3920
4658
|
}
|
|
3921
4659
|
case "arr":
|
|
3922
|
-
return `${wrapComplexAbs(a.shape.element)}[]`;
|
|
4660
|
+
return `${wrapComplexAbs(a.shape.element, typeVars)}[]`;
|
|
3923
4661
|
case "tuple": {
|
|
3924
|
-
const
|
|
3925
|
-
|
|
4662
|
+
const parts = a.shape.elements.map((e) => absToTSType(e, typeVars));
|
|
4663
|
+
if (a.shape.rest) {
|
|
4664
|
+
const rest = a.shape.rest;
|
|
4665
|
+
const restTs = rest.shape.k === "arr" ? absToTSType(rest, typeVars) : `${absToTSType(rest, typeVars)}[]`;
|
|
4666
|
+
parts.push(`...${restTs}`);
|
|
4667
|
+
}
|
|
4668
|
+
return `[${parts.join(", ")}]`;
|
|
3926
4669
|
}
|
|
3927
4670
|
case "fn": {
|
|
3928
|
-
const
|
|
3929
|
-
const
|
|
4671
|
+
const paramTypes = a.shape.paramTypes;
|
|
4672
|
+
const params = a.shape.params.map((p, i) => {
|
|
4673
|
+
const isRest = p.startsWith("...");
|
|
4674
|
+
const name = sanitizeParamName(p, i);
|
|
4675
|
+
const pt = paramTypes?.[i];
|
|
4676
|
+
let typeStr;
|
|
4677
|
+
if (pt) typeStr = absToTSType(pt, typeVars);
|
|
4678
|
+
else if (isRest) typeStr = "unknown[]";
|
|
4679
|
+
else typeStr = "unknown";
|
|
4680
|
+
return `${name}: ${typeStr}`;
|
|
4681
|
+
}).join(", ");
|
|
4682
|
+
const ret = a.shape.returnType ? absToTSType(a.shape.returnType, typeVars) : "unknown";
|
|
3930
4683
|
return `(${params}) => ${ret}`;
|
|
3931
4684
|
}
|
|
3932
4685
|
case "brand":
|
|
3933
|
-
return a.shape.name;
|
|
4686
|
+
return isTsIdent(a.shape.name) || /^[A-Z][A-Za-z0-9_$]*$/.test(a.shape.name) ? a.shape.name : "unknown";
|
|
3934
4687
|
case "eff":
|
|
3935
|
-
if (a.shape.eff === "promise")
|
|
3936
|
-
|
|
4688
|
+
if (a.shape.eff === "promise")
|
|
4689
|
+
return `Promise<${absToTSType(a.shape.inner, typeVars)}>`;
|
|
4690
|
+
return absToTSType(a.shape.inner, typeVars);
|
|
3937
4691
|
case "sum": {
|
|
3938
|
-
const parts = a.shape.members.map(
|
|
4692
|
+
const parts = a.shape.members.map((m) => wrapUnionMember(m, typeVars)).filter((p) => p !== "never");
|
|
3939
4693
|
const uniq = [...new Set(parts)];
|
|
3940
4694
|
if (uniq.length === 0) return "never";
|
|
3941
4695
|
if (uniq.length === 1) return uniq[0];
|
|
@@ -4067,13 +4821,18 @@ function computeMainSignature(fn) {
|
|
|
4067
4821
|
}
|
|
4068
4822
|
const typeStr = paramTypeFromAbs(members);
|
|
4069
4823
|
let name = getParamName(fn, i);
|
|
4824
|
+
const isRest = name.startsWith("...");
|
|
4825
|
+
const bare = isRest ? name.slice(3) : name;
|
|
4826
|
+
if (!isTsIdent(bare)) {
|
|
4827
|
+
name = isRest ? `...arg${i}` : `arg${i}`;
|
|
4828
|
+
}
|
|
4070
4829
|
if (usedNames.has(name)) {
|
|
4071
4830
|
let n = 2;
|
|
4072
4831
|
while (usedNames.has(`${name}${n}`)) n++;
|
|
4073
4832
|
name = `${name}${n}`;
|
|
4074
4833
|
}
|
|
4075
4834
|
usedNames.add(name);
|
|
4076
|
-
const optional = i >= minArity && !
|
|
4835
|
+
const optional = i >= minArity && !isRest;
|
|
4077
4836
|
params.push(optional ? `${name}?: ${typeStr}` : `${name}: ${typeStr}`);
|
|
4078
4837
|
paramNames.push(name);
|
|
4079
4838
|
paramTypes.push(typeStr);
|
|
@@ -4088,7 +4847,7 @@ function generateJSDoc(fn, sig) {
|
|
|
4088
4847
|
for (const c of fn.cases) {
|
|
4089
4848
|
const preciseDiffers = c.argAbs.length !== sig.paramTypes.length || c.argAbs.some((a, i) => absToTSType(a) !== sig.paramTypes[i]) || absToTSType(c.abs) !== sig.returnType;
|
|
4090
4849
|
if (!preciseDiffers) continue;
|
|
4091
|
-
const argsStr = c.argAbs.map(absToTSType).join(", ");
|
|
4850
|
+
const argsStr = c.argAbs.map((a) => absToTSType(a)).join(", ");
|
|
4092
4851
|
lines.push(` * Case: ${c.name} (${argsStr}) => ${absToTSType(c.abs)}`);
|
|
4093
4852
|
}
|
|
4094
4853
|
for (let i = 0; i < sig.paramTypes.length; i++) {
|
|
@@ -4098,8 +4857,103 @@ function generateJSDoc(fn, sig) {
|
|
|
4098
4857
|
lines.push(" */");
|
|
4099
4858
|
return lines.join("\n");
|
|
4100
4859
|
}
|
|
4860
|
+
function computeHofSignature(fn) {
|
|
4861
|
+
const hof = fn.hof;
|
|
4862
|
+
if (!hof) return void 0;
|
|
4863
|
+
const byParam = /* @__PURE__ */ new Map();
|
|
4864
|
+
for (const s of hof.entryShapes ?? []) byParam.set(s.param, s.abs);
|
|
4865
|
+
for (const r of hof.fnRels ?? []) byParam.set(r.param, r.abs);
|
|
4866
|
+
if (byParam.size === 0 && !hof.symbolic) return void 0;
|
|
4867
|
+
if (fn.cases.length > 0) {
|
|
4868
|
+
for (const [param] of byParam) {
|
|
4869
|
+
const idx = fn.paramNames.indexOf(param);
|
|
4870
|
+
if (idx < 0) continue;
|
|
4871
|
+
const caseType = paramTypeFromAbs(
|
|
4872
|
+
fn.cases.map((c) => c.argAbs[idx]).filter((a) => !!a)
|
|
4873
|
+
);
|
|
4874
|
+
if (caseType !== "unknown" && caseType !== "unknown[]") {
|
|
4875
|
+
return void 0;
|
|
4876
|
+
}
|
|
4877
|
+
}
|
|
4878
|
+
}
|
|
4879
|
+
const arity = Math.max(
|
|
4880
|
+
fn.paramNames.length,
|
|
4881
|
+
...[...byParam.keys()].map((p) => fn.paramNames.indexOf(p) + 1),
|
|
4882
|
+
0
|
|
4883
|
+
);
|
|
4884
|
+
if (arity === 0 && !hof.symbolic) return void 0;
|
|
4885
|
+
const free = /* @__PURE__ */ new Set();
|
|
4886
|
+
for (const a of byParam.values()) {
|
|
4887
|
+
for (const id of collectAbsFreeVars(a)) free.add(id);
|
|
4888
|
+
}
|
|
4889
|
+
const retAbs = hof.symbolic ?? fn.combinedAbs;
|
|
4890
|
+
if (retAbs) {
|
|
4891
|
+
for (const id of collectAbsFreeVars(retAbs)) free.add(id);
|
|
4892
|
+
}
|
|
4893
|
+
if (free.size === 0) return void 0;
|
|
4894
|
+
const typeVars = /* @__PURE__ */ new Map();
|
|
4895
|
+
const used = /* @__PURE__ */ new Set();
|
|
4896
|
+
const typeParams = [];
|
|
4897
|
+
for (const id of [...free].sort()) {
|
|
4898
|
+
let n = tsTypeParamName(id);
|
|
4899
|
+
if (used.has(n) || TS_PARAM_RESERVED.has(n)) {
|
|
4900
|
+
let i = 2;
|
|
4901
|
+
while (used.has(`${n}${i}`)) i++;
|
|
4902
|
+
n = `${n}${i}`;
|
|
4903
|
+
}
|
|
4904
|
+
used.add(n);
|
|
4905
|
+
typeVars.set(id, n);
|
|
4906
|
+
typeParams.push(n);
|
|
4907
|
+
}
|
|
4908
|
+
const params = [];
|
|
4909
|
+
const paramNames = [];
|
|
4910
|
+
const paramTypes = [];
|
|
4911
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
4912
|
+
for (let i = 0; i < arity; i++) {
|
|
4913
|
+
const rawName = getParamName(fn, i);
|
|
4914
|
+
const isRest = rawName.startsWith("...");
|
|
4915
|
+
let name = sanitizeParamName(rawName, i);
|
|
4916
|
+
if (usedNames.has(name)) {
|
|
4917
|
+
let n = 2;
|
|
4918
|
+
while (usedNames.has(`${name}${n}`)) n++;
|
|
4919
|
+
name = isRest && name.startsWith("...") ? `...${name.slice(3)}${n}` : `${name}${n}`;
|
|
4920
|
+
}
|
|
4921
|
+
usedNames.add(name);
|
|
4922
|
+
const promoted = byParam.get(fn.paramNames[i] ?? rawName) ?? byParam.get(rawName);
|
|
4923
|
+
const typeStr = promoted ? absToTSType(promoted, typeVars) : isRest ? "unknown[]" : "unknown";
|
|
4924
|
+
params.push(`${name}: ${typeStr}`);
|
|
4925
|
+
paramNames.push(name);
|
|
4926
|
+
paramTypes.push(typeStr);
|
|
4927
|
+
}
|
|
4928
|
+
const returnType = retAbs ? absToTSType(retAbs, typeVars) : "unknown";
|
|
4929
|
+
return { typeParams, params, paramNames, paramTypes, returnType };
|
|
4930
|
+
}
|
|
4101
4931
|
function generateFunctionDtsLines(fn) {
|
|
4102
|
-
|
|
4932
|
+
let emitFn = fn;
|
|
4933
|
+
if (fn.noDeclaration) {
|
|
4934
|
+
const m = /^([A-Za-z_$][\w$]*)\.([A-Za-z_$][\w$]*)$/.exec(fn.name);
|
|
4935
|
+
if (!m) return [];
|
|
4936
|
+
emitFn = { ...fn, name: `${m[1]}_${m[2]}`, noDeclaration: false };
|
|
4937
|
+
}
|
|
4938
|
+
fn = emitFn;
|
|
4939
|
+
const hofSig = computeHofSignature(fn);
|
|
4940
|
+
if (hofSig) {
|
|
4941
|
+
const lines2 = [];
|
|
4942
|
+
if (fn.cases.length > 0) {
|
|
4943
|
+
const jsdoc2 = generateJSDoc(fn, {
|
|
4944
|
+
params: hofSig.params,
|
|
4945
|
+
paramNames: hofSig.paramNames,
|
|
4946
|
+
paramTypes: hofSig.paramTypes,
|
|
4947
|
+
returnType: hofSig.returnType
|
|
4948
|
+
});
|
|
4949
|
+
if (jsdoc2) lines2.push(jsdoc2);
|
|
4950
|
+
}
|
|
4951
|
+
const tparams = hofSig.typeParams.length > 0 ? `<${hofSig.typeParams.join(", ")}>` : "";
|
|
4952
|
+
lines2.push(
|
|
4953
|
+
`export declare function ${fn.name}${tparams}(${hofSig.params.join(", ")}): ${hofSig.returnType};`
|
|
4954
|
+
);
|
|
4955
|
+
return lines2;
|
|
4956
|
+
}
|
|
4103
4957
|
if (fn.cases.length === 0) {
|
|
4104
4958
|
const retAbs = fn.combinedAbs;
|
|
4105
4959
|
if (retAbs) {
|
|
@@ -4108,7 +4962,6 @@ function generateFunctionDtsLines(fn) {
|
|
|
4108
4962
|
];
|
|
4109
4963
|
}
|
|
4110
4964
|
return [];
|
|
4111
|
-
return [];
|
|
4112
4965
|
}
|
|
4113
4966
|
const sig = computeMainSignature(fn);
|
|
4114
4967
|
const jsdoc = generateJSDoc(fn, sig);
|
|
@@ -4539,8 +5392,8 @@ function unifiedDiff(a, b, path) {
|
|
|
4539
5392
|
}
|
|
4540
5393
|
|
|
4541
5394
|
// src/interface-surface.ts
|
|
4542
|
-
import { readFileSync as
|
|
4543
|
-
import { dirname as
|
|
5395
|
+
import { readFileSync as readFileSync7, existsSync as existsSync9 } from "fs";
|
|
5396
|
+
import { dirname as dirname11, resolve as resolve7 } from "path";
|
|
4544
5397
|
import {
|
|
4545
5398
|
effectiveInterface,
|
|
4546
5399
|
formatConstraint,
|
|
@@ -4548,9 +5401,13 @@ import {
|
|
|
4548
5401
|
interfaceDiagCount,
|
|
4549
5402
|
localNamedExports,
|
|
4550
5403
|
refineDiagCount,
|
|
5404
|
+
sidecarPathOf as sidecarPathOf2,
|
|
4551
5405
|
takeInterfaceDiagsSince,
|
|
4552
5406
|
takeRefineDiagsSince
|
|
4553
5407
|
} from "@nudojs/core";
|
|
5408
|
+
function collectDepContents(filePath, source, loadModule) {
|
|
5409
|
+
return collectLoadDepContents(filePath, source, loadModule ?? defaultLoadModule);
|
|
5410
|
+
}
|
|
4554
5411
|
function formatInterfaceSurfaceLine(e) {
|
|
4555
5412
|
const params = `(${e.params.map((p) => `${p.name}: ${p.display}`).join(", ")})`;
|
|
4556
5413
|
let line = ` ${e.fn} [${e.source}] ${params}`;
|
|
@@ -4565,19 +5422,92 @@ function absToImplicitDisplay(a) {
|
|
|
4565
5422
|
return "unknown";
|
|
4566
5423
|
}
|
|
4567
5424
|
}
|
|
5425
|
+
function constraintToJson(c) {
|
|
5426
|
+
return JSON.parse(JSON.stringify(c));
|
|
5427
|
+
}
|
|
5428
|
+
function cachedToEffective(e) {
|
|
5429
|
+
return {
|
|
5430
|
+
fnName: e.fnName,
|
|
5431
|
+
params: e.params.map((p) => ({
|
|
5432
|
+
param: p.param,
|
|
5433
|
+
constraint: p.constraint
|
|
5434
|
+
})),
|
|
5435
|
+
...e.returns ? { returns: { constraint: e.returns.constraint } } : {},
|
|
5436
|
+
source: e.source,
|
|
5437
|
+
...e.conflict ? { conflict: e.conflict } : {}
|
|
5438
|
+
};
|
|
5439
|
+
}
|
|
4568
5440
|
async function interfaceSurface(filePath, opts = {}) {
|
|
4569
|
-
const abs =
|
|
4570
|
-
const source =
|
|
5441
|
+
const abs = resolve7(filePath);
|
|
5442
|
+
const source = opts.source ?? readFileSync7(abs, "utf-8");
|
|
5443
|
+
const fromBuffer = opts.source !== void 0;
|
|
4571
5444
|
const ifaceSince = interfaceDiagCount();
|
|
4572
5445
|
const refineSince = refineDiagCount();
|
|
4573
|
-
const
|
|
5446
|
+
const proj = findProjectConfig(dirname11(abs));
|
|
5447
|
+
const autoBind = opts.autoBind ?? interfaceConfig(proj?.config).autoBind;
|
|
4574
5448
|
const loadModule = opts.loadModule ?? defaultLoadModule;
|
|
4575
5449
|
const exported = localNamedExports(source);
|
|
4576
5450
|
const kindOf = (fnName) => exported.has(fnName) ? "export" : "local";
|
|
4577
|
-
const analysis = await analyzeFileAsync(abs, source, void 0, opts.records);
|
|
5451
|
+
const analysis = await analyzeFileAsync(abs, source, void 0, opts.records, loadModule);
|
|
5452
|
+
let disk;
|
|
5453
|
+
let ifaceKey;
|
|
5454
|
+
let cachedTable;
|
|
5455
|
+
if (!opts.loadModule && !opts.records && !fromBuffer) {
|
|
5456
|
+
const cacheRoot = diskCacheRoot(proj?.config, proj?.projectDir);
|
|
5457
|
+
disk = new DiskCache({ root: cacheRoot, namespace: "iface" });
|
|
5458
|
+
if (disk.enabled) {
|
|
5459
|
+
let sidecarSource;
|
|
5460
|
+
try {
|
|
5461
|
+
const sc = sidecarPathOf2(abs);
|
|
5462
|
+
if (autoBind !== false) {
|
|
5463
|
+
const openSc = loadModule(`./${sc.slice(sc.lastIndexOf("/") + 1)}`, abs);
|
|
5464
|
+
if (openSc !== void 0) sidecarSource = openSc;
|
|
5465
|
+
else if (existsSync9(sc)) sidecarSource = readFileSync7(sc, "utf-8");
|
|
5466
|
+
}
|
|
5467
|
+
} catch {
|
|
5468
|
+
sidecarSource = void 0;
|
|
5469
|
+
}
|
|
5470
|
+
const dep = collectDepContents(abs, source, loadModule);
|
|
5471
|
+
const hasBareMiss = (dep.depContents ?? []).some((d) => d.content == null);
|
|
5472
|
+
if (dep.truncated || hasBareMiss) {
|
|
5473
|
+
ifaceKey = void 0;
|
|
5474
|
+
cachedTable = void 0;
|
|
5475
|
+
} else {
|
|
5476
|
+
ifaceKey = ifaceCacheKey(abs, source, {
|
|
5477
|
+
autoBind: autoBind !== false,
|
|
5478
|
+
projectDir: proj?.projectDir,
|
|
5479
|
+
sidecarSource,
|
|
5480
|
+
depContents: dep.depContents,
|
|
5481
|
+
projectEnvNames: proj?.config.env ?? []
|
|
5482
|
+
});
|
|
5483
|
+
cachedTable = disk.get(ifaceKey);
|
|
5484
|
+
}
|
|
5485
|
+
}
|
|
5486
|
+
}
|
|
4578
5487
|
const entries = [];
|
|
5488
|
+
const freshTable = { fns: {} };
|
|
5489
|
+
const useCache = cachedTable !== void 0;
|
|
4579
5490
|
for (const fn of analysis.functions) {
|
|
4580
|
-
|
|
5491
|
+
let eff;
|
|
5492
|
+
if (useCache) {
|
|
5493
|
+
const hit = cachedTable.fns[fn.name];
|
|
5494
|
+
eff = hit === null ? void 0 : hit ? cachedToEffective(hit) : void 0;
|
|
5495
|
+
} else {
|
|
5496
|
+
eff = effectiveInterface(source, fn.name, { loadModule, fromFile: abs, autoBind });
|
|
5497
|
+
if (freshTable) {
|
|
5498
|
+
freshTable.fns[fn.name] = eff ? {
|
|
5499
|
+
fnName: eff.fnName,
|
|
5500
|
+
params: eff.params.map((p) => ({
|
|
5501
|
+
param: p.param,
|
|
5502
|
+
constraint: constraintToJson(p.constraint)
|
|
5503
|
+
})),
|
|
5504
|
+
...eff.returns ? { returns: { constraint: constraintToJson(eff.returns.constraint) } } : {},
|
|
5505
|
+
// 磁盘表保留真实分档;implicit 也可序列化(展示层用)
|
|
5506
|
+
source: eff.source,
|
|
5507
|
+
...eff.conflict ? { conflict: eff.conflict } : {}
|
|
5508
|
+
} : null;
|
|
5509
|
+
}
|
|
5510
|
+
}
|
|
4581
5511
|
if (eff) {
|
|
4582
5512
|
entries.push({
|
|
4583
5513
|
fn: fn.name,
|
|
@@ -4608,14 +5538,20 @@ async function interfaceSurface(filePath, opts = {}) {
|
|
|
4608
5538
|
}
|
|
4609
5539
|
entries.push({ fn: fn.name, kind: kindOf(fn.name), source: "implicit", params, returns: ret });
|
|
4610
5540
|
}
|
|
5541
|
+
if (disk?.enabled && ifaceKey && !useCache && analysis.functions.length > 0) {
|
|
5542
|
+
try {
|
|
5543
|
+
disk.set(ifaceKey, freshTable);
|
|
5544
|
+
} catch {
|
|
5545
|
+
}
|
|
5546
|
+
}
|
|
4611
5547
|
takeInterfaceDiagsSince(ifaceSince);
|
|
4612
5548
|
takeRefineDiagsSince(refineSince);
|
|
4613
5549
|
return entries;
|
|
4614
5550
|
}
|
|
4615
5551
|
|
|
4616
5552
|
// src/interface-emitter.ts
|
|
4617
|
-
import { existsSync as
|
|
4618
|
-
import { basename as basename2, dirname as
|
|
5553
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync2, renameSync, unlinkSync } from "fs";
|
|
5554
|
+
import { basename as basename2, dirname as dirname12, relative as relative2, resolve as resolve8 } from "path";
|
|
4619
5555
|
import {
|
|
4620
5556
|
execNudoModule,
|
|
4621
5557
|
formatConstraint as formatConstraint2,
|
|
@@ -4626,20 +5562,20 @@ import {
|
|
|
4626
5562
|
localNamedExports as localNamedExports2,
|
|
4627
5563
|
parseSource as parseSource2,
|
|
4628
5564
|
refineDiagCount as refineDiagCount2,
|
|
4629
|
-
sidecarPathOf,
|
|
5565
|
+
sidecarPathOf as sidecarPathOf3,
|
|
4630
5566
|
takeInterfaceDiagsSince as takeInterfaceDiagsSince2,
|
|
4631
5567
|
takeRefineDiagsSince as takeRefineDiagsSince2
|
|
4632
5568
|
} from "@nudojs/core";
|
|
4633
5569
|
import { randomBytes } from "crypto";
|
|
4634
5570
|
var GENERATED_HEADER = "// @generated by nudo \u2014 do not edit; regenerate with `nudo interface --emit`";
|
|
4635
5571
|
async function emitInterface(filePath, opts) {
|
|
4636
|
-
const abs =
|
|
4637
|
-
if (isNodeModulesPath(abs) || isNodeModulesPath(
|
|
5572
|
+
const abs = resolve8(filePath);
|
|
5573
|
+
if (isNodeModulesPath(abs) || isNodeModulesPath(sidecarPathOf3(abs))) {
|
|
4638
5574
|
throw new Error(
|
|
4639
5575
|
`emit target '${abs}' is inside node_modules; contract sidecars are never written there`
|
|
4640
5576
|
);
|
|
4641
5577
|
}
|
|
4642
|
-
const proj = findProjectConfig(
|
|
5578
|
+
const proj = findProjectConfig(dirname12(abs));
|
|
4643
5579
|
const allow = interfaceConfig(proj?.config).emit;
|
|
4644
5580
|
if (!matchesEmitAllowlist(abs, proj?.projectDir, allow)) {
|
|
4645
5581
|
return {
|
|
@@ -4650,28 +5586,34 @@ async function emitInterface(filePath, opts) {
|
|
|
4650
5586
|
{
|
|
4651
5587
|
code: "nudo:interface-emit-denied",
|
|
4652
5588
|
severity: "warning",
|
|
4653
|
-
message: `emit target '${
|
|
5589
|
+
message: `emit target '${relative2(process.cwd(), abs) || abs}' is outside package.json#nudo.interface.emit allowlist`
|
|
4654
5590
|
}
|
|
4655
5591
|
],
|
|
4656
|
-
sidecarPath:
|
|
5592
|
+
sidecarPath: sidecarPathOf3(abs)
|
|
4657
5593
|
};
|
|
4658
5594
|
}
|
|
4659
|
-
const source =
|
|
5595
|
+
const source = opts.source ?? readFileSync8(abs, "utf-8");
|
|
4660
5596
|
const ifaceSince = interfaceDiagCount2();
|
|
4661
5597
|
const refineSince = refineDiagCount2();
|
|
4662
|
-
const sidecarPath =
|
|
4663
|
-
const sidecarSrc =
|
|
4664
|
-
const srcRel =
|
|
5598
|
+
const sidecarPath = sidecarPathOf3(abs);
|
|
5599
|
+
const sidecarSrc = existsSync10(sidecarPath) ? readFileSync8(sidecarPath, "utf-8") : "";
|
|
5600
|
+
const srcRel = relative2(dirname12(sidecarPath), abs) || basename2(abs);
|
|
4665
5601
|
const sections = collectGeneratedSections(sidecarSrc);
|
|
4666
5602
|
const generatedNames = new Set(sections.flatMap((s) => s.names));
|
|
4667
5603
|
const declared = topLevelDeclaredNames(sidecarSrc);
|
|
4668
5604
|
if (declared === void 0) {
|
|
4669
5605
|
throw new Error(
|
|
4670
|
-
`sidecar '${
|
|
5606
|
+
`sidecar '${relative2(process.cwd(), sidecarPath) || sidecarPath}' is not parseable; fix it before emitting`
|
|
4671
5607
|
);
|
|
4672
5608
|
}
|
|
4673
5609
|
for (const n of generatedNames) declared.delete(n);
|
|
4674
|
-
const analysis = await analyzeFileAsync(
|
|
5610
|
+
const analysis = await analyzeFileAsync(
|
|
5611
|
+
abs,
|
|
5612
|
+
source,
|
|
5613
|
+
void 0,
|
|
5614
|
+
opts.records,
|
|
5615
|
+
opts.loadModule
|
|
5616
|
+
);
|
|
4675
5617
|
const exported = localNamedExports2(source);
|
|
4676
5618
|
const fnByName = /* @__PURE__ */ new Map();
|
|
4677
5619
|
for (const f of analysis.functions) {
|
|
@@ -4774,15 +5716,15 @@ async function emitInterface(filePath, opts) {
|
|
|
4774
5716
|
finalContent = joinSections(base, [...preserved, ...accepted.map((a) => a.text)]);
|
|
4775
5717
|
}
|
|
4776
5718
|
const changed = finalContent !== sidecarSrc;
|
|
4777
|
-
const diff = changed ? unifiedDiff(sidecarSrc, finalContent,
|
|
5719
|
+
const diff = changed ? unifiedDiff(sidecarSrc, finalContent, relative2(process.cwd(), sidecarPath) || sidecarPath) : void 0;
|
|
4778
5720
|
if (changed && !opts.dryRun) {
|
|
4779
5721
|
const tmp = `${sidecarPath}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
4780
5722
|
try {
|
|
4781
|
-
|
|
5723
|
+
writeFileSync2(tmp, finalContent, "utf-8");
|
|
4782
5724
|
renameSync(tmp, sidecarPath);
|
|
4783
5725
|
} catch (e) {
|
|
4784
5726
|
try {
|
|
4785
|
-
if (
|
|
5727
|
+
if (existsSync10(tmp)) unlinkSync(tmp);
|
|
4786
5728
|
} catch {
|
|
4787
5729
|
}
|
|
4788
5730
|
throw e;
|
|
@@ -4972,29 +5914,638 @@ function joinSections(base, sectionTexts) {
|
|
|
4972
5914
|
return lead + normalized.join("\n");
|
|
4973
5915
|
}
|
|
4974
5916
|
|
|
4975
|
-
// src/interface-
|
|
4976
|
-
import { existsSync as
|
|
4977
|
-
import { basename as basename3, dirname as
|
|
5917
|
+
// src/interface-draft.ts
|
|
5918
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync3, realpathSync as realpathSync2 } from "fs";
|
|
5919
|
+
import { basename as basename3, dirname as dirname13, isAbsolute as isAbsolute2, join as join6, relative as relative3 } from "path";
|
|
5920
|
+
import {
|
|
5921
|
+
effectiveInterface as effectiveInterface2,
|
|
5922
|
+
formatConstraint as formatConstraint3,
|
|
5923
|
+
formatShape as formatShape6,
|
|
5924
|
+
generalizeFromAst as generalizeFromAst4,
|
|
5925
|
+
isIntFlag,
|
|
5926
|
+
joinThenProject as joinThenProject2,
|
|
5927
|
+
localNamedExports as localNamedExports3,
|
|
5928
|
+
sidecarPathOf as sidecarPathOf4
|
|
5929
|
+
} from "@nudojs/core";
|
|
4978
5930
|
import { parse as parse9 } from "@nudojs/parser";
|
|
5931
|
+
function collectParamBodyAccesses(source) {
|
|
5932
|
+
const out = /* @__PURE__ */ new Map();
|
|
5933
|
+
let ast;
|
|
5934
|
+
try {
|
|
5935
|
+
ast = parse9(source);
|
|
5936
|
+
} catch {
|
|
5937
|
+
return out;
|
|
5938
|
+
}
|
|
5939
|
+
const keyOf = (node) => {
|
|
5940
|
+
if (node.type === "Identifier") return node.name;
|
|
5941
|
+
if (node.type === "StringLiteral") return node.value;
|
|
5942
|
+
return void 0;
|
|
5943
|
+
};
|
|
5944
|
+
const visitFn = (fnName, fnNode, paramNames) => {
|
|
5945
|
+
if (paramNames.size === 0) return;
|
|
5946
|
+
const byParam = /* @__PURE__ */ new Map();
|
|
5947
|
+
const walk = (node, shadowed) => {
|
|
5948
|
+
if (!node || typeof node !== "object") return;
|
|
5949
|
+
const n = node;
|
|
5950
|
+
if ((n.type === "VariableDeclarator" || n.type === "FunctionDeclaration") && n.id?.type === "Identifier") {
|
|
5951
|
+
const id = n.id.name;
|
|
5952
|
+
if (paramNames.has(id)) {
|
|
5953
|
+
shadowed = new Set(shadowed).add(id);
|
|
5954
|
+
}
|
|
5955
|
+
}
|
|
5956
|
+
if (n.type === "MemberExpression" || n.type === "OptionalMemberExpression") {
|
|
5957
|
+
const obj = n.object;
|
|
5958
|
+
const prop = n.property;
|
|
5959
|
+
const computed = n.computed === true;
|
|
5960
|
+
if (obj?.type === "Identifier" && paramNames.has(obj.name) && !shadowed.has(obj.name) && prop && !computed) {
|
|
5961
|
+
const key = keyOf(prop);
|
|
5962
|
+
const pname = obj.name;
|
|
5963
|
+
if (key !== void 0) {
|
|
5964
|
+
if (!byParam.has(pname)) byParam.set(pname, /* @__PURE__ */ new Set());
|
|
5965
|
+
byParam.get(pname).add(key);
|
|
5966
|
+
}
|
|
5967
|
+
}
|
|
5968
|
+
}
|
|
5969
|
+
for (const k of Object.keys(n)) {
|
|
5970
|
+
if (k === "loc" || k === "start" || k === "end") continue;
|
|
5971
|
+
const child = n[k];
|
|
5972
|
+
if (Array.isArray(child)) {
|
|
5973
|
+
for (const item of child) walk(item, shadowed);
|
|
5974
|
+
} else if (child && typeof child === "object") {
|
|
5975
|
+
walk(child, shadowed);
|
|
5976
|
+
}
|
|
5977
|
+
}
|
|
5978
|
+
};
|
|
5979
|
+
walk(fnNode, /* @__PURE__ */ new Set());
|
|
5980
|
+
if (byParam.size > 0) out.set(fnName, byParam);
|
|
5981
|
+
};
|
|
5982
|
+
const paramSet = (fnNode) => {
|
|
5983
|
+
const names = /* @__PURE__ */ new Set();
|
|
5984
|
+
const params = fnNode.params ?? [];
|
|
5985
|
+
for (const p of params) {
|
|
5986
|
+
if (!p) continue;
|
|
5987
|
+
if (p.type === "Identifier") names.add(p.name);
|
|
5988
|
+
else if (p.type === "AssignmentPattern" && p.left?.type === "Identifier") {
|
|
5989
|
+
names.add(p.left.name);
|
|
5990
|
+
} else if (p.type === "RestElement" && p.argument?.type === "Identifier") {
|
|
5991
|
+
names.add(p.argument.name);
|
|
5992
|
+
} else if (p.type === "ObjectPattern") {
|
|
5993
|
+
for (const prop of p.properties ?? []) {
|
|
5994
|
+
if (prop.type === "ObjectProperty") {
|
|
5995
|
+
const v = prop.value;
|
|
5996
|
+
if (v.type === "Identifier") names.add(v.name);
|
|
5997
|
+
else if (v.type === "AssignmentPattern" && v.left?.type === "Identifier") {
|
|
5998
|
+
names.add(v.left.name);
|
|
5999
|
+
}
|
|
6000
|
+
}
|
|
6001
|
+
}
|
|
6002
|
+
}
|
|
6003
|
+
}
|
|
6004
|
+
return names;
|
|
6005
|
+
};
|
|
6006
|
+
const visitClassMethods = (className, classNode) => {
|
|
6007
|
+
const body = classNode.body?.body ?? [];
|
|
6008
|
+
for (const m of body) {
|
|
6009
|
+
const mem = m;
|
|
6010
|
+
const isMethod = mem.type === "MethodDefinition" || mem.type === "ClassMethod" || mem.type === "TSDeclareMethod";
|
|
6011
|
+
if (!isMethod || mem.static) continue;
|
|
6012
|
+
if (mem.kind && mem.kind !== "method") continue;
|
|
6013
|
+
const keyName = mem.key?.type === "Identifier" ? mem.key.name : void 0;
|
|
6014
|
+
if (!keyName) continue;
|
|
6015
|
+
const methodNode = mem.type === "MethodDefinition" ? mem.value : mem;
|
|
6016
|
+
if (!methodNode) continue;
|
|
6017
|
+
visitFn(`${className}.${keyName}`, methodNode, paramSet(methodNode));
|
|
6018
|
+
}
|
|
6019
|
+
};
|
|
6020
|
+
const considerDecl = (decl, exported) => {
|
|
6021
|
+
if (!decl) return;
|
|
6022
|
+
if (decl.type === "FunctionDeclaration" && decl.id) {
|
|
6023
|
+
const id = decl.id;
|
|
6024
|
+
visitFn(id.name, decl, paramSet(decl));
|
|
6025
|
+
return;
|
|
6026
|
+
}
|
|
6027
|
+
if (decl.type === "ClassDeclaration" && decl.id?.name) {
|
|
6028
|
+
visitClassMethods(decl.id.name, decl);
|
|
6029
|
+
return;
|
|
6030
|
+
}
|
|
6031
|
+
if (decl.type === "VariableDeclaration") {
|
|
6032
|
+
for (const d of decl.declarations ?? []) {
|
|
6033
|
+
const id = d.id;
|
|
6034
|
+
const init = d.init;
|
|
6035
|
+
if (exported && id?.type === "Identifier" && init && (init.type === "ArrowFunctionExpression" || init.type === "FunctionExpression")) {
|
|
6036
|
+
visitFn(id.name, init, paramSet(init));
|
|
6037
|
+
}
|
|
6038
|
+
}
|
|
6039
|
+
}
|
|
6040
|
+
};
|
|
6041
|
+
const program = ast.program;
|
|
6042
|
+
const bodyStmts = program?.body ?? [];
|
|
6043
|
+
for (const stmt of bodyStmts) {
|
|
6044
|
+
if (stmt.type === "ExportNamedDeclaration") {
|
|
6045
|
+
considerDecl(stmt.declaration, true);
|
|
6046
|
+
for (const spec of stmt.specifiers ?? []) {
|
|
6047
|
+
const local = spec.local;
|
|
6048
|
+
if (local?.type !== "Identifier") continue;
|
|
6049
|
+
for (const s2 of bodyStmts) {
|
|
6050
|
+
if (s2.type === "ClassDeclaration" && s2.id?.name === local.name) {
|
|
6051
|
+
visitClassMethods(local.name, s2);
|
|
6052
|
+
}
|
|
6053
|
+
}
|
|
6054
|
+
}
|
|
6055
|
+
} else if (stmt.type === "ExportDefaultDeclaration") {
|
|
6056
|
+
considerDecl(stmt.declaration, true);
|
|
6057
|
+
} else if (stmt.type === "ClassDeclaration") {
|
|
6058
|
+
const id = stmt.id;
|
|
6059
|
+
if (id?.name) visitClassMethods(id.name, stmt);
|
|
6060
|
+
} else if (stmt.type === "FunctionDeclaration") {
|
|
6061
|
+
}
|
|
6062
|
+
}
|
|
6063
|
+
return out;
|
|
6064
|
+
}
|
|
6065
|
+
function caseEvidence(fn) {
|
|
6066
|
+
const callsite = fn.cases.filter((c) => c.source === "callsite");
|
|
6067
|
+
const directive = fn.cases.filter((c) => c.source === "directive");
|
|
6068
|
+
return {
|
|
6069
|
+
paramCases: callsite.length > 0 ? callsite : directive,
|
|
6070
|
+
returnCases: callsite.length > 0 ? callsite : directive.length > 0 ? directive : fn.cases,
|
|
6071
|
+
paramEvidence: callsite.length > 0 ? "callsite" : directive.length > 0 ? "directive" : "none",
|
|
6072
|
+
rawReturnEvidence: callsite.length > 0 ? "callsite" : directive.length > 0 ? "directive" : "none"
|
|
6073
|
+
};
|
|
6074
|
+
}
|
|
6075
|
+
function widenDraftConstraint(c) {
|
|
6076
|
+
const stripEqLits = (preds2) => preds2.filter((p) => !(p.op === "eq" && p.b?.op === "lit"));
|
|
6077
|
+
if (c.members && c.members.length > 0) {
|
|
6078
|
+
const widenedMembers = c.members.map(widenDraftConstraint).filter((m) => m !== void 0);
|
|
6079
|
+
if (widenedMembers.length === 0) {
|
|
6080
|
+
const prim = litPrimOf(c.members[0]);
|
|
6081
|
+
if (!prim) return void 0;
|
|
6082
|
+
return { __nudoConstraint: true, prim, preds: [] };
|
|
6083
|
+
}
|
|
6084
|
+
const prims = new Set(widenedMembers.map((m) => m.prim).filter(Boolean));
|
|
6085
|
+
if (prims.size === 1 && widenedMembers.every((m) => !m.fields && !m.element && !m.members)) {
|
|
6086
|
+
return { __nudoConstraint: true, prim: [...prims][0], preds: [] };
|
|
6087
|
+
}
|
|
6088
|
+
return {
|
|
6089
|
+
__nudoConstraint: true,
|
|
6090
|
+
...c.prim ? { prim: c.prim } : {},
|
|
6091
|
+
preds: stripEqLits(c.preds ?? []),
|
|
6092
|
+
members: widenedMembers
|
|
6093
|
+
};
|
|
6094
|
+
}
|
|
6095
|
+
if (c.fields) {
|
|
6096
|
+
const fields = {};
|
|
6097
|
+
for (const [k, f] of Object.entries(c.fields)) {
|
|
6098
|
+
const w = widenDraftConstraint(f.constraint);
|
|
6099
|
+
if (!w) continue;
|
|
6100
|
+
fields[k] = { constraint: w, ...f.optional ? { optional: true } : {} };
|
|
6101
|
+
}
|
|
6102
|
+
return {
|
|
6103
|
+
__nudoConstraint: true,
|
|
6104
|
+
fields,
|
|
6105
|
+
preds: stripEqLits(c.preds ?? [])
|
|
6106
|
+
};
|
|
6107
|
+
}
|
|
6108
|
+
if (c.element) {
|
|
6109
|
+
const el = widenDraftConstraint(c.element);
|
|
6110
|
+
return {
|
|
6111
|
+
__nudoConstraint: true,
|
|
6112
|
+
preds: stripEqLits(c.preds ?? []),
|
|
6113
|
+
...el ? { element: el } : {}
|
|
6114
|
+
};
|
|
6115
|
+
}
|
|
6116
|
+
if (c.fn) return void 0;
|
|
6117
|
+
const preds = stripEqLits(c.preds ?? []);
|
|
6118
|
+
const hasBounds = preds.length > 0;
|
|
6119
|
+
if (!c.prim && !hasBounds && !isIntFlag(c)) return void 0;
|
|
6120
|
+
return {
|
|
6121
|
+
__nudoConstraint: true,
|
|
6122
|
+
...c.prim ? { prim: c.prim } : {},
|
|
6123
|
+
preds,
|
|
6124
|
+
// builder 上 .int 是链式方法,truthy 恒真;必须经 isIntFlag
|
|
6125
|
+
...isIntFlag(c) ? { int: true } : {}
|
|
6126
|
+
};
|
|
6127
|
+
}
|
|
6128
|
+
function litPrimOf(c) {
|
|
6129
|
+
if (!c) return void 0;
|
|
6130
|
+
if (c.prim === "number" || c.prim === "string" || c.prim === "boolean") return c.prim;
|
|
6131
|
+
const eq = c.preds?.find(
|
|
6132
|
+
(p) => p.op === "eq" && p.b?.op === "lit"
|
|
6133
|
+
);
|
|
6134
|
+
const v = eq && eq.b?.op === "lit" ? eq.b.value : void 0;
|
|
6135
|
+
if (typeof v === "number") return "number";
|
|
6136
|
+
if (typeof v === "string") return "string";
|
|
6137
|
+
if (typeof v === "boolean") return "boolean";
|
|
6138
|
+
return c.members?.[0] ? litPrimOf(c.members[0]) : void 0;
|
|
6139
|
+
}
|
|
6140
|
+
function projectDraftParams(fn, paramCases, bodyByParam, formals) {
|
|
6141
|
+
const bodyFor = (name, index) => {
|
|
6142
|
+
if (!bodyByParam) return void 0;
|
|
6143
|
+
const hit = bodyByParam.get(name);
|
|
6144
|
+
if (hit) return hit;
|
|
6145
|
+
const formal = formals?.[index];
|
|
6146
|
+
if (formal && formal.kind === "pattern") {
|
|
6147
|
+
for (const b of formal.bound) {
|
|
6148
|
+
const boundHit = bodyByParam.get(b);
|
|
6149
|
+
if (boundHit) return boundHit;
|
|
6150
|
+
}
|
|
6151
|
+
}
|
|
6152
|
+
return bodyByParam.get(`_p${index}`);
|
|
6153
|
+
};
|
|
6154
|
+
return fn.paramNames.map((name, i) => {
|
|
6155
|
+
const bodyAccesses = bodyFor(name, i) ? [...bodyFor(name, i)].sort() : void 0;
|
|
6156
|
+
const argAbs = [];
|
|
6157
|
+
for (const c of paramCases) {
|
|
6158
|
+
const a = c.argAbs[i];
|
|
6159
|
+
if (a !== void 0) argAbs.push(a);
|
|
6160
|
+
}
|
|
6161
|
+
if (argAbs.length === 0) {
|
|
6162
|
+
if (bodyAccesses && bodyAccesses.length > 0) {
|
|
6163
|
+
return {
|
|
6164
|
+
name,
|
|
6165
|
+
display: `/* body-read { ${bodyAccesses.join(", ")} } \u2014 fill types when accepting */`,
|
|
6166
|
+
projected: false,
|
|
6167
|
+
bodyAccesses
|
|
6168
|
+
};
|
|
6169
|
+
}
|
|
6170
|
+
return { name, display: "/* no evidence \u2014 tighten */", projected: false };
|
|
6171
|
+
}
|
|
6172
|
+
const raw = joinThenProject2(argAbs);
|
|
6173
|
+
if (raw === void 0) {
|
|
6174
|
+
return {
|
|
6175
|
+
name,
|
|
6176
|
+
display: `/* not projectable: ${argAbs.map((a) => formatShape6(a)).join(" | ")} */`,
|
|
6177
|
+
projected: false,
|
|
6178
|
+
...bodyAccesses ? { bodyAccesses } : {}
|
|
6179
|
+
};
|
|
6180
|
+
}
|
|
6181
|
+
const constraint = widenDraftConstraint(raw);
|
|
6182
|
+
if (constraint === void 0) {
|
|
6183
|
+
const observed2 = formatConstraint3(raw);
|
|
6184
|
+
return {
|
|
6185
|
+
name,
|
|
6186
|
+
display: `/* observed: ${observed2} \u2014 widen/confirm before accepting */`,
|
|
6187
|
+
projected: false,
|
|
6188
|
+
...bodyAccesses ? { bodyAccesses } : {}
|
|
6189
|
+
};
|
|
6190
|
+
}
|
|
6191
|
+
let display = formatConstraint3(constraint);
|
|
6192
|
+
const observed = formatConstraint3(raw);
|
|
6193
|
+
if (observed !== display) {
|
|
6194
|
+
display += ` /* observed: ${observed} */`;
|
|
6195
|
+
}
|
|
6196
|
+
if (bodyAccesses && bodyAccesses.length > 0) {
|
|
6197
|
+
display += ` /* body also reads: ${bodyAccesses.join(", ")} */`;
|
|
6198
|
+
}
|
|
6199
|
+
return {
|
|
6200
|
+
name,
|
|
6201
|
+
constraint,
|
|
6202
|
+
display,
|
|
6203
|
+
projected: true,
|
|
6204
|
+
...bodyAccesses ? { bodyAccesses } : {}
|
|
6205
|
+
};
|
|
6206
|
+
});
|
|
6207
|
+
}
|
|
6208
|
+
function projectDraftReturn(fn, returnCases, source, rawEvidence, loadModule, fromFile) {
|
|
6209
|
+
const retAbs = [];
|
|
6210
|
+
for (const c of returnCases) {
|
|
6211
|
+
if (c.throwsAbs.shape.k !== "never") continue;
|
|
6212
|
+
retAbs.push(c.abs);
|
|
6213
|
+
}
|
|
6214
|
+
if (retAbs.length > 0) {
|
|
6215
|
+
const raw = joinThenProject2(retAbs);
|
|
6216
|
+
if (raw !== void 0) {
|
|
6217
|
+
if (rawEvidence === "none" || rawEvidence === "body") {
|
|
6218
|
+
return {
|
|
6219
|
+
display: `/* observed: ${formatConstraint3(raw)} \u2014 confirm before accepting */`,
|
|
6220
|
+
projected: false,
|
|
6221
|
+
evidence: rawEvidence === "none" ? "body" : rawEvidence
|
|
6222
|
+
};
|
|
6223
|
+
}
|
|
6224
|
+
const constraint = widenDraftConstraint(raw);
|
|
6225
|
+
if (constraint === void 0) {
|
|
6226
|
+
return {
|
|
6227
|
+
display: `/* observed: ${formatConstraint3(raw)} \u2014 widen/confirm */`,
|
|
6228
|
+
projected: false,
|
|
6229
|
+
evidence: rawEvidence
|
|
6230
|
+
};
|
|
6231
|
+
}
|
|
6232
|
+
return {
|
|
6233
|
+
constraint,
|
|
6234
|
+
display: formatConstraint3(constraint),
|
|
6235
|
+
projected: true,
|
|
6236
|
+
evidence: rawEvidence
|
|
6237
|
+
};
|
|
6238
|
+
}
|
|
6239
|
+
const shapeText = fn.combinedAbs ? formatShape6(fn.combinedAbs) : formatShape6(retAbs[0]);
|
|
6240
|
+
return {
|
|
6241
|
+
display: `/* not projectable: ${shapeText} */`,
|
|
6242
|
+
projected: false,
|
|
6243
|
+
evidence: rawEvidence
|
|
6244
|
+
};
|
|
6245
|
+
}
|
|
6246
|
+
try {
|
|
6247
|
+
const g = generalizeFromAst4(fn.name, source, {
|
|
6248
|
+
refine: {
|
|
6249
|
+
...loadModule ? { loadModule } : {},
|
|
6250
|
+
...fromFile ? { fromFile } : {}
|
|
6251
|
+
}
|
|
6252
|
+
});
|
|
6253
|
+
if (g?.symbolic) {
|
|
6254
|
+
return {
|
|
6255
|
+
display: `/* symbolic: ${formatShape6(g.symbolic)}${g.display ? ` \u2014 ${g.display}` : ""} */`,
|
|
6256
|
+
projected: false,
|
|
6257
|
+
evidence: "symbolic"
|
|
6258
|
+
};
|
|
6259
|
+
}
|
|
6260
|
+
} catch {
|
|
6261
|
+
}
|
|
6262
|
+
return { display: "/* no evidence */", projected: false, evidence: "none" };
|
|
6263
|
+
}
|
|
6264
|
+
function toDraftBuilderDsl(display) {
|
|
6265
|
+
const s = display.replace(/\s*\/\*[\s\S]*?\*\/\s*/g, "").trim();
|
|
6266
|
+
return s.replace(
|
|
6267
|
+
/([A-Za-z_$][\w$]*)\?\s*:\s*([^,}\n]+)/g,
|
|
6268
|
+
(_m, name, val) => {
|
|
6269
|
+
const v = val.trim();
|
|
6270
|
+
if (v.includes(".optional()")) return `${name}: ${v}`;
|
|
6271
|
+
return `${name}: ${v}.optional()`;
|
|
6272
|
+
}
|
|
6273
|
+
);
|
|
6274
|
+
}
|
|
6275
|
+
function draftExportName(fnName) {
|
|
6276
|
+
return fnName.includes(".") ? fnName.replace(/\./g, "_") : fnName;
|
|
6277
|
+
}
|
|
6278
|
+
function draftDsl(entry) {
|
|
6279
|
+
const parts = entry.params.filter((p) => p.projected && p.constraint !== void 0).map((p) => {
|
|
6280
|
+
const pure = toDraftBuilderDsl(formatConstraint3(p.constraint));
|
|
6281
|
+
return `${p.name}: ${pure}`;
|
|
6282
|
+
});
|
|
6283
|
+
const obj = parts.length === 0 ? "{}" : `{ ${parts.join(", ")} }`;
|
|
6284
|
+
const ret = entry.returns?.projected && entry.returns.constraint !== void 0 ? toDraftBuilderDsl(formatConstraint3(entry.returns.constraint)) : void 0;
|
|
6285
|
+
return ret === void 0 || ret === "" ? `fn(${obj})` : `fn(${obj}, ${ret})`;
|
|
6286
|
+
}
|
|
6287
|
+
function suggestedBodyDsl(fnName, params) {
|
|
6288
|
+
const withBody = params.filter((p) => p.bodyAccesses && p.bodyAccesses.length > 0 && !p.projected);
|
|
6289
|
+
if (withBody.length === 0) return void 0;
|
|
6290
|
+
const parts = withBody.map((p) => {
|
|
6291
|
+
const fields = p.bodyAccesses.map((k) => `${k}: /* TODO */`).join(", ");
|
|
6292
|
+
return `${p.name}: shape({ ${fields} })`;
|
|
6293
|
+
});
|
|
6294
|
+
return `// suggested (body-read, not a contract): ${fnName} = fn({ ${parts.join(", ")} })`;
|
|
6295
|
+
}
|
|
6296
|
+
async function draftInterface(filePath, opts = {}) {
|
|
6297
|
+
const source = opts.source ?? readFileSync9(filePath, "utf-8");
|
|
6298
|
+
const loadModule = opts.loadModule ?? defaultLoadModule;
|
|
6299
|
+
const sidecarPath = sidecarPathOf4(filePath);
|
|
6300
|
+
const wantBody = opts.bodyAccesses !== false;
|
|
6301
|
+
const analysis = await analyzeFileAsync(filePath, source, void 0, opts.records, loadModule);
|
|
6302
|
+
const exported = localNamedExports3(source);
|
|
6303
|
+
const selected = opts.fnNames && opts.fnNames.length > 0 ? new Set(opts.fnNames) : exported;
|
|
6304
|
+
const bodyMap = wantBody ? collectParamBodyAccesses(source) : /* @__PURE__ */ new Map();
|
|
6305
|
+
const entries = [];
|
|
6306
|
+
for (const fn of analysis.functions) {
|
|
6307
|
+
if (!selected.has(fn.name) && !opts.fnNames?.includes(fn.name)) continue;
|
|
6308
|
+
if (!exported.has(fn.name)) {
|
|
6309
|
+
if (opts.fnNames?.includes(fn.name)) {
|
|
6310
|
+
entries.push({
|
|
6311
|
+
fn: fn.name,
|
|
6312
|
+
params: fn.paramNames.map((n) => ({
|
|
6313
|
+
name: n,
|
|
6314
|
+
display: "/* not an export */",
|
|
6315
|
+
projected: false
|
|
6316
|
+
})),
|
|
6317
|
+
paramEvidence: "none",
|
|
6318
|
+
returnEvidence: "none",
|
|
6319
|
+
skipped: "not-an-export"
|
|
6320
|
+
});
|
|
6321
|
+
}
|
|
6322
|
+
continue;
|
|
6323
|
+
}
|
|
6324
|
+
const eff = effectiveInterface2(source, fn.name, {
|
|
6325
|
+
loadModule,
|
|
6326
|
+
fromFile: filePath,
|
|
6327
|
+
autoBind: true
|
|
6328
|
+
});
|
|
6329
|
+
if (eff?.source === "handwritten") {
|
|
6330
|
+
entries.push({
|
|
6331
|
+
fn: fn.name,
|
|
6332
|
+
params: eff.params.map((p) => ({
|
|
6333
|
+
name: p.param,
|
|
6334
|
+
constraint: p.constraint,
|
|
6335
|
+
display: formatConstraint3(p.constraint),
|
|
6336
|
+
projected: true
|
|
6337
|
+
})),
|
|
6338
|
+
...eff.returns ? {
|
|
6339
|
+
returns: {
|
|
6340
|
+
constraint: eff.returns.constraint,
|
|
6341
|
+
display: formatConstraint3(eff.returns.constraint),
|
|
6342
|
+
projected: true
|
|
6343
|
+
}
|
|
6344
|
+
} : {},
|
|
6345
|
+
paramEvidence: "none",
|
|
6346
|
+
returnEvidence: "none",
|
|
6347
|
+
skipped: "handwritten"
|
|
6348
|
+
});
|
|
6349
|
+
continue;
|
|
6350
|
+
}
|
|
6351
|
+
const { paramCases, returnCases, paramEvidence, rawReturnEvidence } = caseEvidence(fn);
|
|
6352
|
+
const bodyByParam = bodyMap.get(fn.name);
|
|
6353
|
+
const params = projectDraftParams(fn, paramCases, bodyByParam, fn.formals);
|
|
6354
|
+
const ret = projectDraftReturn(fn, returnCases, source, rawReturnEvidence, loadModule, filePath);
|
|
6355
|
+
const { evidence: returnEvidence, ...returns } = ret;
|
|
6356
|
+
let evidence = paramEvidence;
|
|
6357
|
+
if (evidence === "none") {
|
|
6358
|
+
const anyBody = params.some((p) => p.bodyAccesses && p.bodyAccesses.length > 0);
|
|
6359
|
+
if (anyBody) evidence = "body";
|
|
6360
|
+
}
|
|
6361
|
+
entries.push({
|
|
6362
|
+
fn: fn.name,
|
|
6363
|
+
params,
|
|
6364
|
+
returns,
|
|
6365
|
+
paramEvidence: evidence,
|
|
6366
|
+
returnEvidence,
|
|
6367
|
+
dsl: draftDsl({ params, returns })
|
|
6368
|
+
});
|
|
6369
|
+
}
|
|
6370
|
+
const draftSource = formatDraftModule(filePath, entries, sidecarPath);
|
|
6371
|
+
return { file: filePath, entries, draftSource, sidecarPath };
|
|
6372
|
+
}
|
|
6373
|
+
function formatDraftModule(filePath, entries, sidecarPath) {
|
|
6374
|
+
const target = sidecarPath ?? sidecarPathOf4(filePath);
|
|
6375
|
+
const draftName = sidecarDraftPath(filePath).split(/[/\\]/).pop() ?? "*.nudo.draft.js";
|
|
6376
|
+
const draftable = entries.filter((e) => e.dsl !== void 0 && e.skipped === void 0);
|
|
6377
|
+
const BUILDERS = ["fn", "number", "string", "boolean", "any", "shape", "array", "lit", "union"];
|
|
6378
|
+
const used = /* @__PURE__ */ new Set(["fn"]);
|
|
6379
|
+
for (const e of draftable) {
|
|
6380
|
+
const blob = `${e.dsl ?? ""}
|
|
6381
|
+
${suggestedBodyDsl(e.fn, e.params) ?? ""}`;
|
|
6382
|
+
for (const b of BUILDERS) {
|
|
6383
|
+
if (new RegExp(`\\b${b}\\b`).test(blob)) used.add(b);
|
|
6384
|
+
}
|
|
6385
|
+
}
|
|
6386
|
+
const importList = BUILDERS.filter((b) => used.has(b)).join(", ");
|
|
6387
|
+
const lines = [
|
|
6388
|
+
"// @nudo:draft",
|
|
6389
|
+
`// Generated by \`nudo interface --draft\` from ${filePath}`,
|
|
6390
|
+
`// This ${draftName} file is NOT loaded as a sidecar contract.`,
|
|
6391
|
+
`// Review each export, then copy it into ${target} to accept.`,
|
|
6392
|
+
"//",
|
|
6393
|
+
"// Evidence: callsite/directive = observed args; body = fields the",
|
|
6394
|
+
"// implementation reads (suggestion only \u2014 never a check obligation);",
|
|
6395
|
+
"// symbolic = generalize; omitted params = no evidence.",
|
|
6396
|
+
"// Handwritten contracts are never overwritten.",
|
|
6397
|
+
"",
|
|
6398
|
+
`import { ${importList} } from "@nudojs/core";`,
|
|
6399
|
+
""
|
|
6400
|
+
];
|
|
6401
|
+
if (draftable.length === 0) {
|
|
6402
|
+
lines.push("// (no draftable exports \u2014 handwritten / non-export / empty)");
|
|
6403
|
+
lines.push("");
|
|
6404
|
+
}
|
|
6405
|
+
for (const e of draftable) {
|
|
6406
|
+
lines.push(`// ${e.fn} \u2014 param: ${e.paramEvidence}, return: ${e.returnEvidence}`);
|
|
6407
|
+
for (const p of e.params) {
|
|
6408
|
+
if (!p.projected) {
|
|
6409
|
+
lines.push(`// ${p.name}: ${p.display}`);
|
|
6410
|
+
} else if (p.display.includes("/* observed") || p.display.includes("/* body also reads")) {
|
|
6411
|
+
lines.push(`// ${p.name}: ${p.display}`);
|
|
6412
|
+
}
|
|
6413
|
+
}
|
|
6414
|
+
const suggested = suggestedBodyDsl(e.fn, e.params);
|
|
6415
|
+
if (suggested) lines.push(suggested);
|
|
6416
|
+
if (e.returns && !e.returns.projected) {
|
|
6417
|
+
lines.push(`// returns: ${e.returns.display}`);
|
|
6418
|
+
} else if (e.returns?.display.includes("/* observed")) {
|
|
6419
|
+
lines.push(`// returns: ${e.returns.display}`);
|
|
6420
|
+
} else if (e.returnEvidence === "symbolic") {
|
|
6421
|
+
lines.push(`// returns: ${e.returns?.display ?? ""} (symbolic)`);
|
|
6422
|
+
}
|
|
6423
|
+
lines.push(`export const ${draftExportName(e.fn)} = ${e.dsl};`);
|
|
6424
|
+
if (e.fn.includes(".")) {
|
|
6425
|
+
lines.push(`// sidecar key may also be written as \`${e.fn}\` / nested { ${e.fn.split(".")[1]}: \u2026 }`);
|
|
6426
|
+
}
|
|
6427
|
+
lines.push("");
|
|
6428
|
+
}
|
|
6429
|
+
const skipped = entries.filter((e) => e.skipped !== void 0);
|
|
6430
|
+
if (skipped.length > 0) {
|
|
6431
|
+
lines.push("// Skipped:");
|
|
6432
|
+
for (const s of skipped) {
|
|
6433
|
+
lines.push(`// ${s.fn} (${s.skipped})`);
|
|
6434
|
+
}
|
|
6435
|
+
lines.push("");
|
|
6436
|
+
}
|
|
6437
|
+
return lines.join("\n");
|
|
6438
|
+
}
|
|
6439
|
+
function sidecarDraftPath(filePath) {
|
|
6440
|
+
return sidecarPathOf4(filePath).replace(/\.nudo\.([cm]?[jt]s)$/, ".nudo.draft.$1");
|
|
6441
|
+
}
|
|
6442
|
+
function safeRealpath(p) {
|
|
6443
|
+
try {
|
|
6444
|
+
return realpathSync2(p);
|
|
6445
|
+
} catch {
|
|
6446
|
+
try {
|
|
6447
|
+
return join6(realpathSync2(dirname13(p)), basename3(p));
|
|
6448
|
+
} catch {
|
|
6449
|
+
return p;
|
|
6450
|
+
}
|
|
6451
|
+
}
|
|
6452
|
+
}
|
|
6453
|
+
function isDraftableEntry(entries) {
|
|
6454
|
+
return entries.some((e) => e.dsl !== void 0 && e.skipped === void 0);
|
|
6455
|
+
}
|
|
6456
|
+
function writeInterfaceDraft(filePath, draftSource, opts = {}) {
|
|
6457
|
+
const draftPath = sidecarDraftPath(filePath);
|
|
6458
|
+
const formalPath = sidecarPathOf4(filePath);
|
|
6459
|
+
if (draftPath === formalPath) {
|
|
6460
|
+
throw new Error(
|
|
6461
|
+
`draft write refused: draft path equals formal sidecar (${formalPath}); never overwrite handwritten contracts`
|
|
6462
|
+
);
|
|
6463
|
+
}
|
|
6464
|
+
if (/[/\\]node_modules[/\\]/.test(draftPath) || /[/\\]node_modules[/\\]/.test(filePath)) {
|
|
6465
|
+
throw new Error(`draft write refused: path is inside node_modules (${draftPath})`);
|
|
6466
|
+
}
|
|
6467
|
+
if (opts.projectDir) {
|
|
6468
|
+
const rootReal = safeRealpath(opts.projectDir);
|
|
6469
|
+
const draftReal = safeRealpath(draftPath);
|
|
6470
|
+
const rel = relative3(rootReal, draftReal);
|
|
6471
|
+
if (rel.startsWith("..") || isAbsolute2(rel)) {
|
|
6472
|
+
throw new Error(`draft write refused: outside project root ${opts.projectDir}`);
|
|
6473
|
+
}
|
|
6474
|
+
}
|
|
6475
|
+
const draftable = opts.draftable !== void 0 ? opts.draftable : opts.entries !== void 0 ? isDraftableEntry(opts.entries) : /export\s+const\s+[\p{ID_Start}$_][\p{ID_Continue}$]*\s*=/u.test(draftSource);
|
|
6476
|
+
const prev = existsSync11(draftPath) ? readFileSync9(draftPath, "utf-8") : void 0;
|
|
6477
|
+
const changed = prev !== draftSource;
|
|
6478
|
+
const written = !opts.dryRun && changed && draftable;
|
|
6479
|
+
if (written) {
|
|
6480
|
+
writeFileSync3(draftPath, draftSource, "utf-8");
|
|
6481
|
+
}
|
|
6482
|
+
return {
|
|
6483
|
+
draftPath,
|
|
6484
|
+
written,
|
|
6485
|
+
changed,
|
|
6486
|
+
draftable,
|
|
6487
|
+
draftSource
|
|
6488
|
+
};
|
|
6489
|
+
}
|
|
6490
|
+
function formatDraftSummary(sourceRel, draftRel, result, write) {
|
|
6491
|
+
const lines = [sourceRel];
|
|
6492
|
+
for (const e of result.entries) {
|
|
6493
|
+
if (e.skipped === "handwritten") {
|
|
6494
|
+
lines.push(` ${e.fn} [handwritten] skipped (draft never overwrites)`);
|
|
6495
|
+
} else if (e.skipped === "not-an-export") {
|
|
6496
|
+
lines.push(` ${e.fn} [not-an-export] skipped`);
|
|
6497
|
+
} else if (e.dsl) {
|
|
6498
|
+
lines.push(` ${e.fn} [draft ${e.paramEvidence}/${e.returnEvidence}] ${e.dsl}`);
|
|
6499
|
+
}
|
|
6500
|
+
}
|
|
6501
|
+
if (write) {
|
|
6502
|
+
if (write.changed && write.draftable) {
|
|
6503
|
+
lines.push(
|
|
6504
|
+
write.written ? `Draft written \u2192 ${draftRel}` : `[dry-run] would write \u2192 ${draftRel}`
|
|
6505
|
+
);
|
|
6506
|
+
} else if (write.changed && !write.draftable) {
|
|
6507
|
+
if (result.entries.length > 0) {
|
|
6508
|
+
const skipped = result.entries.filter((e) => e.skipped !== void 0).length;
|
|
6509
|
+
lines.push(
|
|
6510
|
+
skipped > 0 ? `Draft not written (${skipped} skipped, no writeable exports); nothing written \u2192 ${draftRel}` : `Draft not written (no writeable exports); nothing written \u2192 ${draftRel}`
|
|
6511
|
+
);
|
|
6512
|
+
} else {
|
|
6513
|
+
lines.push(`Draft empty (no draftable exports); nothing written \u2192 ${draftRel}`);
|
|
6514
|
+
}
|
|
6515
|
+
} else {
|
|
6516
|
+
lines.push(`${draftRel}: draft unchanged`);
|
|
6517
|
+
}
|
|
6518
|
+
lines.push(` review, then copy accepted exports into ${sidecarPathOf4(result.file)}`);
|
|
6519
|
+
} else {
|
|
6520
|
+
lines.push("");
|
|
6521
|
+
lines.push(result.draftSource.trimEnd());
|
|
6522
|
+
}
|
|
6523
|
+
return lines;
|
|
6524
|
+
}
|
|
6525
|
+
|
|
6526
|
+
// src/interface-derivation.ts
|
|
6527
|
+
import { existsSync as existsSync12, readFileSync as readFileSync10, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
6528
|
+
import { basename as basename4, dirname as dirname14, relative as relative4, resolve as resolve9 } from "path";
|
|
6529
|
+
import { parse as parse10 } from "@nudojs/parser";
|
|
4979
6530
|
import {
|
|
4980
6531
|
analyzeFn,
|
|
4981
6532
|
beginDerivationSession,
|
|
4982
6533
|
constraintToEntryAbs,
|
|
4983
6534
|
derivationChain,
|
|
4984
|
-
effectiveInterface as
|
|
6535
|
+
effectiveInterface as effectiveInterface3,
|
|
4985
6536
|
endDerivationSession,
|
|
4986
6537
|
execNudoModule as execNudoModule2,
|
|
4987
|
-
formatConstraint as
|
|
6538
|
+
formatConstraint as formatConstraint4,
|
|
4988
6539
|
getDerivation,
|
|
4989
6540
|
interfaceDiagCount as interfaceDiagCount3,
|
|
4990
6541
|
isNodeModulesPath as isNodeModulesPath2,
|
|
4991
6542
|
isNudoConstraint as isNudoConstraint2,
|
|
4992
|
-
joinThenProject as
|
|
6543
|
+
joinThenProject as joinThenProject3,
|
|
4993
6544
|
parseSource as parseSource3,
|
|
4994
6545
|
projectDerivationDsl,
|
|
4995
6546
|
refineDiagCount as refineDiagCount3,
|
|
4996
6547
|
setAbsCallCollector as setAbsCallCollector2,
|
|
4997
|
-
sidecarPathOf as
|
|
6548
|
+
sidecarPathOf as sidecarPathOf5,
|
|
4998
6549
|
tagDerivationRoot,
|
|
4999
6550
|
takeInterfaceDiagsSince as takeInterfaceDiagsSince3,
|
|
5000
6551
|
takeRefineDiagsSince as takeRefineDiagsSince3,
|
|
@@ -5004,7 +6555,7 @@ function extractFnConstraintSources(sidecarSrc, fnName) {
|
|
|
5004
6555
|
const out = { params: {} };
|
|
5005
6556
|
let ast;
|
|
5006
6557
|
try {
|
|
5007
|
-
ast =
|
|
6558
|
+
ast = parse10(sidecarSrc);
|
|
5008
6559
|
} catch {
|
|
5009
6560
|
return out;
|
|
5010
6561
|
}
|
|
@@ -5075,7 +6626,7 @@ function extractFnConstraintSources(sidecarSrc, fnName) {
|
|
|
5075
6626
|
}
|
|
5076
6627
|
function functionParamNames(source, fnName) {
|
|
5077
6628
|
try {
|
|
5078
|
-
const ast =
|
|
6629
|
+
const ast = parse10(source);
|
|
5079
6630
|
for (const stmt of ast.program.body) {
|
|
5080
6631
|
const d = stmt.type === "ExportNamedDeclaration" ? stmt.declaration ?? void 0 : stmt;
|
|
5081
6632
|
if (!d) continue;
|
|
@@ -5104,7 +6655,7 @@ function paramNameOf(p) {
|
|
|
5104
6655
|
function topLevelFnNames(source) {
|
|
5105
6656
|
const out = [];
|
|
5106
6657
|
try {
|
|
5107
|
-
const ast =
|
|
6658
|
+
const ast = parse10(source);
|
|
5108
6659
|
for (const stmt of ast.program.body) {
|
|
5109
6660
|
const d = stmt.type === "ExportNamedDeclaration" ? stmt.declaration ?? void 0 : stmt;
|
|
5110
6661
|
if (!d) continue;
|
|
@@ -5124,16 +6675,16 @@ function topLevelFnNames(source) {
|
|
|
5124
6675
|
function importLocalMap(source, fromFile) {
|
|
5125
6676
|
const out = /* @__PURE__ */ new Map();
|
|
5126
6677
|
try {
|
|
5127
|
-
const ast =
|
|
5128
|
-
const base =
|
|
6678
|
+
const ast = parse10(source);
|
|
6679
|
+
const base = dirname14(resolve9(fromFile));
|
|
5129
6680
|
for (const stmt of ast.program.body) {
|
|
5130
6681
|
if (stmt.type !== "ImportDeclaration") continue;
|
|
5131
6682
|
const spec = stmt.source.value;
|
|
5132
6683
|
if (!spec.startsWith(".") && !spec.startsWith("/")) continue;
|
|
5133
|
-
const raw =
|
|
6684
|
+
const raw = resolve9(base, spec);
|
|
5134
6685
|
let modulePath = null;
|
|
5135
6686
|
for (const cand of [raw, `${raw}.js`, `${raw}.ts`, `${raw}.mjs`]) {
|
|
5136
|
-
if (
|
|
6687
|
+
if (existsSync12(cand)) {
|
|
5137
6688
|
modulePath = cand;
|
|
5138
6689
|
break;
|
|
5139
6690
|
}
|
|
@@ -5151,14 +6702,14 @@ function importLocalMap(source, fromFile) {
|
|
|
5151
6702
|
return out;
|
|
5152
6703
|
}
|
|
5153
6704
|
function resolveRelImport(fromSpec, fromDir, targetDir) {
|
|
5154
|
-
const abs =
|
|
5155
|
-
let rel =
|
|
6705
|
+
const abs = resolve9(fromDir, fromSpec);
|
|
6706
|
+
let rel = relative4(targetDir, abs);
|
|
5156
6707
|
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
5157
6708
|
return rel.split("\\").join("/");
|
|
5158
6709
|
}
|
|
5159
6710
|
function projectParamSlot(absList, paramName) {
|
|
5160
6711
|
if (absList.length === 0) return void 0;
|
|
5161
|
-
const constraint =
|
|
6712
|
+
const constraint = joinThenProject3(absList);
|
|
5162
6713
|
if (constraint === void 0) return void 0;
|
|
5163
6714
|
if (absList.length === 1) {
|
|
5164
6715
|
const node = getDerivation(absList[0]);
|
|
@@ -5182,7 +6733,7 @@ function projectParamSlot(absList, paramName) {
|
|
|
5182
6733
|
}
|
|
5183
6734
|
return {
|
|
5184
6735
|
constraint,
|
|
5185
|
-
dsl:
|
|
6736
|
+
dsl: formatConstraint4(constraint),
|
|
5186
6737
|
prelude: [],
|
|
5187
6738
|
imports: [],
|
|
5188
6739
|
compositional: false
|
|
@@ -5190,7 +6741,7 @@ function projectParamSlot(absList, paramName) {
|
|
|
5190
6741
|
}
|
|
5191
6742
|
function projectReturnSlot(retAbs, params, paramNames) {
|
|
5192
6743
|
if (retAbs.length === 0) return void 0;
|
|
5193
|
-
const constraint =
|
|
6744
|
+
const constraint = joinThenProject3(retAbs);
|
|
5194
6745
|
if (constraint === void 0) return void 0;
|
|
5195
6746
|
if (retAbs.length === 1) {
|
|
5196
6747
|
const node = getDerivation(retAbs[0]);
|
|
@@ -5219,7 +6770,7 @@ function projectReturnSlot(retAbs, params, paramNames) {
|
|
|
5219
6770
|
}
|
|
5220
6771
|
return {
|
|
5221
6772
|
constraint,
|
|
5222
|
-
dsl:
|
|
6773
|
+
dsl: formatConstraint4(constraint),
|
|
5223
6774
|
prelude: [],
|
|
5224
6775
|
imports: [],
|
|
5225
6776
|
compositional: false
|
|
@@ -5251,18 +6802,18 @@ function projectReturnRelativeToParams(node, params, paramNames) {
|
|
|
5251
6802
|
return void 0;
|
|
5252
6803
|
}
|
|
5253
6804
|
function deriveFromRoot(filePath, opts = {}) {
|
|
5254
|
-
const abs =
|
|
5255
|
-
const source =
|
|
6805
|
+
const abs = resolve9(filePath);
|
|
6806
|
+
const source = readFileSync10(abs, "utf-8");
|
|
5256
6807
|
const since = interfaceDiagCount3();
|
|
5257
|
-
const autoBind = opts.autoBind ?? interfaceConfig(findProjectConfig(
|
|
6808
|
+
const autoBind = opts.autoBind ?? interfaceConfig(findProjectConfig(dirname14(abs))?.config).autoBind;
|
|
5258
6809
|
const loadModule = opts.loadModule ?? defaultLoadModule;
|
|
5259
|
-
const sidecarPath =
|
|
5260
|
-
const sidecarSrc =
|
|
6810
|
+
const sidecarPath = sidecarPathOf5(abs);
|
|
6811
|
+
const sidecarSrc = existsSync12(sidecarPath) && !isNodeModulesPath2(sidecarPath) ? readFileSync10(sidecarPath, "utf-8") : "";
|
|
5261
6812
|
const fnNames = topLevelFnNames(source);
|
|
5262
6813
|
const roots = [];
|
|
5263
6814
|
const plans = [];
|
|
5264
6815
|
for (const fn of fnNames) {
|
|
5265
|
-
const eff =
|
|
6816
|
+
const eff = effectiveInterface3(source, fn, { loadModule, fromFile: abs, autoBind });
|
|
5266
6817
|
if (!eff || eff.source !== "handwritten") continue;
|
|
5267
6818
|
roots.push(fn);
|
|
5268
6819
|
const sources = sidecarSrc ? extractFnConstraintSources(sidecarSrc, fn) : { params: {} };
|
|
@@ -5298,8 +6849,8 @@ function deriveFromRoot(filePath, opts = {}) {
|
|
|
5298
6849
|
const wanted = opts.fnNames && opts.fnNames.length > 0 ? new Set(opts.fnNames) : void 0;
|
|
5299
6850
|
const derived = [];
|
|
5300
6851
|
for (const plan of plans) {
|
|
5301
|
-
const relRoot =
|
|
5302
|
-
const label = `${relRoot === "" || relRoot.startsWith("..") ?
|
|
6852
|
+
const relRoot = relative4(process.cwd(), abs);
|
|
6853
|
+
const label = `${relRoot === "" || relRoot.startsWith("..") ? basename4(abs) : relRoot}:${plan.fnName}`;
|
|
5303
6854
|
const rows = deriveOneRoot(plan, source, abs, modules, importLocals, label);
|
|
5304
6855
|
for (const row of rows) {
|
|
5305
6856
|
if (wanted && !wanted.has(row.fn)) continue;
|
|
@@ -5346,7 +6897,7 @@ function deriveOneRoot(plan, source, file, modules, importLocals, label) {
|
|
|
5346
6897
|
targetFile = file;
|
|
5347
6898
|
}
|
|
5348
6899
|
if (isNodeModulesPath2(targetFile)) continue;
|
|
5349
|
-
if (!
|
|
6900
|
+
if (!existsSync12(targetFile)) continue;
|
|
5350
6901
|
const key = `${targetFile}::${targetExport}`;
|
|
5351
6902
|
let agg = byCallee.get(key);
|
|
5352
6903
|
if (!agg) {
|
|
@@ -5360,7 +6911,7 @@ function deriveOneRoot(plan, source, file, modules, importLocals, label) {
|
|
|
5360
6911
|
for (const agg of byCallee.values()) {
|
|
5361
6912
|
let targetSource;
|
|
5362
6913
|
try {
|
|
5363
|
-
targetSource =
|
|
6914
|
+
targetSource = readFileSync10(agg.targetFile, "utf-8");
|
|
5364
6915
|
} catch {
|
|
5365
6916
|
continue;
|
|
5366
6917
|
}
|
|
@@ -5597,7 +7148,7 @@ function sidecarAssembles(text, fromFile, loadModule) {
|
|
|
5597
7148
|
}
|
|
5598
7149
|
}
|
|
5599
7150
|
function emitDerivedFromRoot(rootFile, opts) {
|
|
5600
|
-
const abs =
|
|
7151
|
+
const abs = resolve9(rootFile);
|
|
5601
7152
|
const derive = deriveFromRoot(abs, {
|
|
5602
7153
|
...opts.loadModule ? { loadModule: opts.loadModule } : {},
|
|
5603
7154
|
...opts.autoBind !== void 0 ? { autoBind: opts.autoBind } : {},
|
|
@@ -5606,10 +7157,10 @@ function emitDerivedFromRoot(rootFile, opts) {
|
|
|
5606
7157
|
if (!derive.hasRoot) {
|
|
5607
7158
|
return { sidecars: [], hasRoot: false, roots: [], entryOnly: true };
|
|
5608
7159
|
}
|
|
5609
|
-
const proj = findProjectConfig(
|
|
7160
|
+
const proj = findProjectConfig(dirname14(abs));
|
|
5610
7161
|
const allow = interfaceConfig(proj?.config).emit;
|
|
5611
7162
|
const projectDir = proj?.projectDir;
|
|
5612
|
-
const rootSidecarDir =
|
|
7163
|
+
const rootSidecarDir = dirname14(sidecarPathOf5(abs));
|
|
5613
7164
|
const wanted = opts.fnNames && opts.fnNames.length > 0 ? new Set(opts.fnNames) : void 0;
|
|
5614
7165
|
const result = {
|
|
5615
7166
|
sidecars: [],
|
|
@@ -5620,7 +7171,7 @@ function emitDerivedFromRoot(rootFile, opts) {
|
|
|
5620
7171
|
for (const row of derive.derived) {
|
|
5621
7172
|
if (wanted && !wanted.has(row.fn)) continue;
|
|
5622
7173
|
if (!matchesEmitAllowlist(row.file, projectDir, allow)) continue;
|
|
5623
|
-
const sp =
|
|
7174
|
+
const sp = sidecarPathOf5(row.file);
|
|
5624
7175
|
if (isNodeModulesPath2(sp)) continue;
|
|
5625
7176
|
const list = bySidecar.get(sp) ?? [];
|
|
5626
7177
|
list.push(row);
|
|
@@ -5628,8 +7179,8 @@ function emitDerivedFromRoot(rootFile, opts) {
|
|
|
5628
7179
|
}
|
|
5629
7180
|
for (const [sidecarPath, rows0] of bySidecar) {
|
|
5630
7181
|
const targetFile = rows0[0].file;
|
|
5631
|
-
const targetSidecarDir =
|
|
5632
|
-
const prevSrc =
|
|
7182
|
+
const targetSidecarDir = dirname14(sidecarPath);
|
|
7183
|
+
const prevSrc = existsSync12(sidecarPath) ? readFileSync10(sidecarPath, "utf-8") : "";
|
|
5633
7184
|
const rows = opts.refreshExistingOnly ? rows0.filter((r) => findGeneratedSectionText(prevSrc, r.fn) !== void 0) : rows0;
|
|
5634
7185
|
if (rows.length === 0) continue;
|
|
5635
7186
|
const handwritten = handwrittenNames(prevSrc);
|
|
@@ -5648,7 +7199,7 @@ function emitDerivedFromRoot(rootFile, opts) {
|
|
|
5648
7199
|
issues.push({
|
|
5649
7200
|
code: "nudo:interface-name-clash",
|
|
5650
7201
|
severity: "error",
|
|
5651
|
-
message: `sidecar already has a handwritten binding '${row.fn}' (${
|
|
7202
|
+
message: `sidecar already has a handwritten binding '${row.fn}' (${relative4(process.cwd(), sidecarPath) || sidecarPath}); handwritten wins \u2014 skipping emit`
|
|
5652
7203
|
});
|
|
5653
7204
|
anySkip = "name-clash";
|
|
5654
7205
|
continue;
|
|
@@ -5676,7 +7227,7 @@ function emitDerivedFromRoot(rootFile, opts) {
|
|
|
5676
7227
|
continue;
|
|
5677
7228
|
}
|
|
5678
7229
|
for (const n of body.usedNames) taken.add(n);
|
|
5679
|
-
const srcRel =
|
|
7230
|
+
const srcRel = relative4(targetSidecarDir, targetFile) || basename4(targetFile);
|
|
5680
7231
|
const section = [
|
|
5681
7232
|
DERIVED_HEADER,
|
|
5682
7233
|
`// source: ${srcRel}:${row.fn}`,
|
|
@@ -5696,8 +7247,8 @@ function emitDerivedFromRoot(rootFile, opts) {
|
|
|
5696
7247
|
for (const n of collectTopLevelNames(prevSection)) taken.add(n);
|
|
5697
7248
|
continue;
|
|
5698
7249
|
}
|
|
5699
|
-
const
|
|
5700
|
-
if (prevSection !== void 0 && normalizeSectionText(prevSection) ===
|
|
7250
|
+
const norm2 = normalizeSectionText(section);
|
|
7251
|
+
if (prevSection !== void 0 && normalizeSectionText(prevSection) === norm2) {
|
|
5701
7252
|
anySkip = "no-change";
|
|
5702
7253
|
accepted.push({ fn: row.fn, text: prevSection, prevText: prevSection });
|
|
5703
7254
|
for (const n of collectTopLevelNames(prevSection)) taken.add(n);
|
|
@@ -5726,7 +7277,7 @@ function emitDerivedFromRoot(rootFile, opts) {
|
|
|
5726
7277
|
issues.push({
|
|
5727
7278
|
code: "nudo:interface-not-projectable",
|
|
5728
7279
|
severity: "error",
|
|
5729
|
-
message: `assembled sidecar failed round-trip (${
|
|
7280
|
+
message: `assembled sidecar failed round-trip (${relative4(process.cwd(), sidecarPath) || sidecarPath}); refusing to write`
|
|
5730
7281
|
});
|
|
5731
7282
|
anySkip = "not-projectable";
|
|
5732
7283
|
result.sidecars.push({
|
|
@@ -5741,15 +7292,15 @@ function emitDerivedFromRoot(rootFile, opts) {
|
|
|
5741
7292
|
continue;
|
|
5742
7293
|
}
|
|
5743
7294
|
const changed = finalContent !== prevSrc;
|
|
5744
|
-
const diff = changed ? unifiedDiff(prevSrc, finalContent,
|
|
7295
|
+
const diff = changed ? unifiedDiff(prevSrc, finalContent, relative4(process.cwd(), sidecarPath) || sidecarPath) : void 0;
|
|
5745
7296
|
if (changed && !opts.dryRun) {
|
|
5746
7297
|
const tmp = `${sidecarPath}.tmp-${process.pid}-${Math.random().toString(16).slice(2, 10)}`;
|
|
5747
7298
|
try {
|
|
5748
|
-
|
|
7299
|
+
writeFileSync4(tmp, finalContent, "utf-8");
|
|
5749
7300
|
renameSync2(tmp, sidecarPath);
|
|
5750
7301
|
} catch (e) {
|
|
5751
7302
|
try {
|
|
5752
|
-
if (
|
|
7303
|
+
if (existsSync12(tmp)) unlinkSync2(tmp);
|
|
5753
7304
|
} catch {
|
|
5754
7305
|
}
|
|
5755
7306
|
throw e;
|
|
@@ -5900,10 +7451,16 @@ function derivedRoundTrips(text, fn, fromFile, loadModule) {
|
|
|
5900
7451
|
}
|
|
5901
7452
|
}
|
|
5902
7453
|
export {
|
|
7454
|
+
ANALYSIS_ABI,
|
|
7455
|
+
DEFAULT_ANALYSIS_MODE,
|
|
7456
|
+
DiskCache,
|
|
5903
7457
|
SEMANTIC_TOKEN_MODIFIERS,
|
|
5904
7458
|
SEMANTIC_TOKEN_TYPES,
|
|
5905
7459
|
absToTSType,
|
|
5906
7460
|
absToZodSchema,
|
|
7461
|
+
ambientSourcesOfSidecar,
|
|
7462
|
+
analysisConfig,
|
|
7463
|
+
analysisFileCacheKey,
|
|
5907
7464
|
analyzeExportsFromSource,
|
|
5908
7465
|
analyzeFile,
|
|
5909
7466
|
analyzeFileAsync,
|
|
@@ -5913,12 +7470,15 @@ export {
|
|
|
5913
7470
|
buildCaseDirective,
|
|
5914
7471
|
buildModuleGraph,
|
|
5915
7472
|
buildSemanticTokens,
|
|
7473
|
+
checkCacheKey,
|
|
5916
7474
|
clearAbsModuleCache,
|
|
5917
7475
|
clearAnalysisFileCache,
|
|
5918
7476
|
clearAnalysisSessionCaches,
|
|
5919
7477
|
clearBPathCache,
|
|
7478
|
+
clearEnvPathDeps,
|
|
5920
7479
|
clearFnAnalysisCache,
|
|
5921
7480
|
clearHarvestCache,
|
|
7481
|
+
clearPathEnvCaches,
|
|
5922
7482
|
collectAbsBindingsFromGraph,
|
|
5923
7483
|
collectAbsInlays,
|
|
5924
7484
|
collectBPathDiagnostics,
|
|
@@ -5929,14 +7489,20 @@ export {
|
|
|
5929
7489
|
collectDtsFromEntry,
|
|
5930
7490
|
collectEnvGlobals,
|
|
5931
7491
|
collectEnvModules,
|
|
7492
|
+
collectLoadDepContents,
|
|
7493
|
+
collectParamBodyAccesses,
|
|
5932
7494
|
collectStaticImports,
|
|
5933
7495
|
computeDirtySet,
|
|
5934
7496
|
defaultAbsLoadModule,
|
|
5935
7497
|
defaultLoadModule,
|
|
5936
7498
|
deriveFromRoot,
|
|
7499
|
+
diagnosticsLevelForFile,
|
|
7500
|
+
diskCacheRoot,
|
|
7501
|
+
draftInterface,
|
|
5937
7502
|
emitDerivedFromRoot,
|
|
5938
7503
|
emitInterface,
|
|
5939
7504
|
encodeSemanticTokens,
|
|
7505
|
+
envPathDependents,
|
|
5940
7506
|
evalAbsModuleGraph,
|
|
5941
7507
|
evalProgramAbsWithModules,
|
|
5942
7508
|
evictAbsModuleCacheFiles,
|
|
@@ -5945,8 +7511,12 @@ export {
|
|
|
5945
7511
|
evictBPathCacheForFiles,
|
|
5946
7512
|
evictFnAnalysisCacheForFiles,
|
|
5947
7513
|
extractFnConstraintSources,
|
|
7514
|
+
extractNudoImportSpecs,
|
|
7515
|
+
filterDiagnosticsByLevel,
|
|
5948
7516
|
findProjectConfig,
|
|
5949
7517
|
formatDerivedSection,
|
|
7518
|
+
formatDraftModule,
|
|
7519
|
+
formatDraftSummary,
|
|
5950
7520
|
formatEmitSummary,
|
|
5951
7521
|
formatHarvestSummary,
|
|
5952
7522
|
formatInterfaceSurfaceLine,
|
|
@@ -5957,6 +7527,7 @@ export {
|
|
|
5957
7527
|
getAbsAtPosition,
|
|
5958
7528
|
getAbsAtPositionAsync,
|
|
5959
7529
|
getAnalysisFileCacheSize,
|
|
7530
|
+
getAnalysisSession,
|
|
5960
7531
|
getCasesForFile,
|
|
5961
7532
|
getCompletionsAtPosition,
|
|
5962
7533
|
getHoverAtPosition,
|
|
@@ -5967,24 +7538,39 @@ export {
|
|
|
5967
7538
|
harvestPackageCached,
|
|
5968
7539
|
harvestToAbsModules,
|
|
5969
7540
|
harvestedValueToAbs,
|
|
7541
|
+
ifaceCacheKey,
|
|
5970
7542
|
insertGeneratedCaseDirectives,
|
|
5971
7543
|
interfaceConfig,
|
|
5972
7544
|
interfaceSurface,
|
|
7545
|
+
interfaceTierModifierBit,
|
|
5973
7546
|
isBPathCapable,
|
|
7547
|
+
isDraftableEntry,
|
|
7548
|
+
isEnvTemplatePath,
|
|
5974
7549
|
isNudoTargetPath,
|
|
7550
|
+
isProjectConfigPath,
|
|
7551
|
+
isSidecarPath,
|
|
7552
|
+
isWatchRelevantPath,
|
|
5975
7553
|
lookupHarvested,
|
|
5976
7554
|
matchesEmitAllowlist,
|
|
5977
7555
|
mockDirectivesToAbsSeeds,
|
|
7556
|
+
noteEnvPathDeps,
|
|
5978
7557
|
packageHarvestToAbsModules,
|
|
7558
|
+
relativizePath,
|
|
5979
7559
|
resetAllAnalysisCaches,
|
|
5980
7560
|
resolvePackageRoot,
|
|
5981
7561
|
serializeCaseArg,
|
|
5982
7562
|
serializeInferJson,
|
|
7563
|
+
setAnalysisSession,
|
|
7564
|
+
sha256Hex,
|
|
7565
|
+
shouldAnalyzeFile,
|
|
7566
|
+
sidecarDraftPath,
|
|
7567
|
+
hasNudoDirectives as sourceHasNudoDirectives,
|
|
5983
7568
|
stripGeneratedCaseDirectives,
|
|
5984
7569
|
summarizeNodeEnv,
|
|
5985
7570
|
topoSortDirty,
|
|
5986
7571
|
tryBPathCall,
|
|
5987
7572
|
tryBPathCallFull,
|
|
5988
7573
|
tryRunBPath,
|
|
5989
|
-
unifiedDiff
|
|
7574
|
+
unifiedDiff,
|
|
7575
|
+
writeInterfaceDraft
|
|
5990
7576
|
};
|