@nudojs/service 0.3.2 → 1.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-FSIHAE7I.js +5871 -0
- package/dist/config-UlReSTfh.d.ts +152 -0
- package/dist/evaluator/evaluator-api.d.ts +28 -0
- package/dist/evaluator/evaluator-api.js +68 -0
- package/dist/index.d.ts +117 -5
- package/dist/index.js +1033 -555
- package/package.json +14 -7
package/dist/index.js
CHANGED
|
@@ -1,6 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BUILTIN_PROTOTYPE_METHOD_APPROXIMATIONS,
|
|
3
|
+
USAGE_SITE_MODULE,
|
|
4
|
+
evaluate,
|
|
5
|
+
evaluateFunction,
|
|
6
|
+
evaluateFunctionFull,
|
|
7
|
+
evaluateProgram,
|
|
8
|
+
findProjectConfig,
|
|
9
|
+
getUnreachableRanges,
|
|
10
|
+
interfaceConfig,
|
|
11
|
+
loadEnvs,
|
|
12
|
+
memberMayExistOn,
|
|
13
|
+
preloadPathEnvs,
|
|
14
|
+
resetEnvModules,
|
|
15
|
+
resetMemo,
|
|
16
|
+
resetMockModules,
|
|
17
|
+
resetUnreachableRanges,
|
|
18
|
+
resolveNpmNudo,
|
|
19
|
+
setCallCollector,
|
|
20
|
+
setCurrentFileDir,
|
|
21
|
+
setCurrentSource,
|
|
22
|
+
setEnvModules,
|
|
23
|
+
setMockModules,
|
|
24
|
+
setModuleResolver,
|
|
25
|
+
setNodeTypeCollector,
|
|
26
|
+
setProvenanceTracking,
|
|
27
|
+
setSampleCount,
|
|
28
|
+
setUnknownBuiltinHandler,
|
|
29
|
+
setUnknownCollector,
|
|
30
|
+
setUsageSiteTag
|
|
31
|
+
} from "./chunk-FSIHAE7I.js";
|
|
32
|
+
|
|
1
33
|
// src/analyzer.ts
|
|
2
|
-
import { readFileSync as
|
|
3
|
-
import { resolve as
|
|
34
|
+
import { readFileSync as readFileSync5, existsSync as existsSync4, statSync as statSync4, realpathSync } from "fs";
|
|
35
|
+
import { resolve as resolve5, dirname as dirname6 } from "path";
|
|
4
36
|
import traverse from "@babel/traverse";
|
|
5
37
|
import {
|
|
6
38
|
T as T2,
|
|
@@ -11,7 +43,6 @@ import {
|
|
|
11
43
|
createEnvironment as createEnvironment2,
|
|
12
44
|
isSubtypeOf,
|
|
13
45
|
mockHelperToTypeValue,
|
|
14
|
-
getFnSig as getFnSig3,
|
|
15
46
|
generalizeFromAst as generalizeFromAst2,
|
|
16
47
|
termToString,
|
|
17
48
|
predToString,
|
|
@@ -20,8 +51,7 @@ import {
|
|
|
20
51
|
setAbsCallCollector,
|
|
21
52
|
setAbsNodeCollector,
|
|
22
53
|
absFunction as absFunction5,
|
|
23
|
-
|
|
24
|
-
findAbsAtPosition,
|
|
54
|
+
checkInjectedDomainEvidence,
|
|
25
55
|
typeValueToAbs as typeValueToAbs5,
|
|
26
56
|
absToTypeValue as absToTypeValue2,
|
|
27
57
|
formatAbs as formatAbs3,
|
|
@@ -30,36 +60,6 @@ import {
|
|
|
30
60
|
fnFingerprints
|
|
31
61
|
} from "@nudojs/core";
|
|
32
62
|
import { parse as parse5, extractDirectives, extractFileDirectives, parseTypeValueExpr as parseTypeValueExpr2 } from "@nudojs/parser";
|
|
33
|
-
import {
|
|
34
|
-
evaluate,
|
|
35
|
-
evaluateFunction,
|
|
36
|
-
evaluateFunctionFull,
|
|
37
|
-
memberMayExistOn,
|
|
38
|
-
setUsageSiteTag,
|
|
39
|
-
USAGE_SITE_MODULE,
|
|
40
|
-
evaluateProgram,
|
|
41
|
-
setModuleResolver,
|
|
42
|
-
setCurrentFileDir,
|
|
43
|
-
resetMemo,
|
|
44
|
-
getUnreachableRanges,
|
|
45
|
-
resetUnreachableRanges,
|
|
46
|
-
setNodeTypeCollector,
|
|
47
|
-
setCallCollector,
|
|
48
|
-
setUnknownCollector,
|
|
49
|
-
setProvenanceTracking,
|
|
50
|
-
setSampleCount,
|
|
51
|
-
setUnknownBuiltinHandler,
|
|
52
|
-
setEnvModules,
|
|
53
|
-
resetEnvModules,
|
|
54
|
-
setMockModules,
|
|
55
|
-
resetMockModules,
|
|
56
|
-
setCurrentSource,
|
|
57
|
-
loadEnvs as loadEnvs2,
|
|
58
|
-
preloadPathEnvs,
|
|
59
|
-
findProjectConfig,
|
|
60
|
-
resolveNpmNudo,
|
|
61
|
-
BUILTIN_PROTOTYPE_METHOD_APPROXIMATIONS
|
|
62
|
-
} from "@nudojs/cli/evaluator";
|
|
63
63
|
|
|
64
64
|
// src/mock-abs.ts
|
|
65
65
|
import { parseTypeValueExpr } from "@nudojs/parser";
|
|
@@ -268,17 +268,44 @@ function mockDirectivesToAbsSeeds(functions) {
|
|
|
268
268
|
return { seedVars, seedFns };
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
+
// src/load-module.ts
|
|
272
|
+
import { existsSync, statSync, readFileSync } from "fs";
|
|
273
|
+
import { dirname, resolve, join } from "path";
|
|
274
|
+
function defaultLoadModule(spec, fromFile) {
|
|
275
|
+
if (!spec.startsWith(".") && !spec.startsWith("/")) return void 0;
|
|
276
|
+
try {
|
|
277
|
+
const base = dirname(resolve(fromFile));
|
|
278
|
+
const p = resolve(base, spec);
|
|
279
|
+
for (const cand of [
|
|
280
|
+
p,
|
|
281
|
+
`${p}.js`,
|
|
282
|
+
`${p}.mjs`,
|
|
283
|
+
`${p}.ts`,
|
|
284
|
+
join(p, "index.js"),
|
|
285
|
+
join(p, "index.mjs"),
|
|
286
|
+
join(p, "index.ts")
|
|
287
|
+
]) {
|
|
288
|
+
if (existsSync(cand) && !statSync(cand).isDirectory()) {
|
|
289
|
+
return readFileSync(cand, "utf-8");
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return void 0;
|
|
293
|
+
} catch {
|
|
294
|
+
return void 0;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
271
298
|
// src/harvest-auto.ts
|
|
272
299
|
import { parse as parse2 } from "@nudojs/parser";
|
|
273
300
|
|
|
274
301
|
// src/static-imports.ts
|
|
275
302
|
import { parse } from "@nudojs/parser";
|
|
276
|
-
import { readFileSync, existsSync } from "fs";
|
|
277
|
-
import { resolve, dirname, join } from "path";
|
|
303
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
304
|
+
import { resolve as resolve2, dirname as dirname2, join as join2 } from "path";
|
|
278
305
|
import { generalizeFromAst } from "@nudojs/core";
|
|
279
306
|
function resolveRelative(fromFile, spec) {
|
|
280
307
|
if (!spec.startsWith(".") && !spec.startsWith("/")) return void 0;
|
|
281
|
-
const base =
|
|
308
|
+
const base = resolve2(dirname2(fromFile), spec);
|
|
282
309
|
for (const cand of [
|
|
283
310
|
base,
|
|
284
311
|
base + ".js",
|
|
@@ -287,12 +314,12 @@ function resolveRelative(fromFile, spec) {
|
|
|
287
314
|
base + ".ts",
|
|
288
315
|
base + ".mts",
|
|
289
316
|
base + ".cts",
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
317
|
+
join2(base, "index.js"),
|
|
318
|
+
join2(base, "index.cjs"),
|
|
319
|
+
join2(base, "index.mjs"),
|
|
320
|
+
join2(base, "index.ts")
|
|
294
321
|
]) {
|
|
295
|
-
if (
|
|
322
|
+
if (existsSync2(cand)) return cand;
|
|
296
323
|
}
|
|
297
324
|
return void 0;
|
|
298
325
|
}
|
|
@@ -411,15 +438,15 @@ function collectCjsExports(stmt, named, setDefault) {
|
|
|
411
438
|
function collectStaticImports(entryFile, maxDepth = 8) {
|
|
412
439
|
const graph = /* @__PURE__ */ new Map();
|
|
413
440
|
const queue = [
|
|
414
|
-
{ file:
|
|
441
|
+
{ file: resolve2(entryFile), depth: 0 }
|
|
415
442
|
];
|
|
416
443
|
const seen = /* @__PURE__ */ new Set();
|
|
417
444
|
while (queue.length > 0) {
|
|
418
445
|
const { file, depth } = queue.shift();
|
|
419
446
|
if (seen.has(file) || depth > maxDepth) continue;
|
|
420
447
|
seen.add(file);
|
|
421
|
-
if (!
|
|
422
|
-
const source =
|
|
448
|
+
if (!existsSync2(file)) continue;
|
|
449
|
+
const source = readFileSync2(file, "utf8");
|
|
423
450
|
const mod = analyzeExportsFromSource(file, source);
|
|
424
451
|
graph.set(file, mod);
|
|
425
452
|
const ast = parse(source);
|
|
@@ -432,19 +459,19 @@ function collectStaticImports(entryFile, maxDepth = 8) {
|
|
|
432
459
|
}
|
|
433
460
|
|
|
434
461
|
// src/harvest-package.ts
|
|
435
|
-
import { existsSync as
|
|
436
|
-
import { join as
|
|
462
|
+
import { existsSync as existsSync3, readdirSync, statSync as statSync2, readFileSync as readFileSync3 } from "fs";
|
|
463
|
+
import { join as join3, resolve as resolve3, dirname as dirname3, basename } from "path";
|
|
437
464
|
import { harvestDts } from "@nudojs/harvester";
|
|
438
465
|
import { typeValueToString as typeValueToString2 } from "@nudojs/core";
|
|
439
466
|
function resolvePackageRoot(pkg, fromDir = process.cwd()) {
|
|
440
|
-
let dir =
|
|
467
|
+
let dir = resolve3(fromDir);
|
|
441
468
|
const bare = pkg.replace(/^@/, "").replace(/\//g, "__");
|
|
442
469
|
for (let i = 0; i < 8; i++) {
|
|
443
|
-
const typesPath =
|
|
444
|
-
if (
|
|
445
|
-
const pkgPath =
|
|
446
|
-
if (
|
|
447
|
-
const parent =
|
|
470
|
+
const typesPath = join3(dir, "node_modules", "@types", bare);
|
|
471
|
+
if (existsSync3(typesPath)) return typesPath;
|
|
472
|
+
const pkgPath = join3(dir, "node_modules", pkg);
|
|
473
|
+
if (existsSync3(pkgPath)) return pkgPath;
|
|
474
|
+
const parent = resolve3(dir, "..");
|
|
448
475
|
if (parent === dir) break;
|
|
449
476
|
dir = parent;
|
|
450
477
|
}
|
|
@@ -474,10 +501,10 @@ function collectDtsFiles(root, maxFiles = 8) {
|
|
|
474
501
|
for (const name of entries) {
|
|
475
502
|
if (out.length >= maxFiles * 3) return;
|
|
476
503
|
if (SKIP_DIRS.has(name)) continue;
|
|
477
|
-
const p =
|
|
504
|
+
const p = join3(dir, name);
|
|
478
505
|
let st;
|
|
479
506
|
try {
|
|
480
|
-
st =
|
|
507
|
+
st = statSync2(p);
|
|
481
508
|
} catch {
|
|
482
509
|
continue;
|
|
483
510
|
}
|
|
@@ -493,15 +520,15 @@ function collectDtsFiles(root, maxFiles = 8) {
|
|
|
493
520
|
return out.slice(0, maxFiles);
|
|
494
521
|
}
|
|
495
522
|
function entryDtsFromPackageJson(root) {
|
|
496
|
-
const pj =
|
|
497
|
-
if (!
|
|
523
|
+
const pj = join3(root, "package.json");
|
|
524
|
+
if (!existsSync3(pj)) return void 0;
|
|
498
525
|
try {
|
|
499
|
-
const raw = JSON.parse(
|
|
526
|
+
const raw = JSON.parse(readFileSync3(pj, "utf8"));
|
|
500
527
|
const entry = raw.types ?? raw.typings;
|
|
501
528
|
if (typeof entry === "string" && entry.endsWith(".d.ts")) {
|
|
502
|
-
const p =
|
|
503
|
-
if (
|
|
504
|
-
const st =
|
|
529
|
+
const p = resolve3(root, entry);
|
|
530
|
+
if (existsSync3(p)) {
|
|
531
|
+
const st = statSync2(p);
|
|
505
532
|
if (st.size <= MAX_DTS_BYTES) return p;
|
|
506
533
|
}
|
|
507
534
|
}
|
|
@@ -517,24 +544,24 @@ function collectDtsFromEntry(entry, maxFiles = 200) {
|
|
|
517
544
|
const RELATIVE_FROM_REGEX = /\bfrom\s+["'](\.[^"']+)["']/g;
|
|
518
545
|
while (queue.length > 0 && files.length < maxFiles) {
|
|
519
546
|
const current = queue.shift();
|
|
520
|
-
if (seen.has(current) || !
|
|
547
|
+
if (seen.has(current) || !existsSync3(current)) continue;
|
|
521
548
|
seen.add(current);
|
|
522
549
|
if (!current.endsWith(".d.ts")) continue;
|
|
523
550
|
files.push(current);
|
|
524
551
|
let text;
|
|
525
552
|
try {
|
|
526
|
-
text =
|
|
553
|
+
text = readFileSync3(current, "utf-8");
|
|
527
554
|
} catch {
|
|
528
555
|
continue;
|
|
529
556
|
}
|
|
530
|
-
const dir =
|
|
557
|
+
const dir = dirname3(current);
|
|
531
558
|
for (const match of text.matchAll(REFERENCE_PATH_REGEX)) {
|
|
532
|
-
queue.push(
|
|
559
|
+
queue.push(resolve3(dir, match[1]));
|
|
533
560
|
}
|
|
534
561
|
for (const match of text.matchAll(RELATIVE_FROM_REGEX)) {
|
|
535
|
-
const base =
|
|
536
|
-
for (const candidate of [`${base}.d.ts`,
|
|
537
|
-
if (
|
|
562
|
+
const base = resolve3(dir, match[1]);
|
|
563
|
+
for (const candidate of [`${base}.d.ts`, join3(base, "index.d.ts")]) {
|
|
564
|
+
if (existsSync3(candidate)) {
|
|
538
565
|
queue.push(candidate);
|
|
539
566
|
break;
|
|
540
567
|
}
|
|
@@ -546,11 +573,11 @@ function collectDtsFromEntry(entry, maxFiles = 200) {
|
|
|
546
573
|
function harvestPackage(pkg, fromDir, maxFiles = 8) {
|
|
547
574
|
const root = resolvePackageRoot(pkg, fromDir);
|
|
548
575
|
if (!root) return { error: `package not found: ${pkg}` };
|
|
549
|
-
const entry = entryDtsFromPackageJson(root) ??
|
|
550
|
-
let dtsFiles =
|
|
576
|
+
const entry = entryDtsFromPackageJson(root) ?? join3(root, "index.d.ts");
|
|
577
|
+
let dtsFiles = existsSync3(entry) ? collectDtsFromEntry(entry, maxFiles) : [];
|
|
551
578
|
if (dtsFiles.length === 0) {
|
|
552
579
|
const collected = collectDtsFiles(root, maxFiles);
|
|
553
|
-
dtsFiles = entry &&
|
|
580
|
+
dtsFiles = entry && existsSync3(entry) && !collected.includes(entry) ? [entry, ...collected.filter((f) => f !== entry)].slice(0, maxFiles) : collected;
|
|
554
581
|
}
|
|
555
582
|
if (dtsFiles.length === 0) {
|
|
556
583
|
return { error: `no .d.ts under ${root}` };
|
|
@@ -639,8 +666,8 @@ function autoHarvestModules(source, fromDir) {
|
|
|
639
666
|
}
|
|
640
667
|
|
|
641
668
|
// src/abs-modules-graph.ts
|
|
642
|
-
import { readFileSync as
|
|
643
|
-
import { dirname as
|
|
669
|
+
import { readFileSync as readFileSync4, statSync as statSync3 } from "fs";
|
|
670
|
+
import { dirname as dirname5, resolve as resolve4 } from "path";
|
|
644
671
|
import { parse as parse3 } from "@nudojs/parser";
|
|
645
672
|
import {
|
|
646
673
|
evalProgramAbs,
|
|
@@ -656,7 +683,7 @@ import {
|
|
|
656
683
|
getFnSig,
|
|
657
684
|
absFunction as absFunction2
|
|
658
685
|
} from "@nudojs/core";
|
|
659
|
-
import { dirname as
|
|
686
|
+
import { dirname as dirname4 } from "path";
|
|
660
687
|
function markMockConf2(a) {
|
|
661
688
|
a.conf = confJoin2(a.conf, "mock");
|
|
662
689
|
return a;
|
|
@@ -728,7 +755,7 @@ function bareSpecToAbsModules(spec, fromFile) {
|
|
|
728
755
|
const parts = spec.split("/");
|
|
729
756
|
const pkg = spec.startsWith("@") ? parts.length >= 2 ? `${parts[0]}/${parts[1]}` : void 0 : parts[0];
|
|
730
757
|
if (!pkg) return void 0;
|
|
731
|
-
const table = harvestToAbsModules(pkg,
|
|
758
|
+
const table = harvestToAbsModules(pkg, dirname4(fromFile));
|
|
732
759
|
return table[spec] ?? table[pkg];
|
|
733
760
|
}
|
|
734
761
|
|
|
@@ -736,10 +763,10 @@ function bareSpecToAbsModules(spec, fromFile) {
|
|
|
736
763
|
function defaultAbsLoadModule(spec, fromFile) {
|
|
737
764
|
if (!spec.startsWith(".") && !spec.startsWith("/")) return void 0;
|
|
738
765
|
try {
|
|
739
|
-
const p =
|
|
740
|
-
for (const cand of [p, `${p}.js`, `${p}.mjs`, `${p}.ts`,
|
|
766
|
+
const p = resolve4(dirname5(resolve4(fromFile)), spec);
|
|
767
|
+
for (const cand of [p, `${p}.js`, `${p}.mjs`, `${p}.ts`, resolve4(p, "index.js")]) {
|
|
741
768
|
try {
|
|
742
|
-
return
|
|
769
|
+
return readFileSync4(cand, "utf-8");
|
|
743
770
|
} catch {
|
|
744
771
|
}
|
|
745
772
|
}
|
|
@@ -749,14 +776,14 @@ function defaultAbsLoadModule(spec, fromFile) {
|
|
|
749
776
|
}
|
|
750
777
|
}
|
|
751
778
|
function relCandidates(spec, fromFile) {
|
|
752
|
-
const p =
|
|
753
|
-
return [p, `${p}.js`, `${p}.mjs`, `${p}.ts`,
|
|
779
|
+
const p = resolve4(dirname5(resolve4(fromFile)), spec);
|
|
780
|
+
return [p, `${p}.js`, `${p}.mjs`, `${p}.ts`, resolve4(p, "index.js")];
|
|
754
781
|
}
|
|
755
782
|
function resolveRel(spec, fromFile) {
|
|
756
783
|
if (!spec.startsWith(".") && !spec.startsWith("/")) return null;
|
|
757
784
|
for (const cand of relCandidates(spec, fromFile)) {
|
|
758
785
|
try {
|
|
759
|
-
|
|
786
|
+
readFileSync4(cand, "utf-8");
|
|
760
787
|
return cand;
|
|
761
788
|
} catch {
|
|
762
789
|
}
|
|
@@ -854,7 +881,7 @@ function evalAbsModuleGraph(entrySource, entryFile, opts = {}) {
|
|
|
854
881
|
const shared = absModuleCache.get(absPath);
|
|
855
882
|
if (shared) {
|
|
856
883
|
try {
|
|
857
|
-
const st =
|
|
884
|
+
const st = statSync3(absPath);
|
|
858
885
|
if (st.mtimeMs === shared.mtimeMs && st.size === shared.size) {
|
|
859
886
|
for (const iss of shared.issues) pushIssue(iss.kind, iss.label, iss.reason);
|
|
860
887
|
cache.set(absPath, shared.exports);
|
|
@@ -919,7 +946,7 @@ function evalAbsModuleGraph(entrySource, entryFile, opts = {}) {
|
|
|
919
946
|
cache.set(absPath, exports);
|
|
920
947
|
let fingerprint;
|
|
921
948
|
try {
|
|
922
|
-
const st =
|
|
949
|
+
const st = statSync3(absPath);
|
|
923
950
|
fingerprint = { mtimeMs: st.mtimeMs, size: st.size };
|
|
924
951
|
} catch {
|
|
925
952
|
}
|
|
@@ -994,7 +1021,6 @@ import {
|
|
|
994
1021
|
getFnImpl as getFnImpl2
|
|
995
1022
|
} from "@nudojs/core";
|
|
996
1023
|
import { parse as parse4, extractInlineDirectives } from "@nudojs/parser";
|
|
997
|
-
import { loadEnvs } from "@nudojs/cli/evaluator";
|
|
998
1024
|
|
|
999
1025
|
// src/env-to-abs.ts
|
|
1000
1026
|
import {
|
|
@@ -1114,10 +1140,10 @@ function evictFnAnalysisCacheForFiles(files) {
|
|
|
1114
1140
|
}
|
|
1115
1141
|
return n;
|
|
1116
1142
|
}
|
|
1117
|
-
function caseDirectiveKey(directives,
|
|
1143
|
+
function caseDirectiveKey(directives, typeValueToString7) {
|
|
1118
1144
|
return directives.map((d) => {
|
|
1119
1145
|
if (d.kind === "case") {
|
|
1120
|
-
const args = (d.args ?? []).map((a) =>
|
|
1146
|
+
const args = (d.args ?? []).map((a) => typeValueToString7(a)).join(",");
|
|
1121
1147
|
return `c:${d.name ?? ""}:${args}`;
|
|
1122
1148
|
}
|
|
1123
1149
|
return d.kind;
|
|
@@ -1576,23 +1602,23 @@ function resolveModule(source, fromDir) {
|
|
|
1576
1602
|
const extensions = [".js", ".ts", ".mjs"];
|
|
1577
1603
|
const nudoPath = resolveNpmNudo(source, fromDir);
|
|
1578
1604
|
if (nudoPath) {
|
|
1579
|
-
const src =
|
|
1605
|
+
const src = readFileSync5(nudoPath, "utf-8");
|
|
1580
1606
|
return { ast: parse5(src), filePath: nudoPath };
|
|
1581
1607
|
}
|
|
1582
|
-
const basePath =
|
|
1608
|
+
const basePath = resolve5(fromDir, source);
|
|
1583
1609
|
for (const ext of ["", ...extensions]) {
|
|
1584
1610
|
const candidate = basePath + ext;
|
|
1585
|
-
if (!
|
|
1586
|
-
if (
|
|
1611
|
+
if (!existsSync4(candidate)) continue;
|
|
1612
|
+
if (statSync4(candidate).isDirectory()) {
|
|
1587
1613
|
let entry = null;
|
|
1588
|
-
const pkgPath =
|
|
1589
|
-
if (
|
|
1614
|
+
const pkgPath = resolve5(candidate, "package.json");
|
|
1615
|
+
if (existsSync4(pkgPath)) {
|
|
1590
1616
|
try {
|
|
1591
|
-
const main = JSON.parse(
|
|
1617
|
+
const main = JSON.parse(readFileSync5(pkgPath, "utf-8")).main;
|
|
1592
1618
|
if (typeof main === "string") {
|
|
1593
1619
|
for (const e of ["", ...extensions]) {
|
|
1594
|
-
const p =
|
|
1595
|
-
if (
|
|
1620
|
+
const p = resolve5(candidate, main + e);
|
|
1621
|
+
if (existsSync4(p) && statSync4(p).isFile()) {
|
|
1596
1622
|
entry = p;
|
|
1597
1623
|
break;
|
|
1598
1624
|
}
|
|
@@ -1603,34 +1629,34 @@ function resolveModule(source, fromDir) {
|
|
|
1603
1629
|
}
|
|
1604
1630
|
if (!entry) {
|
|
1605
1631
|
for (const e of ["", ...extensions]) {
|
|
1606
|
-
const p =
|
|
1607
|
-
if (
|
|
1632
|
+
const p = resolve5(candidate, "index" + e);
|
|
1633
|
+
if (existsSync4(p) && statSync4(p).isFile()) {
|
|
1608
1634
|
entry = p;
|
|
1609
1635
|
break;
|
|
1610
1636
|
}
|
|
1611
1637
|
}
|
|
1612
1638
|
}
|
|
1613
1639
|
if (!entry) return null;
|
|
1614
|
-
const src2 =
|
|
1640
|
+
const src2 = readFileSync5(entry, "utf-8");
|
|
1615
1641
|
return { ast: parse5(src2), filePath: entry };
|
|
1616
1642
|
}
|
|
1617
1643
|
if (candidate.endsWith(".json")) {
|
|
1618
1644
|
try {
|
|
1619
|
-
return { ast: parse5("module.exports = undefined;"), filePath: candidate, json: JSON.parse(
|
|
1645
|
+
return { ast: parse5("module.exports = undefined;"), filePath: candidate, json: JSON.parse(readFileSync5(candidate, "utf-8")) };
|
|
1620
1646
|
} catch {
|
|
1621
1647
|
return null;
|
|
1622
1648
|
}
|
|
1623
1649
|
}
|
|
1624
|
-
const src =
|
|
1650
|
+
const src = readFileSync5(candidate, "utf-8");
|
|
1625
1651
|
return { ast: parse5(src), filePath: candidate };
|
|
1626
1652
|
}
|
|
1627
1653
|
return null;
|
|
1628
1654
|
}
|
|
1629
1655
|
function resolveImportPath(specifier, fromDir) {
|
|
1630
|
-
const basePath =
|
|
1656
|
+
const basePath = resolve5(fromDir, specifier);
|
|
1631
1657
|
for (const ext of ["", ".js", ".ts", ".mjs"]) {
|
|
1632
1658
|
const candidate = basePath + ext;
|
|
1633
|
-
if (
|
|
1659
|
+
if (existsSync4(candidate)) return candidate;
|
|
1634
1660
|
}
|
|
1635
1661
|
return null;
|
|
1636
1662
|
}
|
|
@@ -1650,7 +1676,7 @@ function buildModuleGraph(files, cache) {
|
|
|
1650
1676
|
if (cache) {
|
|
1651
1677
|
let stat = null;
|
|
1652
1678
|
try {
|
|
1653
|
-
stat =
|
|
1679
|
+
stat = statSync4(file);
|
|
1654
1680
|
} catch {
|
|
1655
1681
|
}
|
|
1656
1682
|
const cached = stat ? cache.get(file) : void 0;
|
|
@@ -1671,7 +1697,7 @@ function extractImportEdges(file) {
|
|
|
1671
1697
|
const edges = [];
|
|
1672
1698
|
let ast;
|
|
1673
1699
|
try {
|
|
1674
|
-
ast = parse5(
|
|
1700
|
+
ast = parse5(readFileSync5(file, "utf-8"));
|
|
1675
1701
|
} catch {
|
|
1676
1702
|
return edges;
|
|
1677
1703
|
}
|
|
@@ -1679,7 +1705,7 @@ function extractImportEdges(file) {
|
|
|
1679
1705
|
if (stmt.type !== "ImportDeclaration") continue;
|
|
1680
1706
|
const specifier = stmt.source.value;
|
|
1681
1707
|
if (!specifier.startsWith(".") && !specifier.startsWith("/")) continue;
|
|
1682
|
-
const resolved = resolveImportPath(specifier,
|
|
1708
|
+
const resolved = resolveImportPath(specifier, dirname6(file));
|
|
1683
1709
|
if (resolved) edges.push(resolved);
|
|
1684
1710
|
}
|
|
1685
1711
|
return edges;
|
|
@@ -1757,8 +1783,8 @@ function applyMocks(directives, env, filePath, diagnostics) {
|
|
|
1757
1783
|
}
|
|
1758
1784
|
env.bind(d.name, parseTypeValueExpr2(d.expression));
|
|
1759
1785
|
} else if (d.fromPath) {
|
|
1760
|
-
const mockPath =
|
|
1761
|
-
const mockSource =
|
|
1786
|
+
const mockPath = resolve5(dirname6(filePath), d.fromPath);
|
|
1787
|
+
const mockSource = readFileSync5(mockPath, "utf-8");
|
|
1762
1788
|
const mockAst = parse5(mockSource);
|
|
1763
1789
|
const mockEnv = createEnvironment2();
|
|
1764
1790
|
evaluateProgram(mockAst, mockEnv);
|
|
@@ -1822,6 +1848,16 @@ function resolveFunctionNode(node) {
|
|
|
1822
1848
|
}
|
|
1823
1849
|
return node;
|
|
1824
1850
|
}
|
|
1851
|
+
function fnNameLoc(node, fallback) {
|
|
1852
|
+
const unwrap = (n) => n.type === "ExportNamedDeclaration" && n.declaration ? n.declaration : n.type === "ExportDefaultDeclaration" ? n.declaration : n;
|
|
1853
|
+
const d = unwrap(node);
|
|
1854
|
+
if (d.type === "VariableDeclaration") {
|
|
1855
|
+
const id = d.declarations[0]?.id;
|
|
1856
|
+
if (id?.type === "Identifier" && id.loc) return id.loc;
|
|
1857
|
+
}
|
|
1858
|
+
if (d.id?.loc) return d.id.loc;
|
|
1859
|
+
return fallback;
|
|
1860
|
+
}
|
|
1825
1861
|
function isFnExprValue(node) {
|
|
1826
1862
|
return !!node && (node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression");
|
|
1827
1863
|
}
|
|
@@ -2180,14 +2216,14 @@ function collectEnvNames(filePath, source, includeProject) {
|
|
|
2180
2216
|
const fileDirectives = extractFileDirectives(ast);
|
|
2181
2217
|
const fileEnvNames = fileDirectives.filter((d) => d.kind === "env").flatMap((d) => d.envs);
|
|
2182
2218
|
if (!includeProject) return fileEnvNames;
|
|
2183
|
-
const projectConfig = findProjectConfig(
|
|
2219
|
+
const projectConfig = findProjectConfig(dirname6(filePath));
|
|
2184
2220
|
const projectEnvNames = projectConfig?.config.env ?? [];
|
|
2185
2221
|
return [.../* @__PURE__ */ new Set([...projectEnvNames, ...fileEnvNames])];
|
|
2186
2222
|
}
|
|
2187
2223
|
async function analyzeFileAsync(filePath, source, activeCases, externalCallRecords) {
|
|
2188
2224
|
const envNames = collectEnvNames(filePath, source, true);
|
|
2189
2225
|
if (envNames.length > 0) {
|
|
2190
|
-
await preloadPathEnvs(envNames,
|
|
2226
|
+
await preloadPathEnvs(envNames, dirname6(filePath));
|
|
2191
2227
|
}
|
|
2192
2228
|
return analyzeFile(filePath, source, activeCases, externalCallRecords);
|
|
2193
2229
|
}
|
|
@@ -2199,7 +2235,7 @@ function collectCallRecords(filePath, source) {
|
|
|
2199
2235
|
resetEnvModules();
|
|
2200
2236
|
resetMockModules();
|
|
2201
2237
|
setModuleResolver(resolveModule);
|
|
2202
|
-
setCurrentFileDir(
|
|
2238
|
+
setCurrentFileDir(dirname6(filePath));
|
|
2203
2239
|
setCurrentSource(source);
|
|
2204
2240
|
setCallCollector((record) => records.push(record));
|
|
2205
2241
|
try {
|
|
@@ -2330,11 +2366,11 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2330
2366
|
resetEnvModules();
|
|
2331
2367
|
resetMockModules();
|
|
2332
2368
|
setModuleResolver(resolveModule);
|
|
2333
|
-
setCurrentFileDir(
|
|
2369
|
+
setCurrentFileDir(dirname6(filePath));
|
|
2334
2370
|
setCurrentSource(source);
|
|
2335
2371
|
const fileDirectives = extractFileDirectives(ast);
|
|
2336
2372
|
const fileEnvNames = fileDirectives.filter((d) => d.kind === "env").flatMap((d) => d.envs);
|
|
2337
|
-
const projectConfig = findProjectConfig(
|
|
2373
|
+
const projectConfig = findProjectConfig(dirname6(filePath));
|
|
2338
2374
|
const projectEnvNames = projectConfig?.config.env ?? [];
|
|
2339
2375
|
const envNames = [.../* @__PURE__ */ new Set([...projectEnvNames, ...fileEnvNames])];
|
|
2340
2376
|
setUnknownBuiltinHandler((name, loc) => {
|
|
@@ -2352,15 +2388,15 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2352
2388
|
});
|
|
2353
2389
|
const globalEnv = createEnvironment2();
|
|
2354
2390
|
{
|
|
2355
|
-
const loaded = envNames.length > 0 ?
|
|
2356
|
-
const auto = autoHarvestModules(source,
|
|
2391
|
+
const loaded = envNames.length > 0 ? loadEnvs(envNames, globalEnv) : { modules: {} };
|
|
2392
|
+
const auto = autoHarvestModules(source, dirname6(filePath));
|
|
2357
2393
|
const modules = { ...loaded.modules, ...auto };
|
|
2358
2394
|
if (Object.keys(modules).length > 0) setEnvModules(modules);
|
|
2359
2395
|
}
|
|
2360
2396
|
const mocks = /* @__PURE__ */ new Map();
|
|
2361
2397
|
if (projectConfig?.config.mocks) {
|
|
2362
2398
|
for (const [source2, mockPath] of Object.entries(projectConfig.config.mocks)) {
|
|
2363
|
-
mocks.set(source2, { fromPath:
|
|
2399
|
+
mocks.set(source2, { fromPath: resolve5(projectConfig.projectDir, mockPath) });
|
|
2364
2400
|
}
|
|
2365
2401
|
}
|
|
2366
2402
|
const mockModuleDirectives = fileDirectives.filter((d) => d.kind === "mock-module");
|
|
@@ -2838,8 +2874,47 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2838
2874
|
}
|
|
2839
2875
|
return n;
|
|
2840
2876
|
};
|
|
2841
|
-
const currentModulePath = normalizeModulePath(
|
|
2877
|
+
const currentModulePath = normalizeModulePath(resolve5(filePath));
|
|
2842
2878
|
const singleExportFn = findSingleModuleExportsFunction(ast);
|
|
2879
|
+
const reportInjectedDomainExceeds = (name, node, fallbackLoc) => {
|
|
2880
|
+
if (!externalCallRecords || externalCallRecords.length === 0) return;
|
|
2881
|
+
const singleExportHit = singleExportFn !== null && node === singleExportFn;
|
|
2882
|
+
const nameRoutes = (r) => r.fnName === name || r.targetExport === name || (r.targetAliases?.includes(name) ?? false) || singleExportHit && (r.fnModule !== void 0 || r.targetModule !== void 0);
|
|
2883
|
+
const matchingExternal = (r) => {
|
|
2884
|
+
const attributed = r.fnModule !== void 0 && normalizeModulePath(r.fnModule) === currentModulePath || r.targetModule !== void 0 && normalizeModulePath(r.targetModule) === currentModulePath;
|
|
2885
|
+
if (!attributed) return false;
|
|
2886
|
+
return nameRoutes(r);
|
|
2887
|
+
};
|
|
2888
|
+
const injected = externalCallRecords.filter(matchingExternal);
|
|
2889
|
+
if (injected.length === 0) return;
|
|
2890
|
+
const nameLoc = fnNameLoc(node, fallbackLoc);
|
|
2891
|
+
const fnNode = resolveFunctionNode(node);
|
|
2892
|
+
const autoBind = interfaceConfig(findProjectConfig(dirname6(filePath))?.config).autoBind;
|
|
2893
|
+
const domainIssues = checkInjectedDomainEvidence(name, source, injected, {
|
|
2894
|
+
paramNames: extractParamNames(fnNode),
|
|
2895
|
+
loadModule: defaultLoadModule,
|
|
2896
|
+
fromFile: filePath,
|
|
2897
|
+
loc: { line: nameLoc.start.line, column: nameLoc.start.column },
|
|
2898
|
+
...autoBind === false ? { autoBind: false } : {}
|
|
2899
|
+
});
|
|
2900
|
+
for (const issue of domainIssues) {
|
|
2901
|
+
const line = issue.line ?? nameLoc.start.line;
|
|
2902
|
+
const column = issue.column ?? nameLoc.start.column;
|
|
2903
|
+
diagnostics.push({
|
|
2904
|
+
range: { start: { line, column }, end: { line, column: column + name.length } },
|
|
2905
|
+
severity: issue.severity,
|
|
2906
|
+
message: issue.message,
|
|
2907
|
+
code: issue.code,
|
|
2908
|
+
suggestions: issue.suggestion ? [issue.suggestion] : void 0,
|
|
2909
|
+
data: { actual: issue.actual, expected: issue.expected }
|
|
2910
|
+
});
|
|
2911
|
+
}
|
|
2912
|
+
};
|
|
2913
|
+
for (const fn of functions) {
|
|
2914
|
+
if (fn.directives.some((d) => d.kind === "case")) {
|
|
2915
|
+
reportInjectedDomainExceeds(fn.name, fn.node, locFromNode(fn.node));
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2843
2918
|
for (const candidate of synthCandidates) {
|
|
2844
2919
|
const singleExportHit = singleExportFn !== null && candidate.node === singleExportFn;
|
|
2845
2920
|
const nameRoutes = (r) => r.fnName === candidate.name || r.targetExport === candidate.name || (r.targetAliases?.includes(candidate.name) ?? false) || singleExportHit && (r.fnModule !== void 0 || r.targetModule !== void 0);
|
|
@@ -2859,6 +2934,7 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
|
|
|
2859
2934
|
...(externalCallRecords ?? []).filter(matchingExternal)
|
|
2860
2935
|
].filter((r) => !(r.resultType.kind === "never" && r.throws.kind === "never"))
|
|
2861
2936
|
);
|
|
2937
|
+
reportInjectedDomainExceeds(candidate.name, candidate.node, candidate.analysis.loc);
|
|
2862
2938
|
if (records.length > 0) {
|
|
2863
2939
|
const informativeness = (r) => {
|
|
2864
2940
|
if (r.resultType.kind === "unknown") return 2;
|
|
@@ -3101,136 +3177,338 @@ function buildNodeTypeMap(ast, env, nodeTypeMap) {
|
|
|
3101
3177
|
} catch {
|
|
3102
3178
|
}
|
|
3103
3179
|
}
|
|
3104
|
-
function
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
const cases = fn.directives.filter((d) => d.kind === "case").map((d, i) => ({ name: d.name, index: i }));
|
|
3109
|
-
return { functionName: fn.name, cases, loc: locFromNode(fn.node) };
|
|
3110
|
-
});
|
|
3111
|
-
}
|
|
3112
|
-
async function getTypeAtPositionAsync(filePath, source, line, column, activeCases) {
|
|
3113
|
-
const envNames = collectEnvNames(filePath, source, false);
|
|
3114
|
-
if (envNames.length > 0) {
|
|
3115
|
-
await preloadPathEnvs(envNames, dirname5(filePath));
|
|
3116
|
-
}
|
|
3117
|
-
return getTypeAtPosition(filePath, source, line, column, activeCases);
|
|
3118
|
-
}
|
|
3119
|
-
function positionInsideCaseFunction(source, ast, line) {
|
|
3120
|
-
try {
|
|
3121
|
-
const enclosing = findEnclosingFunction(extractDirectives(ast), line);
|
|
3122
|
-
return !!enclosing && enclosing.directives.some((d) => d.kind === "case");
|
|
3123
|
-
} catch {
|
|
3180
|
+
function absIsBetter(absTv, prev) {
|
|
3181
|
+
if (prev.kind === "unknown") return absTv.kind !== "unknown";
|
|
3182
|
+
if (absTv.kind === "unknown" || absTv.kind === "never") return false;
|
|
3183
|
+
if (prev.kind === "literal" || prev.kind === "object" || prev.kind === "array" || prev.kind === "tuple" || prev.kind === "union" || prev.kind === "function" || prev.kind === "instance" || prev.kind === "promise") {
|
|
3124
3184
|
return false;
|
|
3125
3185
|
}
|
|
3186
|
+
if (absTv.kind === "refined" && prev.kind === "primitive") return true;
|
|
3187
|
+
return false;
|
|
3126
3188
|
}
|
|
3127
|
-
function
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
seedFns: seeds.seedFns
|
|
3189
|
+
function isSelfContainedSource(source, envNames) {
|
|
3190
|
+
if (envNames.length > 0) return false;
|
|
3191
|
+
return !/\brequire\s*\(|\bimport\s*[{'"*]/.test(source);
|
|
3192
|
+
}
|
|
3193
|
+
function absModulesOk(source, envNames) {
|
|
3194
|
+
void source;
|
|
3195
|
+
void envNames;
|
|
3196
|
+
return true;
|
|
3197
|
+
}
|
|
3198
|
+
function collectAbsCallRecords(source, seeds, filePath, precomputedModules, envNames = []) {
|
|
3199
|
+
const absCalls = [];
|
|
3200
|
+
let modules = precomputedModules;
|
|
3201
|
+
let importLocals = /* @__PURE__ */ new Map();
|
|
3202
|
+
if (filePath && absModulesOk(source, envNames)) {
|
|
3203
|
+
if (!modules) {
|
|
3204
|
+
try {
|
|
3205
|
+
const graph = evalAbsModuleGraph(source, filePath, {
|
|
3206
|
+
seedVars: seeds?.seedVars,
|
|
3207
|
+
seedFns: seeds?.seedFns
|
|
3147
3208
|
});
|
|
3148
|
-
|
|
3149
|
-
|
|
3209
|
+
modules = graph.modules;
|
|
3210
|
+
} catch {
|
|
3211
|
+
modules = void 0;
|
|
3150
3212
|
}
|
|
3213
|
+
}
|
|
3214
|
+
if (envNames.length > 0) {
|
|
3215
|
+
modules = { ...collectEnvModules(envNames), ...modules ?? {} };
|
|
3216
|
+
}
|
|
3217
|
+
try {
|
|
3218
|
+
importLocals = buildAbsImportLocalMap(source, filePath);
|
|
3151
3219
|
} catch {
|
|
3220
|
+
importLocals = /* @__PURE__ */ new Map();
|
|
3152
3221
|
}
|
|
3153
3222
|
}
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
{
|
|
3161
|
-
const loaded = envNames.length > 0 ? loadEnvs2(envNames, globalEnv) : { modules: {} };
|
|
3162
|
-
const auto = autoHarvestModules(source, dirname5(filePath));
|
|
3163
|
-
const modules = { ...loaded.modules, ...auto };
|
|
3164
|
-
if (Object.keys(modules).length > 0) setEnvModules(modules);
|
|
3223
|
+
setAbsCallCollector((r) => absCalls.push(r));
|
|
3224
|
+
try {
|
|
3225
|
+
evalProgramAbs2(source, { ...seeds, modules });
|
|
3226
|
+
} catch {
|
|
3227
|
+
} finally {
|
|
3228
|
+
setAbsCallCollector(null);
|
|
3165
3229
|
}
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
evaluateFunctionFull(enclosingFn.node, directive.args, globalEnv);
|
|
3179
|
-
setNodeTypeCollector(null);
|
|
3180
|
-
for (const [node, tv] of fnNodeTypeMap) {
|
|
3181
|
-
nodeTypeMap.set(node, tv);
|
|
3182
|
-
}
|
|
3230
|
+
return absCalls.map((r) => {
|
|
3231
|
+
const rec = {
|
|
3232
|
+
fnName: r.fnName,
|
|
3233
|
+
argTypes: r.args.map((a) => absToTypeValue2(a)),
|
|
3234
|
+
resultType: r.threw ? T2.never : absToTypeValue2(r.result),
|
|
3235
|
+
throws: r.threw ? absToTypeValue2(r.result) : T2.never,
|
|
3236
|
+
callLoc: r.callLoc
|
|
3237
|
+
};
|
|
3238
|
+
const imp = importLocals.get(r.fnName);
|
|
3239
|
+
if (imp) {
|
|
3240
|
+
rec.targetModule = imp.modulePath;
|
|
3241
|
+
rec.targetExport = imp.exportName;
|
|
3183
3242
|
}
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
resetEnvModules();
|
|
3187
|
-
resetMockModules();
|
|
3188
|
-
return findBestTypeAtPosition(nodeTypeMap, globalEnv, ast, line, column);
|
|
3243
|
+
return rec;
|
|
3244
|
+
});
|
|
3189
3245
|
}
|
|
3190
|
-
function
|
|
3191
|
-
|
|
3246
|
+
function collectAbsBindsAndNodes(source, seeds, modules) {
|
|
3247
|
+
const binds = /* @__PURE__ */ new Map();
|
|
3248
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
3249
|
+
setAbsNodeCollector((node, value) => {
|
|
3250
|
+
nodes.set(node, value);
|
|
3251
|
+
});
|
|
3192
3252
|
try {
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
const g = generalizeFromAst2(fnName, source, file ? { file } : {});
|
|
3205
|
-
if (g) {
|
|
3206
|
-
gDisplay = g.display;
|
|
3207
|
-
gAbs = formatAbs3(g.symbolic);
|
|
3208
|
-
gMulti = formatAbsMultiline(g.symbolic, fnName);
|
|
3253
|
+
const { env } = evalProgramAbs2(source, {
|
|
3254
|
+
...seeds,
|
|
3255
|
+
modules
|
|
3256
|
+
});
|
|
3257
|
+
for (const [k, v] of env.vars) binds.set(k, v);
|
|
3258
|
+
for (const [name, impl] of env.fns) {
|
|
3259
|
+
if (!binds.has(name)) {
|
|
3260
|
+
binds.set(
|
|
3261
|
+
name,
|
|
3262
|
+
absFunction5(impl.params, { body: impl.body, async: impl.async, env })
|
|
3263
|
+
);
|
|
3209
3264
|
}
|
|
3210
|
-
} catch {
|
|
3211
3265
|
}
|
|
3266
|
+
} catch {
|
|
3267
|
+
} finally {
|
|
3268
|
+
setAbsNodeCollector(null);
|
|
3212
3269
|
}
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3270
|
+
return { binds, nodes };
|
|
3271
|
+
}
|
|
3272
|
+
function buildAbsImportLocalMap(source, fromFile) {
|
|
3273
|
+
const out = /* @__PURE__ */ new Map();
|
|
3274
|
+
try {
|
|
3275
|
+
const file = parse5(source);
|
|
3276
|
+
for (const stmt of file.program.body) {
|
|
3277
|
+
if (stmt.type !== "ImportDeclaration") continue;
|
|
3278
|
+
const spec = stmt.source.value;
|
|
3279
|
+
let modulePath = null;
|
|
3280
|
+
if (spec.startsWith(".") || spec.startsWith("/")) {
|
|
3281
|
+
modulePath = resolveImportAbs(spec, fromFile);
|
|
3282
|
+
} else if (!spec.startsWith("node:")) {
|
|
3283
|
+
modulePath = spec;
|
|
3284
|
+
}
|
|
3285
|
+
if (!modulePath) continue;
|
|
3286
|
+
for (const s of stmt.specifiers) {
|
|
3287
|
+
if (s.type === "ImportSpecifier") {
|
|
3288
|
+
const imported = s.imported.type === "Identifier" ? s.imported.name : String(s.imported);
|
|
3289
|
+
out.set(s.local.name, { modulePath, exportName: imported });
|
|
3290
|
+
} else if (s.type === "ImportDefaultSpecifier") {
|
|
3291
|
+
out.set(s.local.name, { modulePath, exportName: "default" });
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
}
|
|
3295
|
+
} catch {
|
|
3296
|
+
}
|
|
3297
|
+
return out;
|
|
3298
|
+
}
|
|
3299
|
+
function resolveImportAbs(spec, fromFile) {
|
|
3300
|
+
const base = dirname6(resolve5(fromFile));
|
|
3301
|
+
const p = resolve5(base, spec);
|
|
3302
|
+
for (const cand of [p, `${p}.js`, `${p}.mjs`, `${p}.ts`, resolve5(p, "index.js")]) {
|
|
3303
|
+
if (existsSync4(cand) && !statSync4(cand).isDirectory()) return cand;
|
|
3304
|
+
}
|
|
3305
|
+
return null;
|
|
3306
|
+
}
|
|
3307
|
+
function tryEvalAbs(source, fnName, args, filePath, mocks) {
|
|
3308
|
+
const raw = tryEvalAbsRaw(source, fnName, args, filePath, mocks);
|
|
3309
|
+
return raw ? absToTypeValue2(raw) : void 0;
|
|
3310
|
+
}
|
|
3311
|
+
function tryEvalAbsRaw(source, fnName, args, filePath, mocks) {
|
|
3312
|
+
if (/\brequire\s*\(/.test(source)) return void 0;
|
|
3313
|
+
try {
|
|
3314
|
+
const absArgs = args.map((a) => typeValueToAbs5(a));
|
|
3315
|
+
if (filePath) {
|
|
3316
|
+
const viaB = tryBPathCall(source, filePath, fnName, absArgs, { mocks });
|
|
3317
|
+
if (viaB) return viaB;
|
|
3318
|
+
}
|
|
3319
|
+
let modules;
|
|
3320
|
+
if (filePath && /\bimport\s*[{'"*]/.test(source)) {
|
|
3321
|
+
modules = evalAbsModuleGraph(source, filePath).modules;
|
|
3322
|
+
}
|
|
3323
|
+
const result = analyzeFn(source, fnName, absArgs, void 0, void 0, void 0, modules);
|
|
3324
|
+
if (result.shape.k === "unknown" && !result.term) {
|
|
3325
|
+
return void 0;
|
|
3326
|
+
}
|
|
3327
|
+
return result;
|
|
3328
|
+
} catch {
|
|
3329
|
+
return void 0;
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
function tryEvalEntryAbs(source, fnName, args, filePath, mocks) {
|
|
3333
|
+
return tryEvalAbs(source, fnName, args, filePath, mocks);
|
|
3334
|
+
}
|
|
3335
|
+
function tryAttachIntension(caseResult, source, fnName) {
|
|
3336
|
+
try {
|
|
3337
|
+
const g = generalizeFromAst2(fnName, source);
|
|
3338
|
+
if (!g) return;
|
|
3339
|
+
const sym = g.symbolic;
|
|
3340
|
+
const intension = {
|
|
3341
|
+
display: g.display,
|
|
3342
|
+
abs: formatAbs3(sym),
|
|
3343
|
+
absMultiline: formatAbsMultiline(sym, fnName)
|
|
3344
|
+
};
|
|
3345
|
+
if (sym.term && sym.term.op !== "lit") {
|
|
3346
|
+
intension.term = termToString(sym.term);
|
|
3347
|
+
}
|
|
3348
|
+
if (sym.pred && sym.pred.op !== "true") {
|
|
3349
|
+
intension.pred = predToString(sym.pred);
|
|
3350
|
+
}
|
|
3351
|
+
intension.conf = sym.conf;
|
|
3352
|
+
caseResult.intension = intension;
|
|
3353
|
+
} catch {
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
function attachAbsToIntension(caseResult, absVal, label) {
|
|
3357
|
+
const prev = caseResult.intension ?? {};
|
|
3358
|
+
caseResult.abs = absVal;
|
|
3359
|
+
caseResult.intension = {
|
|
3360
|
+
...prev,
|
|
3361
|
+
abs: formatAbs3(absVal),
|
|
3362
|
+
absMultiline: formatAbsMultiline(absVal, label ?? caseResult.name),
|
|
3363
|
+
conf: absVal.conf
|
|
3364
|
+
};
|
|
3365
|
+
}
|
|
3366
|
+
|
|
3367
|
+
// src/lsp-surface.ts
|
|
3368
|
+
import { dirname as dirname7 } from "path";
|
|
3369
|
+
import traverse2 from "@babel/traverse";
|
|
3370
|
+
import {
|
|
3371
|
+
typeValueToString as typeValueToString4,
|
|
3372
|
+
createEnvironment as createEnvironment3,
|
|
3373
|
+
generalizeFromAst as generalizeFromAst3,
|
|
3374
|
+
formatAbs as formatAbs4,
|
|
3375
|
+
formatAbsMultiline as formatAbsMultiline2,
|
|
3376
|
+
getFnSig as getFnSig3,
|
|
3377
|
+
collectAbsNodeTypes,
|
|
3378
|
+
findAbsAtPosition,
|
|
3379
|
+
absToTypeValue as absToTypeValue3
|
|
3380
|
+
} from "@nudojs/core";
|
|
3381
|
+
import { parse as parse6, extractDirectives as extractDirectives2, extractFileDirectives as extractFileDirectives2 } from "@nudojs/parser";
|
|
3382
|
+
function getCasesForFile(filePath, source) {
|
|
3383
|
+
const ast = parse6(source);
|
|
3384
|
+
const functions = extractDirectives2(ast);
|
|
3385
|
+
return functions.map((fn) => {
|
|
3386
|
+
const cases = fn.directives.filter((d) => d.kind === "case").map((d, i) => ({ name: d.name, index: i }));
|
|
3387
|
+
return { functionName: fn.name, cases, loc: locFromNode(fn.node) };
|
|
3388
|
+
});
|
|
3389
|
+
}
|
|
3390
|
+
async function getTypeAtPositionAsync(filePath, source, line, column, activeCases) {
|
|
3391
|
+
const envNames = collectEnvNames(filePath, source, false);
|
|
3392
|
+
if (envNames.length > 0) {
|
|
3393
|
+
await preloadPathEnvs(envNames, dirname7(filePath));
|
|
3394
|
+
}
|
|
3395
|
+
return getTypeAtPosition(filePath, source, line, column, activeCases);
|
|
3396
|
+
}
|
|
3397
|
+
function positionInsideCaseFunction(source, ast, line) {
|
|
3398
|
+
try {
|
|
3399
|
+
const enclosing = findEnclosingFunction(extractDirectives2(ast), line);
|
|
3400
|
+
return !!enclosing && enclosing.directives.some((d) => d.kind === "case");
|
|
3401
|
+
} catch {
|
|
3402
|
+
return false;
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
function getTypeAtPosition(filePath, source, line, column, activeCases) {
|
|
3406
|
+
const ast = parse6(source);
|
|
3407
|
+
const fileDirectives = extractFileDirectives2(ast);
|
|
3408
|
+
const envNames = fileDirectives.filter((d) => d.kind === "env").flatMap((d) => d.envs);
|
|
3409
|
+
if (isBPathCapable(source, envNames) && !positionInsideCaseFunction(source, ast, line)) {
|
|
3410
|
+
try {
|
|
3411
|
+
const seeds = mockDirectivesToAbsSeeds(extractDirectives2(ast));
|
|
3412
|
+
const { modules } = evalAbsModuleGraph(source, filePath);
|
|
3413
|
+
const absNodes = collectAbsNodeTypes(source, {
|
|
3414
|
+
...seeds,
|
|
3415
|
+
modules,
|
|
3416
|
+
file: ast
|
|
3417
|
+
});
|
|
3418
|
+
const absAt = findAbsAtPosition(absNodes, line, column);
|
|
3419
|
+
if (absAt) return absToTypeValue3(absAt);
|
|
3420
|
+
const ident = findIdentNameAtPosition(source, line, column, ast);
|
|
3421
|
+
if (ident) {
|
|
3422
|
+
const binds = collectAbsBindingsFromGraph(source, filePath, {
|
|
3423
|
+
seedVars: seeds.seedVars,
|
|
3424
|
+
seedFns: seeds.seedFns
|
|
3425
|
+
});
|
|
3426
|
+
const bound = binds.get(ident);
|
|
3427
|
+
if (bound) return absToTypeValue3(bound);
|
|
3428
|
+
}
|
|
3429
|
+
} catch {
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
resetMemo();
|
|
3433
|
+
resetEnvModules();
|
|
3434
|
+
setModuleResolver(resolveModule);
|
|
3435
|
+
setCurrentFileDir(dirname7(filePath));
|
|
3436
|
+
setCurrentSource(source);
|
|
3437
|
+
const globalEnv = createEnvironment3();
|
|
3438
|
+
{
|
|
3439
|
+
const loaded = envNames.length > 0 ? loadEnvs(envNames, globalEnv) : { modules: {} };
|
|
3440
|
+
const auto = autoHarvestModules(source, dirname7(filePath));
|
|
3441
|
+
const modules = { ...loaded.modules, ...auto };
|
|
3442
|
+
if (Object.keys(modules).length > 0) setEnvModules(modules);
|
|
3443
|
+
}
|
|
3444
|
+
evaluateProgram(ast, globalEnv);
|
|
3445
|
+
const nodeTypeMap = /* @__PURE__ */ new Map();
|
|
3446
|
+
buildNodeTypeMap(ast, globalEnv, nodeTypeMap);
|
|
3447
|
+
const functions = extractDirectives2(ast);
|
|
3448
|
+
const enclosingFn = findEnclosingFunction(functions, line);
|
|
3449
|
+
if (enclosingFn) {
|
|
3450
|
+
const caseDirectives = enclosingFn.directives.filter((d) => d.kind === "case");
|
|
3451
|
+
if (caseDirectives.length > 0) {
|
|
3452
|
+
const caseIndex = activeCases?.get(enclosingFn.name) ?? 0;
|
|
3453
|
+
const directive = caseDirectives[Math.min(caseIndex, caseDirectives.length - 1)];
|
|
3454
|
+
const fnNodeTypeMap = /* @__PURE__ */ new Map();
|
|
3455
|
+
setNodeTypeCollector((node, tv) => fnNodeTypeMap.set(node, tv));
|
|
3456
|
+
evaluateFunctionFull(enclosingFn.node, directive.args, globalEnv);
|
|
3457
|
+
setNodeTypeCollector(null);
|
|
3458
|
+
for (const [node, tv] of fnNodeTypeMap) {
|
|
3459
|
+
nodeTypeMap.set(node, tv);
|
|
3460
|
+
}
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
setModuleResolver(null);
|
|
3464
|
+
resetEnvModules();
|
|
3465
|
+
resetMockModules();
|
|
3466
|
+
return findBestTypeAtPosition(nodeTypeMap, globalEnv, ast, line, column);
|
|
3467
|
+
}
|
|
3468
|
+
function getHoverAtPosition(filePath, source, line, column, activeCases) {
|
|
3469
|
+
let file;
|
|
3470
|
+
try {
|
|
3471
|
+
file = parse6(source);
|
|
3472
|
+
} catch {
|
|
3473
|
+
file = void 0;
|
|
3474
|
+
}
|
|
3475
|
+
const envNames = collectEnvNames(filePath, source, false);
|
|
3476
|
+
const fnName = findFunctionNameAtPosition(source, line, column, file);
|
|
3477
|
+
let gDisplay;
|
|
3478
|
+
let gAbs;
|
|
3479
|
+
let gMulti;
|
|
3480
|
+
if (fnName) {
|
|
3481
|
+
try {
|
|
3482
|
+
const g = generalizeFromAst3(fnName, source, file ? { file } : {});
|
|
3483
|
+
if (g) {
|
|
3484
|
+
gDisplay = g.display;
|
|
3485
|
+
gAbs = formatAbs4(g.symbolic);
|
|
3486
|
+
gMulti = formatAbsMultiline2(g.symbolic, fnName);
|
|
3487
|
+
}
|
|
3488
|
+
} catch {
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
const attachIntension = (info2) => {
|
|
3492
|
+
if (!gDisplay) return info2;
|
|
3493
|
+
if (!info2) {
|
|
3494
|
+
return { typeText: gDisplay, intension: gDisplay, abs: gAbs, absMultiline: gMulti };
|
|
3495
|
+
}
|
|
3496
|
+
return {
|
|
3497
|
+
...info2,
|
|
3498
|
+
intension: gDisplay,
|
|
3499
|
+
// 外延侧已有更准 Abs 时保留;否则用 symbolic 兜底
|
|
3500
|
+
abs: info2.abs ?? gAbs,
|
|
3223
3501
|
absMultiline: info2.absMultiline ?? gMulti
|
|
3224
3502
|
};
|
|
3225
3503
|
};
|
|
3226
3504
|
const insideCaseFn = positionInsideCaseFunction(
|
|
3227
3505
|
source,
|
|
3228
|
-
file ??
|
|
3506
|
+
file ?? parse6(source),
|
|
3229
3507
|
line
|
|
3230
3508
|
);
|
|
3231
3509
|
if (isBPathCapable(source, envNames) && !insideCaseFn) {
|
|
3232
3510
|
try {
|
|
3233
|
-
const seeds = mockDirectivesToAbsSeeds(
|
|
3511
|
+
const seeds = mockDirectivesToAbsSeeds(extractDirectives2(file ?? parse6(source)));
|
|
3234
3512
|
const { modules } = evalAbsModuleGraph(source, filePath);
|
|
3235
3513
|
const absNodes = collectAbsNodeTypes(source, {
|
|
3236
3514
|
...seeds,
|
|
@@ -3246,34 +3524,34 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
|
|
|
3246
3524
|
});
|
|
3247
3525
|
const bound = binds.get(ident2);
|
|
3248
3526
|
if (bound) {
|
|
3249
|
-
const absLine =
|
|
3250
|
-
const absMulti =
|
|
3527
|
+
const absLine = formatAbs4(bound);
|
|
3528
|
+
const absMulti = formatAbsMultiline2(bound, ident2);
|
|
3251
3529
|
return attachIntension({ typeText: absLine, abs: absLine, absMultiline: absMulti });
|
|
3252
3530
|
}
|
|
3253
3531
|
}
|
|
3254
3532
|
if (absAt) {
|
|
3255
|
-
const absLine =
|
|
3256
|
-
const absMulti =
|
|
3533
|
+
const absLine = formatAbs4(absAt);
|
|
3534
|
+
const absMulti = formatAbsMultiline2(absAt, void 0);
|
|
3257
3535
|
return attachIntension({ typeText: absLine, abs: absLine, absMultiline: absMulti });
|
|
3258
3536
|
}
|
|
3259
3537
|
} catch {
|
|
3260
3538
|
}
|
|
3261
3539
|
}
|
|
3262
3540
|
const tv = getTypeAtPosition(filePath, source, line, column, activeCases);
|
|
3263
|
-
const info = tv ? { typeText:
|
|
3541
|
+
const info = tv ? { typeText: typeValueToString4(tv) } : null;
|
|
3264
3542
|
const ident = findIdentNameAtPosition(source, line, column, file);
|
|
3265
3543
|
if (ident && !fnName && !insideCaseFn) {
|
|
3266
3544
|
try {
|
|
3267
3545
|
if (isBPathCapable(source, []) || !/\brequire\s*\(/.test(source)) {
|
|
3268
|
-
const seeds = mockDirectivesToAbsSeeds(
|
|
3546
|
+
const seeds = mockDirectivesToAbsSeeds(extractDirectives2(file ?? parse6(source)));
|
|
3269
3547
|
const absBinds = collectAbsBindingsFromGraph(source, filePath, {
|
|
3270
3548
|
seedVars: seeds.seedVars,
|
|
3271
3549
|
seedFns: seeds.seedFns
|
|
3272
3550
|
});
|
|
3273
3551
|
const absBound = absBinds.get(ident);
|
|
3274
3552
|
if (absBound) {
|
|
3275
|
-
const absLine =
|
|
3276
|
-
const absMulti =
|
|
3553
|
+
const absLine = formatAbs4(absBound);
|
|
3554
|
+
const absMulti = formatAbsMultiline2(absBound, ident);
|
|
3277
3555
|
if (info) {
|
|
3278
3556
|
info.abs = absLine;
|
|
3279
3557
|
info.absMultiline = absMulti;
|
|
@@ -3288,7 +3566,7 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
|
|
|
3288
3566
|
try {
|
|
3289
3567
|
if (!insideCaseFn && (isBPathCapable(source, []) || !/\brequire\s*\(|\bimport\s*[{'"*]/.test(source))) {
|
|
3290
3568
|
const seeds = mockDirectivesToAbsSeeds(
|
|
3291
|
-
|
|
3569
|
+
extractDirectives2(file ?? parse6(source))
|
|
3292
3570
|
);
|
|
3293
3571
|
const { modules } = evalAbsModuleGraph(source, filePath);
|
|
3294
3572
|
const nodeTypes = collectAbsNodeTypes(source, {
|
|
@@ -3298,8 +3576,8 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
|
|
|
3298
3576
|
});
|
|
3299
3577
|
const absAt = findAbsAtPosition(nodeTypes, line, column);
|
|
3300
3578
|
if (absAt) {
|
|
3301
|
-
const absLine =
|
|
3302
|
-
const absMulti =
|
|
3579
|
+
const absLine = formatAbs4(absAt);
|
|
3580
|
+
const absMulti = formatAbsMultiline2(absAt, void 0);
|
|
3303
3581
|
if (info) {
|
|
3304
3582
|
info.abs = absLine;
|
|
3305
3583
|
info.absMultiline = absMulti;
|
|
@@ -3314,9 +3592,9 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
|
|
|
3314
3592
|
}
|
|
3315
3593
|
function findIdentNameAtPosition(source, line, column, fileAst) {
|
|
3316
3594
|
try {
|
|
3317
|
-
const ast = fileAst ??
|
|
3595
|
+
const ast = fileAst ?? parse6(source);
|
|
3318
3596
|
let found;
|
|
3319
|
-
|
|
3597
|
+
traverse2(ast, {
|
|
3320
3598
|
Identifier(path) {
|
|
3321
3599
|
const loc = path.node.loc;
|
|
3322
3600
|
if (!loc) return;
|
|
@@ -3332,9 +3610,9 @@ function findIdentNameAtPosition(source, line, column, fileAst) {
|
|
|
3332
3610
|
}
|
|
3333
3611
|
function findFunctionNameAtPosition(source, line, column, fileAst) {
|
|
3334
3612
|
try {
|
|
3335
|
-
const ast = fileAst ??
|
|
3613
|
+
const ast = fileAst ?? parse6(source);
|
|
3336
3614
|
let found;
|
|
3337
|
-
|
|
3615
|
+
traverse2(ast, {
|
|
3338
3616
|
Identifier(path) {
|
|
3339
3617
|
const loc = path.node.loc;
|
|
3340
3618
|
if (!loc) return;
|
|
@@ -3387,7 +3665,7 @@ function findBestTypeAtPosition(nodeTypeMap, globalEnv, ast, line, column) {
|
|
|
3387
3665
|
}
|
|
3388
3666
|
function findIdentifierAtPosition(ast, line, column) {
|
|
3389
3667
|
let found = null;
|
|
3390
|
-
const traverseFn = typeof
|
|
3668
|
+
const traverseFn = typeof traverse2 === "function" ? traverse2 : traverse2.default;
|
|
3391
3669
|
try {
|
|
3392
3670
|
traverseFn(ast, {
|
|
3393
3671
|
Identifier(path) {
|
|
@@ -3411,18 +3689,18 @@ function getCompletionsAtPosition(filePath, source, line, column) {
|
|
|
3411
3689
|
const safeSource = sanitizeSourceForParsing(source);
|
|
3412
3690
|
let ast;
|
|
3413
3691
|
try {
|
|
3414
|
-
ast =
|
|
3692
|
+
ast = parse6(safeSource);
|
|
3415
3693
|
} catch {
|
|
3416
3694
|
try {
|
|
3417
|
-
ast =
|
|
3695
|
+
ast = parse6(source);
|
|
3418
3696
|
} catch {
|
|
3419
3697
|
return [];
|
|
3420
3698
|
}
|
|
3421
3699
|
}
|
|
3422
3700
|
resetMemo();
|
|
3423
3701
|
setModuleResolver(resolveModule);
|
|
3424
|
-
setCurrentFileDir(
|
|
3425
|
-
const globalEnv =
|
|
3702
|
+
setCurrentFileDir(dirname7(filePath));
|
|
3703
|
+
const globalEnv = createEnvironment3();
|
|
3426
3704
|
evaluateProgram(ast, globalEnv);
|
|
3427
3705
|
if (!globalEnv.has(objName)) {
|
|
3428
3706
|
setModuleResolver(null);
|
|
@@ -3442,11 +3720,11 @@ function getTextBeforePosition(source, line, column) {
|
|
|
3442
3720
|
return lines[line - 1].slice(0, column);
|
|
3443
3721
|
}
|
|
3444
3722
|
function getVariableCompletions(filePath, source) {
|
|
3445
|
-
const ast =
|
|
3723
|
+
const ast = parse6(source);
|
|
3446
3724
|
resetMemo();
|
|
3447
3725
|
setModuleResolver(resolveModule);
|
|
3448
|
-
setCurrentFileDir(
|
|
3449
|
-
const globalEnv =
|
|
3726
|
+
setCurrentFileDir(dirname7(filePath));
|
|
3727
|
+
const globalEnv = createEnvironment3();
|
|
3450
3728
|
evaluateProgram(ast, globalEnv);
|
|
3451
3729
|
const ownBindings = globalEnv.getOwnBindings();
|
|
3452
3730
|
const completions = [];
|
|
@@ -3455,7 +3733,7 @@ function getVariableCompletions(filePath, source) {
|
|
|
3455
3733
|
completions.push({
|
|
3456
3734
|
label: name,
|
|
3457
3735
|
kind: tv.kind === "function" ? "method" : "variable",
|
|
3458
|
-
detail:
|
|
3736
|
+
detail: typeValueToString4(tv)
|
|
3459
3737
|
});
|
|
3460
3738
|
}
|
|
3461
3739
|
setModuleResolver(null);
|
|
@@ -3463,7 +3741,7 @@ function getVariableCompletions(filePath, source) {
|
|
|
3463
3741
|
}
|
|
3464
3742
|
function builtinMemberType(memberExpr) {
|
|
3465
3743
|
try {
|
|
3466
|
-
const result = evaluate(
|
|
3744
|
+
const result = evaluate(parse6(`${memberExpr};`), createEnvironment3());
|
|
3467
3745
|
if (!result || typeof result !== "object" || !("kind" in result)) return null;
|
|
3468
3746
|
return result;
|
|
3469
3747
|
} catch {
|
|
@@ -3479,10 +3757,10 @@ function describeMember(label, tv, fallbackClass) {
|
|
|
3479
3757
|
const sig = getFnSig3(tv);
|
|
3480
3758
|
if (sig) {
|
|
3481
3759
|
const paramNames = tv.kind === "function" ? tv.params : [];
|
|
3482
|
-
const params = sig.paramTypes.map((p, i) => `${paramNames[i] ?? `arg${i}`}: ${
|
|
3483
|
-
return `(${params}) => ${
|
|
3760
|
+
const params = sig.paramTypes.map((p, i) => `${paramNames[i] ?? `arg${i}`}: ${typeValueToString4(p)}`).join(", ");
|
|
3761
|
+
return `(${params}) => ${typeValueToString4(sig.returnType)}`;
|
|
3484
3762
|
}
|
|
3485
|
-
if (tv.kind !== "function") return
|
|
3763
|
+
if (tv.kind !== "function") return typeValueToString4(tv);
|
|
3486
3764
|
}
|
|
3487
3765
|
return `${label}(\u2026)@${fallbackClass}`;
|
|
3488
3766
|
}
|
|
@@ -3505,281 +3783,95 @@ function getPromiseCompletions() {
|
|
|
3505
3783
|
label: m,
|
|
3506
3784
|
kind: "method",
|
|
3507
3785
|
detail: describeMember(m, builtinMemberType(`Promise.prototype.${m}`), "Promise")
|
|
3508
|
-
}));
|
|
3509
|
-
}
|
|
3510
|
-
function getStringCompletions() {
|
|
3511
|
-
const completions = [];
|
|
3512
|
-
for (const m of builtinProtoMembers("String")) {
|
|
3513
|
-
completions.push({
|
|
3514
|
-
label: m,
|
|
3515
|
-
kind: "method",
|
|
3516
|
-
detail: describeMember(m, builtinMemberType(`"s".${m}`), "String")
|
|
3517
|
-
});
|
|
3518
|
-
}
|
|
3519
|
-
completions.push({ label: "length", kind: "property", detail: "number" });
|
|
3520
|
-
return completions;
|
|
3521
|
-
}
|
|
3522
|
-
function getUnionCompletions(tv) {
|
|
3523
|
-
const members = tv.members;
|
|
3524
|
-
if (members.length === 0) return [];
|
|
3525
|
-
const labelsByMember = members.map((m) => getCompletionsForType(m));
|
|
3526
|
-
const baseIdx = labelsByMember.findIndex((labels) => labels.length > 0);
|
|
3527
|
-
if (baseIdx === -1) return [];
|
|
3528
|
-
const common = [];
|
|
3529
|
-
for (const base of labelsByMember[baseIdx]) {
|
|
3530
|
-
let allPresent = true;
|
|
3531
|
-
const memberTypes = [base.detail ?? base.label];
|
|
3532
|
-
for (let i = 0; i < members.length; i++) {
|
|
3533
|
-
if (i === baseIdx) continue;
|
|
3534
|
-
const labels = labelsByMember[i];
|
|
3535
|
-
if (labels.length === 0) continue;
|
|
3536
|
-
const hit = labels.find((l) => l.label === base.label);
|
|
3537
|
-
if (!hit) {
|
|
3538
|
-
allPresent = false;
|
|
3539
|
-
break;
|
|
3540
|
-
}
|
|
3541
|
-
memberTypes.push(hit.detail ?? hit.label);
|
|
3542
|
-
}
|
|
3543
|
-
if (allPresent) {
|
|
3544
|
-
common.push({ ...base, detail: memberTypes.join(" | ") });
|
|
3545
|
-
}
|
|
3546
|
-
}
|
|
3547
|
-
return common;
|
|
3548
|
-
}
|
|
3549
|
-
function getCompletionsForType(tv) {
|
|
3550
|
-
const completions = [];
|
|
3551
|
-
if (tv.kind === "object") {
|
|
3552
|
-
for (const [key, val] of Object.entries(tv.properties)) {
|
|
3553
|
-
completions.push({
|
|
3554
|
-
label: key,
|
|
3555
|
-
kind: val.kind === "function" ? "method" : "property",
|
|
3556
|
-
detail: typeValueToString3(val)
|
|
3557
|
-
});
|
|
3558
|
-
}
|
|
3559
|
-
return completions;
|
|
3560
|
-
}
|
|
3561
|
-
if (tv.kind === "instance") {
|
|
3562
|
-
for (const [key, val] of Object.entries(tv.properties)) {
|
|
3563
|
-
completions.push({
|
|
3564
|
-
label: key,
|
|
3565
|
-
kind: val.kind === "function" ? "method" : "property",
|
|
3566
|
-
detail: typeValueToString3(val)
|
|
3567
|
-
});
|
|
3568
|
-
}
|
|
3569
|
-
return completions;
|
|
3570
|
-
}
|
|
3571
|
-
if (tv.kind === "union") {
|
|
3572
|
-
return getUnionCompletions(tv);
|
|
3573
|
-
}
|
|
3574
|
-
if (tv.kind === "array" || tv.kind === "tuple") {
|
|
3575
|
-
return getArrayCompletions(tv);
|
|
3576
|
-
}
|
|
3577
|
-
if (tv.kind === "promise") {
|
|
3578
|
-
return getPromiseCompletions();
|
|
3579
|
-
}
|
|
3580
|
-
if (tv.kind === "primitive" && tv.type === "string") {
|
|
3581
|
-
return getStringCompletions();
|
|
3582
|
-
}
|
|
3583
|
-
return completions;
|
|
3584
|
-
}
|
|
3585
|
-
function absIsBetter(absTv, prev) {
|
|
3586
|
-
if (prev.kind === "unknown") return absTv.kind !== "unknown";
|
|
3587
|
-
if (absTv.kind === "unknown" || absTv.kind === "never") return false;
|
|
3588
|
-
if (prev.kind === "literal" || prev.kind === "object" || prev.kind === "array" || prev.kind === "tuple" || prev.kind === "union" || prev.kind === "function" || prev.kind === "instance" || prev.kind === "promise") {
|
|
3589
|
-
return false;
|
|
3590
|
-
}
|
|
3591
|
-
if (absTv.kind === "refined" && prev.kind === "primitive") return true;
|
|
3592
|
-
return false;
|
|
3593
|
-
}
|
|
3594
|
-
function isSelfContainedSource(source, envNames) {
|
|
3595
|
-
if (envNames.length > 0) return false;
|
|
3596
|
-
return !/\brequire\s*\(|\bimport\s*[{'"*]/.test(source);
|
|
3597
|
-
}
|
|
3598
|
-
function absModulesOk(source, envNames) {
|
|
3599
|
-
void source;
|
|
3600
|
-
void envNames;
|
|
3601
|
-
return true;
|
|
3602
|
-
}
|
|
3603
|
-
function collectAbsCallRecords(source, seeds, filePath, precomputedModules, envNames = []) {
|
|
3604
|
-
const absCalls = [];
|
|
3605
|
-
let modules = precomputedModules;
|
|
3606
|
-
let importLocals = /* @__PURE__ */ new Map();
|
|
3607
|
-
if (filePath && absModulesOk(source, envNames)) {
|
|
3608
|
-
if (!modules) {
|
|
3609
|
-
try {
|
|
3610
|
-
const graph = evalAbsModuleGraph(source, filePath, {
|
|
3611
|
-
seedVars: seeds?.seedVars,
|
|
3612
|
-
seedFns: seeds?.seedFns
|
|
3613
|
-
});
|
|
3614
|
-
modules = graph.modules;
|
|
3615
|
-
} catch {
|
|
3616
|
-
modules = void 0;
|
|
3617
|
-
}
|
|
3618
|
-
}
|
|
3619
|
-
if (envNames.length > 0) {
|
|
3620
|
-
modules = { ...collectEnvModules(envNames), ...modules ?? {} };
|
|
3621
|
-
}
|
|
3622
|
-
try {
|
|
3623
|
-
importLocals = buildAbsImportLocalMap(source, filePath);
|
|
3624
|
-
} catch {
|
|
3625
|
-
importLocals = /* @__PURE__ */ new Map();
|
|
3626
|
-
}
|
|
3627
|
-
}
|
|
3628
|
-
setAbsCallCollector((r) => absCalls.push(r));
|
|
3629
|
-
try {
|
|
3630
|
-
evalProgramAbs2(source, { ...seeds, modules });
|
|
3631
|
-
} catch {
|
|
3632
|
-
} finally {
|
|
3633
|
-
setAbsCallCollector(null);
|
|
3634
|
-
}
|
|
3635
|
-
return absCalls.map((r) => {
|
|
3636
|
-
const rec = {
|
|
3637
|
-
fnName: r.fnName,
|
|
3638
|
-
argTypes: r.args.map((a) => absToTypeValue2(a)),
|
|
3639
|
-
resultType: r.threw ? T2.never : absToTypeValue2(r.result),
|
|
3640
|
-
throws: r.threw ? absToTypeValue2(r.result) : T2.never,
|
|
3641
|
-
callLoc: r.callLoc
|
|
3642
|
-
};
|
|
3643
|
-
const imp = importLocals.get(r.fnName);
|
|
3644
|
-
if (imp) {
|
|
3645
|
-
rec.targetModule = imp.modulePath;
|
|
3646
|
-
rec.targetExport = imp.exportName;
|
|
3647
|
-
}
|
|
3648
|
-
return rec;
|
|
3649
|
-
});
|
|
3650
|
-
}
|
|
3651
|
-
function collectAbsBindsAndNodes(source, seeds, modules) {
|
|
3652
|
-
const binds = /* @__PURE__ */ new Map();
|
|
3653
|
-
const nodes = /* @__PURE__ */ new Map();
|
|
3654
|
-
setAbsNodeCollector((node, value) => {
|
|
3655
|
-
nodes.set(node, value);
|
|
3656
|
-
});
|
|
3657
|
-
try {
|
|
3658
|
-
const { env } = evalProgramAbs2(source, {
|
|
3659
|
-
...seeds,
|
|
3660
|
-
modules
|
|
3661
|
-
});
|
|
3662
|
-
for (const [k, v] of env.vars) binds.set(k, v);
|
|
3663
|
-
for (const [name, impl] of env.fns) {
|
|
3664
|
-
if (!binds.has(name)) {
|
|
3665
|
-
binds.set(
|
|
3666
|
-
name,
|
|
3667
|
-
absFunction5(impl.params, { body: impl.body, async: impl.async, env })
|
|
3668
|
-
);
|
|
3669
|
-
}
|
|
3670
|
-
}
|
|
3671
|
-
} catch {
|
|
3672
|
-
} finally {
|
|
3673
|
-
setAbsNodeCollector(null);
|
|
3674
|
-
}
|
|
3675
|
-
return { binds, nodes };
|
|
3676
|
-
}
|
|
3677
|
-
function buildAbsImportLocalMap(source, fromFile) {
|
|
3678
|
-
const out = /* @__PURE__ */ new Map();
|
|
3679
|
-
try {
|
|
3680
|
-
const file = parse5(source);
|
|
3681
|
-
for (const stmt of file.program.body) {
|
|
3682
|
-
if (stmt.type !== "ImportDeclaration") continue;
|
|
3683
|
-
const spec = stmt.source.value;
|
|
3684
|
-
let modulePath = null;
|
|
3685
|
-
if (spec.startsWith(".") || spec.startsWith("/")) {
|
|
3686
|
-
modulePath = resolveImportAbs(spec, fromFile);
|
|
3687
|
-
} else if (!spec.startsWith("node:")) {
|
|
3688
|
-
modulePath = spec;
|
|
3689
|
-
}
|
|
3690
|
-
if (!modulePath) continue;
|
|
3691
|
-
for (const s of stmt.specifiers) {
|
|
3692
|
-
if (s.type === "ImportSpecifier") {
|
|
3693
|
-
const imported = s.imported.type === "Identifier" ? s.imported.name : String(s.imported);
|
|
3694
|
-
out.set(s.local.name, { modulePath, exportName: imported });
|
|
3695
|
-
} else if (s.type === "ImportDefaultSpecifier") {
|
|
3696
|
-
out.set(s.local.name, { modulePath, exportName: "default" });
|
|
3697
|
-
}
|
|
3698
|
-
}
|
|
3699
|
-
}
|
|
3700
|
-
} catch {
|
|
3701
|
-
}
|
|
3702
|
-
return out;
|
|
3703
|
-
}
|
|
3704
|
-
function resolveImportAbs(spec, fromFile) {
|
|
3705
|
-
const base = dirname5(resolve4(fromFile));
|
|
3706
|
-
const p = resolve4(base, spec);
|
|
3707
|
-
for (const cand of [p, `${p}.js`, `${p}.mjs`, `${p}.ts`, resolve4(p, "index.js")]) {
|
|
3708
|
-
if (existsSync3(cand) && !statSync3(cand).isDirectory()) return cand;
|
|
3709
|
-
}
|
|
3710
|
-
return null;
|
|
3786
|
+
}));
|
|
3711
3787
|
}
|
|
3712
|
-
function
|
|
3713
|
-
const
|
|
3714
|
-
|
|
3788
|
+
function getStringCompletions() {
|
|
3789
|
+
const completions = [];
|
|
3790
|
+
for (const m of builtinProtoMembers("String")) {
|
|
3791
|
+
completions.push({
|
|
3792
|
+
label: m,
|
|
3793
|
+
kind: "method",
|
|
3794
|
+
detail: describeMember(m, builtinMemberType(`"s".${m}`), "String")
|
|
3795
|
+
});
|
|
3796
|
+
}
|
|
3797
|
+
completions.push({ label: "length", kind: "property", detail: "number" });
|
|
3798
|
+
return completions;
|
|
3715
3799
|
}
|
|
3716
|
-
function
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
let
|
|
3725
|
-
|
|
3726
|
-
|
|
3800
|
+
function getUnionCompletions(tv) {
|
|
3801
|
+
const members = tv.members;
|
|
3802
|
+
if (members.length === 0) return [];
|
|
3803
|
+
const labelsByMember = members.map((m) => getCompletionsForType(m));
|
|
3804
|
+
const baseIdx = labelsByMember.findIndex((labels) => labels.length > 0);
|
|
3805
|
+
if (baseIdx === -1) return [];
|
|
3806
|
+
const common = [];
|
|
3807
|
+
for (const base of labelsByMember[baseIdx]) {
|
|
3808
|
+
let allPresent = true;
|
|
3809
|
+
const memberTypes = [base.detail ?? base.label];
|
|
3810
|
+
for (let i = 0; i < members.length; i++) {
|
|
3811
|
+
if (i === baseIdx) continue;
|
|
3812
|
+
const labels = labelsByMember[i];
|
|
3813
|
+
if (labels.length === 0) continue;
|
|
3814
|
+
const hit = labels.find((l) => l.label === base.label);
|
|
3815
|
+
if (!hit) {
|
|
3816
|
+
allPresent = false;
|
|
3817
|
+
break;
|
|
3818
|
+
}
|
|
3819
|
+
memberTypes.push(hit.detail ?? hit.label);
|
|
3727
3820
|
}
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
return void 0;
|
|
3821
|
+
if (allPresent) {
|
|
3822
|
+
common.push({ ...base, detail: memberTypes.join(" | ") });
|
|
3731
3823
|
}
|
|
3732
|
-
return result;
|
|
3733
|
-
} catch {
|
|
3734
|
-
return void 0;
|
|
3735
3824
|
}
|
|
3825
|
+
return common;
|
|
3736
3826
|
}
|
|
3737
|
-
function
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
display: g.display,
|
|
3747
|
-
abs: formatAbs3(sym),
|
|
3748
|
-
absMultiline: formatAbsMultiline(sym, fnName)
|
|
3749
|
-
};
|
|
3750
|
-
if (sym.term && sym.term.op !== "lit") {
|
|
3751
|
-
intension.term = termToString(sym.term);
|
|
3827
|
+
function getCompletionsForType(tv) {
|
|
3828
|
+
const completions = [];
|
|
3829
|
+
if (tv.kind === "object") {
|
|
3830
|
+
for (const [key, val] of Object.entries(tv.properties)) {
|
|
3831
|
+
completions.push({
|
|
3832
|
+
label: key,
|
|
3833
|
+
kind: val.kind === "function" ? "method" : "property",
|
|
3834
|
+
detail: typeValueToString4(val)
|
|
3835
|
+
});
|
|
3752
3836
|
}
|
|
3753
|
-
|
|
3754
|
-
|
|
3837
|
+
return completions;
|
|
3838
|
+
}
|
|
3839
|
+
if (tv.kind === "instance") {
|
|
3840
|
+
for (const [key, val] of Object.entries(tv.properties)) {
|
|
3841
|
+
completions.push({
|
|
3842
|
+
label: key,
|
|
3843
|
+
kind: val.kind === "function" ? "method" : "property",
|
|
3844
|
+
detail: typeValueToString4(val)
|
|
3845
|
+
});
|
|
3755
3846
|
}
|
|
3756
|
-
|
|
3757
|
-
caseResult.intension = intension;
|
|
3758
|
-
} catch {
|
|
3847
|
+
return completions;
|
|
3759
3848
|
}
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3849
|
+
if (tv.kind === "union") {
|
|
3850
|
+
return getUnionCompletions(tv);
|
|
3851
|
+
}
|
|
3852
|
+
if (tv.kind === "array" || tv.kind === "tuple") {
|
|
3853
|
+
return getArrayCompletions(tv);
|
|
3854
|
+
}
|
|
3855
|
+
if (tv.kind === "promise") {
|
|
3856
|
+
return getPromiseCompletions();
|
|
3857
|
+
}
|
|
3858
|
+
if (tv.kind === "primitive" && tv.type === "string") {
|
|
3859
|
+
return getStringCompletions();
|
|
3860
|
+
}
|
|
3861
|
+
return completions;
|
|
3770
3862
|
}
|
|
3771
3863
|
|
|
3772
3864
|
// src/index.ts
|
|
3773
3865
|
import { collectAbsInlays } from "@nudojs/core";
|
|
3774
3866
|
|
|
3775
3867
|
// src/infer-json.ts
|
|
3776
|
-
import { typeValueToString as
|
|
3868
|
+
import { typeValueToString as typeValueToString5 } from "@nudojs/core";
|
|
3777
3869
|
function mapCase(c) {
|
|
3778
3870
|
const out = {
|
|
3779
3871
|
name: c.name,
|
|
3780
|
-
args: c.args.map((a) =>
|
|
3781
|
-
result:
|
|
3782
|
-
throws: c.throws.kind !== "never" ?
|
|
3872
|
+
args: c.args.map((a) => typeValueToString5(a)),
|
|
3873
|
+
result: typeValueToString5(c.result),
|
|
3874
|
+
throws: c.throws.kind !== "never" ? typeValueToString5(c.throws) : null,
|
|
3783
3875
|
source: c.source ?? null
|
|
3784
3876
|
};
|
|
3785
3877
|
if (c.aggregatedFrom !== void 0) out.aggregatedFrom = c.aggregatedFrom;
|
|
@@ -3804,7 +3896,7 @@ function mapFunction(f) {
|
|
|
3804
3896
|
cases: f.cases.map(mapCase)
|
|
3805
3897
|
};
|
|
3806
3898
|
if (f.noDeclaration) out.noDeclaration = true;
|
|
3807
|
-
if (f.combined) out.combined =
|
|
3899
|
+
if (f.combined) out.combined = typeValueToString5(f.combined);
|
|
3808
3900
|
return out;
|
|
3809
3901
|
}
|
|
3810
3902
|
function serializeInferJson(result, file) {
|
|
@@ -3843,36 +3935,10 @@ function serializeInferJson(result, file) {
|
|
|
3843
3935
|
function isNudoTargetPath(path) {
|
|
3844
3936
|
const lower = path.toLowerCase();
|
|
3845
3937
|
if (lower.endsWith(".d.ts")) return false;
|
|
3938
|
+
if (lower.endsWith(".nudo.js") || lower.endsWith(".nudo.ts")) return false;
|
|
3846
3939
|
return lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".ts");
|
|
3847
3940
|
}
|
|
3848
3941
|
|
|
3849
|
-
// src/load-module.ts
|
|
3850
|
-
import { existsSync as existsSync4, statSync as statSync4, readFileSync as readFileSync5 } from "fs";
|
|
3851
|
-
import { dirname as dirname6, resolve as resolve5, join as join3 } from "path";
|
|
3852
|
-
function defaultLoadModule(spec, fromFile) {
|
|
3853
|
-
if (!spec.startsWith(".") && !spec.startsWith("/")) return void 0;
|
|
3854
|
-
try {
|
|
3855
|
-
const base = dirname6(resolve5(fromFile));
|
|
3856
|
-
const p = resolve5(base, spec);
|
|
3857
|
-
for (const cand of [
|
|
3858
|
-
p,
|
|
3859
|
-
`${p}.js`,
|
|
3860
|
-
`${p}.mjs`,
|
|
3861
|
-
`${p}.ts`,
|
|
3862
|
-
join3(p, "index.js"),
|
|
3863
|
-
join3(p, "index.mjs"),
|
|
3864
|
-
join3(p, "index.ts")
|
|
3865
|
-
]) {
|
|
3866
|
-
if (existsSync4(cand) && !statSync4(cand).isDirectory()) {
|
|
3867
|
-
return readFileSync5(cand, "utf-8");
|
|
3868
|
-
}
|
|
3869
|
-
}
|
|
3870
|
-
return void 0;
|
|
3871
|
-
} catch {
|
|
3872
|
-
return void 0;
|
|
3873
|
-
}
|
|
3874
|
-
}
|
|
3875
|
-
|
|
3876
3942
|
// src/session-cache.ts
|
|
3877
3943
|
import {
|
|
3878
3944
|
resetGeneralizeMemo,
|
|
@@ -3923,15 +3989,9 @@ function summarizeNodeEnv(env) {
|
|
|
3923
3989
|
}
|
|
3924
3990
|
|
|
3925
3991
|
// src/semantic-tokens.ts
|
|
3926
|
-
import { dirname as
|
|
3927
|
-
import { createEnvironment as
|
|
3928
|
-
import { parse as
|
|
3929
|
-
import {
|
|
3930
|
-
evaluateProgram as evaluateProgram2,
|
|
3931
|
-
setModuleResolver as setModuleResolver2,
|
|
3932
|
-
setCurrentFileDir as setCurrentFileDir2,
|
|
3933
|
-
resetMemo as resetMemo2
|
|
3934
|
-
} from "@nudojs/cli/evaluator";
|
|
3992
|
+
import { dirname as dirname8 } from "path";
|
|
3993
|
+
import { createEnvironment as createEnvironment4 } from "@nudojs/core";
|
|
3994
|
+
import { parse as parse7 } from "@nudojs/parser";
|
|
3935
3995
|
var SEMANTIC_TOKEN_TYPES = [
|
|
3936
3996
|
"function",
|
|
3937
3997
|
"variable",
|
|
@@ -3973,19 +4033,19 @@ var MOD_DECLARATION = 1 << SEMANTIC_TOKEN_MODIFIERS.indexOf("declaration");
|
|
|
3973
4033
|
function buildSemanticTokens(filePath, source) {
|
|
3974
4034
|
let ast;
|
|
3975
4035
|
try {
|
|
3976
|
-
ast =
|
|
4036
|
+
ast = parse7(source);
|
|
3977
4037
|
} catch {
|
|
3978
4038
|
return [];
|
|
3979
4039
|
}
|
|
3980
|
-
const env =
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
4040
|
+
const env = createEnvironment4();
|
|
4041
|
+
resetMemo();
|
|
4042
|
+
setModuleResolver(resolveModule);
|
|
4043
|
+
setCurrentFileDir(dirname8(filePath));
|
|
3984
4044
|
try {
|
|
3985
|
-
|
|
4045
|
+
evaluateProgram(ast, env);
|
|
3986
4046
|
} catch {
|
|
3987
4047
|
} finally {
|
|
3988
|
-
|
|
4048
|
+
setModuleResolver(null);
|
|
3989
4049
|
}
|
|
3990
4050
|
const program = ast.program ?? ast;
|
|
3991
4051
|
const ownBindings = env.getOwnBindings();
|
|
@@ -4377,7 +4437,7 @@ function generateGuardBody(tv, varName) {
|
|
|
4377
4437
|
}
|
|
4378
4438
|
|
|
4379
4439
|
// src/case-emitter.ts
|
|
4380
|
-
import { parse as
|
|
4440
|
+
import { parse as parse8, extractDirectives as extractDirectives3 } from "@nudojs/parser";
|
|
4381
4441
|
var GENERATED_PREFIX = "call@";
|
|
4382
4442
|
var GENERATED_CASE_LINE_REGEX = /^\s*\*?\s*@nudo:case\s+"(call@[^"]*)"/;
|
|
4383
4443
|
var IDENT_KEY_REGEX = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
@@ -4520,11 +4580,11 @@ function collectExistingCases(source) {
|
|
|
4520
4580
|
const byName = /* @__PURE__ */ new Map();
|
|
4521
4581
|
let ast;
|
|
4522
4582
|
try {
|
|
4523
|
-
ast =
|
|
4583
|
+
ast = parse8(source, { errorRecovery: true });
|
|
4524
4584
|
} catch {
|
|
4525
4585
|
return byName;
|
|
4526
4586
|
}
|
|
4527
|
-
for (const { name, directives } of
|
|
4587
|
+
for (const { name, directives } of extractDirectives3(ast)) {
|
|
4528
4588
|
for (const d of directives) {
|
|
4529
4589
|
if (d.kind !== "case") continue;
|
|
4530
4590
|
const list = byName.get(name);
|
|
@@ -4729,6 +4789,418 @@ function unifiedDiff(a, b, path) {
|
|
|
4729
4789
|
}
|
|
4730
4790
|
return out.join("\n") + "\n";
|
|
4731
4791
|
}
|
|
4792
|
+
|
|
4793
|
+
// src/interface-surface.ts
|
|
4794
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
4795
|
+
import { dirname as dirname9, resolve as resolve6 } from "path";
|
|
4796
|
+
import {
|
|
4797
|
+
effectiveInterface,
|
|
4798
|
+
formatConstraint,
|
|
4799
|
+
interfaceDiagCount,
|
|
4800
|
+
localNamedExports,
|
|
4801
|
+
refineDiagCount,
|
|
4802
|
+
takeInterfaceDiagsSince,
|
|
4803
|
+
takeRefineDiagsSince,
|
|
4804
|
+
typeValueToString as typeValueToString6
|
|
4805
|
+
} from "@nudojs/core";
|
|
4806
|
+
function formatInterfaceSurfaceLine(e) {
|
|
4807
|
+
const params = `(${e.params.map((p) => `${p.name}: ${p.display}`).join(", ")})`;
|
|
4808
|
+
let line = ` ${e.fn} [${e.source}] ${params}`;
|
|
4809
|
+
if (e.returns !== void 0) line += ` \u2192 ${e.returns}`;
|
|
4810
|
+
if (e.kind === "local") line += " (local)";
|
|
4811
|
+
return line;
|
|
4812
|
+
}
|
|
4813
|
+
async function interfaceSurface(filePath, opts = {}) {
|
|
4814
|
+
const abs = resolve6(filePath);
|
|
4815
|
+
const source = readFileSync6(abs, "utf-8");
|
|
4816
|
+
const ifaceSince = interfaceDiagCount();
|
|
4817
|
+
const refineSince = refineDiagCount();
|
|
4818
|
+
const autoBind = opts.autoBind ?? interfaceConfig(findProjectConfig(dirname9(abs))?.config).autoBind;
|
|
4819
|
+
const loadModule = opts.loadModule ?? defaultLoadModule;
|
|
4820
|
+
const exported = localNamedExports(source);
|
|
4821
|
+
const kindOf = (fnName) => exported.has(fnName) ? "export" : "local";
|
|
4822
|
+
const analysis = await analyzeFileAsync(abs, source, void 0, opts.records);
|
|
4823
|
+
const entries = [];
|
|
4824
|
+
for (const fn of analysis.functions) {
|
|
4825
|
+
const eff = effectiveInterface(source, fn.name, { loadModule, fromFile: abs, autoBind });
|
|
4826
|
+
if (eff) {
|
|
4827
|
+
entries.push({
|
|
4828
|
+
fn: fn.name,
|
|
4829
|
+
kind: kindOf(fn.name),
|
|
4830
|
+
source: eff.source,
|
|
4831
|
+
params: eff.params.map((p) => ({ name: p.param, display: formatConstraint(p.constraint) })),
|
|
4832
|
+
returns: eff.returns ? formatConstraint(eff.returns.constraint) : void 0
|
|
4833
|
+
});
|
|
4834
|
+
continue;
|
|
4835
|
+
}
|
|
4836
|
+
const params = fn.paramNames.map((name, i) => {
|
|
4837
|
+
const seen = [];
|
|
4838
|
+
for (const c of fn.cases) {
|
|
4839
|
+
const arg = c.args[i];
|
|
4840
|
+
if (!arg) continue;
|
|
4841
|
+
const s = typeValueToString6(arg);
|
|
4842
|
+
if (!seen.includes(s)) seen.push(s);
|
|
4843
|
+
}
|
|
4844
|
+
return { name, display: seen.length > 0 ? seen.join(" | ") : "unknown" };
|
|
4845
|
+
});
|
|
4846
|
+
const ret = fn.combined !== void 0 ? typeValueToString6(fn.combined) : fn.cases.length > 0 ? typeValueToString6(fn.cases[fn.cases.length - 1].result) : void 0;
|
|
4847
|
+
entries.push({ fn: fn.name, kind: kindOf(fn.name), source: "implicit", params, returns: ret });
|
|
4848
|
+
}
|
|
4849
|
+
takeInterfaceDiagsSince(ifaceSince);
|
|
4850
|
+
takeRefineDiagsSince(refineSince);
|
|
4851
|
+
return entries;
|
|
4852
|
+
}
|
|
4853
|
+
|
|
4854
|
+
// src/interface-emitter.ts
|
|
4855
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7, writeFileSync, renameSync, unlinkSync } from "fs";
|
|
4856
|
+
import { basename as basename2, dirname as dirname10, relative, resolve as resolve7 } from "path";
|
|
4857
|
+
import {
|
|
4858
|
+
execNudoModule,
|
|
4859
|
+
formatConstraint as formatConstraint2,
|
|
4860
|
+
interfaceDiagCount as interfaceDiagCount2,
|
|
4861
|
+
isNodeModulesPath,
|
|
4862
|
+
isNudoConstraint,
|
|
4863
|
+
joinThenProject,
|
|
4864
|
+
localNamedExports as localNamedExports2,
|
|
4865
|
+
parseSource as parseSource2,
|
|
4866
|
+
refineDiagCount as refineDiagCount2,
|
|
4867
|
+
sidecarPathOf,
|
|
4868
|
+
takeInterfaceDiagsSince as takeInterfaceDiagsSince2,
|
|
4869
|
+
takeRefineDiagsSince as takeRefineDiagsSince2,
|
|
4870
|
+
typeValueToAbs as typeValueToAbs6
|
|
4871
|
+
} from "@nudojs/core";
|
|
4872
|
+
import { randomBytes } from "crypto";
|
|
4873
|
+
var GENERATED_HEADER = "// @generated by nudo \u2014 do not edit; regenerate with `nudo interface --emit`";
|
|
4874
|
+
async function emitInterface(filePath, opts) {
|
|
4875
|
+
const abs = resolve7(filePath);
|
|
4876
|
+
if (isNodeModulesPath(abs) || isNodeModulesPath(sidecarPathOf(abs))) {
|
|
4877
|
+
throw new Error(
|
|
4878
|
+
`emit target '${abs}' is inside node_modules; contract sidecars are never written there`
|
|
4879
|
+
);
|
|
4880
|
+
}
|
|
4881
|
+
const source = readFileSync7(abs, "utf-8");
|
|
4882
|
+
const ifaceSince = interfaceDiagCount2();
|
|
4883
|
+
const refineSince = refineDiagCount2();
|
|
4884
|
+
const sidecarPath = sidecarPathOf(abs);
|
|
4885
|
+
const sidecarSrc = existsSync6(sidecarPath) ? readFileSync7(sidecarPath, "utf-8") : "";
|
|
4886
|
+
const srcRel = relative(dirname10(sidecarPath), abs) || basename2(abs);
|
|
4887
|
+
const sections = collectGeneratedSections(sidecarSrc);
|
|
4888
|
+
const generatedNames = new Set(sections.flatMap((s) => s.names));
|
|
4889
|
+
const declared = topLevelDeclaredNames(sidecarSrc);
|
|
4890
|
+
if (declared === void 0) {
|
|
4891
|
+
throw new Error(
|
|
4892
|
+
`sidecar '${relative(process.cwd(), sidecarPath) || sidecarPath}' is not parseable; fix it before emitting`
|
|
4893
|
+
);
|
|
4894
|
+
}
|
|
4895
|
+
for (const n of generatedNames) declared.delete(n);
|
|
4896
|
+
const analysis = await analyzeFileAsync(abs, source, void 0, opts.records);
|
|
4897
|
+
const exported = localNamedExports2(source);
|
|
4898
|
+
const fnByName = /* @__PURE__ */ new Map();
|
|
4899
|
+
for (const f of analysis.functions) {
|
|
4900
|
+
if (!fnByName.has(f.name)) fnByName.set(f.name, f);
|
|
4901
|
+
}
|
|
4902
|
+
const fileExportOrder = analysis.functions.map((f) => f.name).filter((n) => exported.has(n));
|
|
4903
|
+
let targetNames;
|
|
4904
|
+
if (opts.fnNames && opts.fnNames.length > 0) {
|
|
4905
|
+
targetNames = [...new Set(opts.fnNames)];
|
|
4906
|
+
} else if (opts.all) {
|
|
4907
|
+
targetNames = fileExportOrder;
|
|
4908
|
+
} else {
|
|
4909
|
+
targetNames = fileExportOrder.filter((n) => generatedNames.has(n));
|
|
4910
|
+
}
|
|
4911
|
+
const targetSet = new Set(targetNames);
|
|
4912
|
+
const written = [];
|
|
4913
|
+
const skipped = [];
|
|
4914
|
+
const issues = [];
|
|
4915
|
+
const accepted = [];
|
|
4916
|
+
const planCache = /* @__PURE__ */ new Map();
|
|
4917
|
+
const planFor = (name) => {
|
|
4918
|
+
const hit = planCache.get(name);
|
|
4919
|
+
if (hit) return hit;
|
|
4920
|
+
const fn = fnByName.get(name);
|
|
4921
|
+
const dsl = fn === void 0 ? void 0 : projectFunctionDsl(fn);
|
|
4922
|
+
const roundTrip = dsl !== void 0 && roundTrips(sectionText(name, dsl, srcRel), name);
|
|
4923
|
+
const rec = { dsl, roundTrip };
|
|
4924
|
+
planCache.set(name, rec);
|
|
4925
|
+
return rec;
|
|
4926
|
+
};
|
|
4927
|
+
const atomicSections = /* @__PURE__ */ new Set();
|
|
4928
|
+
for (const s of sections) {
|
|
4929
|
+
if (s.names.length <= 1) continue;
|
|
4930
|
+
const everyRewritable = s.names.every(
|
|
4931
|
+
(n) => targetSet.has(n) && exported.has(n) && !declared.has(n) && planFor(n).dsl !== void 0 && planFor(n).roundTrip
|
|
4932
|
+
);
|
|
4933
|
+
if (!everyRewritable) atomicSections.add(s);
|
|
4934
|
+
}
|
|
4935
|
+
const acceptedAtomic = /* @__PURE__ */ new Set();
|
|
4936
|
+
for (const name of targetNames) {
|
|
4937
|
+
if (!exported.has(name) || !fnByName.has(name)) {
|
|
4938
|
+
skipped.push({
|
|
4939
|
+
fn: name,
|
|
4940
|
+
reason: "not-an-export"
|
|
4941
|
+
});
|
|
4942
|
+
continue;
|
|
4943
|
+
}
|
|
4944
|
+
if (declared.has(name)) {
|
|
4945
|
+
skipped.push({ fn: name, reason: "name-clash" });
|
|
4946
|
+
issues.push({
|
|
4947
|
+
code: "nudo:interface-name-clash",
|
|
4948
|
+
severity: "error",
|
|
4949
|
+
message: `sidecar already has a handwritten binding '${name}' (${srcRel}); handwritten wins \u2014 skipping emit for it`
|
|
4950
|
+
});
|
|
4951
|
+
continue;
|
|
4952
|
+
}
|
|
4953
|
+
const prev = sections.find((s) => s.names.includes(name));
|
|
4954
|
+
if (prev !== void 0 && atomicSections.has(prev)) {
|
|
4955
|
+
if (!acceptedAtomic.has(prev)) {
|
|
4956
|
+
acceptedAtomic.add(prev);
|
|
4957
|
+
const keptText = normalizeSection(prev.text);
|
|
4958
|
+
accepted.push({ fn: prev.names.join("+"), text: keptText, prevText: keptText });
|
|
4959
|
+
issues.push({
|
|
4960
|
+
code: "nudo:interface-multi-declarator",
|
|
4961
|
+
severity: "warning",
|
|
4962
|
+
message: `generated section '${prev.names.join(", ")}' is a hand-merged multi-declarator form; kept verbatim (split it into one export per section to re-emit)`
|
|
4963
|
+
});
|
|
4964
|
+
}
|
|
4965
|
+
skipped.push({ fn: name, reason: "multi-declarator" });
|
|
4966
|
+
continue;
|
|
4967
|
+
}
|
|
4968
|
+
const prevText = prev === void 0 ? void 0 : normalizeSection(prev.text);
|
|
4969
|
+
const plan = planFor(name);
|
|
4970
|
+
if (plan.dsl === void 0 || !plan.roundTrip) {
|
|
4971
|
+
skipped.push({ fn: name, reason: "not-projectable" });
|
|
4972
|
+
if (prevText !== void 0) {
|
|
4973
|
+
accepted.push({ fn: name, text: prevText, prevText });
|
|
4974
|
+
}
|
|
4975
|
+
continue;
|
|
4976
|
+
}
|
|
4977
|
+
const text = sectionText(name, plan.dsl, srcRel);
|
|
4978
|
+
if (opts.mode === "add" && prev !== void 0) {
|
|
4979
|
+
skipped.push({ fn: name, reason: "no-change" });
|
|
4980
|
+
continue;
|
|
4981
|
+
}
|
|
4982
|
+
if (prevText !== void 0 && prevText === normalizeSection(text)) {
|
|
4983
|
+
skipped.push({ fn: name, reason: "no-change" });
|
|
4984
|
+
accepted.push({ fn: name, text: prevText, prevText });
|
|
4985
|
+
continue;
|
|
4986
|
+
}
|
|
4987
|
+
written.push(name);
|
|
4988
|
+
accepted.push({ fn: name, text, prevText });
|
|
4989
|
+
}
|
|
4990
|
+
let finalContent;
|
|
4991
|
+
if (opts.mode === "add") {
|
|
4992
|
+
finalContent = accepted.length === 0 ? sidecarSrc : joinSections(sidecarSrc, accepted.map((a) => a.text));
|
|
4993
|
+
} else {
|
|
4994
|
+
const base = removeSections(sidecarSrc, sections);
|
|
4995
|
+
const preserved = sections.filter((s) => !s.names.some((n) => targetSet.has(n))).map((s) => normalizeSection(s.text));
|
|
4996
|
+
finalContent = joinSections(base, [...preserved, ...accepted.map((a) => a.text)]);
|
|
4997
|
+
}
|
|
4998
|
+
const changed = finalContent !== sidecarSrc;
|
|
4999
|
+
const diff = changed ? unifiedDiff(sidecarSrc, finalContent, relative(process.cwd(), sidecarPath) || sidecarPath) : void 0;
|
|
5000
|
+
if (changed && !opts.dryRun) {
|
|
5001
|
+
const tmp = `${sidecarPath}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
5002
|
+
try {
|
|
5003
|
+
writeFileSync(tmp, finalContent, "utf-8");
|
|
5004
|
+
renameSync(tmp, sidecarPath);
|
|
5005
|
+
} catch (e) {
|
|
5006
|
+
try {
|
|
5007
|
+
if (existsSync6(tmp)) unlinkSync(tmp);
|
|
5008
|
+
} catch {
|
|
5009
|
+
}
|
|
5010
|
+
throw e;
|
|
5011
|
+
}
|
|
5012
|
+
}
|
|
5013
|
+
takeRefineDiagsSince2(refineSince);
|
|
5014
|
+
takeInterfaceDiagsSince2(ifaceSince);
|
|
5015
|
+
return {
|
|
5016
|
+
written,
|
|
5017
|
+
skipped,
|
|
5018
|
+
changed,
|
|
5019
|
+
...diff !== void 0 ? { diff } : {},
|
|
5020
|
+
issues,
|
|
5021
|
+
sidecarPath,
|
|
5022
|
+
...opts.fnNames && opts.fnNames.length > 0 ? {} : opts.all ? {} : targetNames.length === 0 ? { emptyDefaultTargets: true } : {}
|
|
5023
|
+
};
|
|
5024
|
+
}
|
|
5025
|
+
function formatEmitSummary(sourcePath, sidecarRel, result) {
|
|
5026
|
+
const lines = [];
|
|
5027
|
+
if (result.changed) {
|
|
5028
|
+
lines.push(`Updated ${sourcePath} \u2192 ${sidecarRel}`);
|
|
5029
|
+
lines.push(` written: ${result.written.join(", ") || "(none)"}`);
|
|
5030
|
+
} else {
|
|
5031
|
+
lines.push(`${sourcePath}: no interface changes`);
|
|
5032
|
+
if (result.emptyDefaultTargets) {
|
|
5033
|
+
lines.push(
|
|
5034
|
+
` tip: default --emit only refreshes existing @generated segments; pass --fn <name> or --all to create new ones`
|
|
5035
|
+
);
|
|
5036
|
+
}
|
|
5037
|
+
}
|
|
5038
|
+
for (const s of result.skipped.filter((x) => x.reason !== "no-change")) {
|
|
5039
|
+
lines.push(` skipped ${s.fn} (${s.reason})`);
|
|
5040
|
+
}
|
|
5041
|
+
for (const i of result.issues) {
|
|
5042
|
+
lines.push(` [${i.severity}] ${i.code}: ${i.message}`);
|
|
5043
|
+
}
|
|
5044
|
+
return lines;
|
|
5045
|
+
}
|
|
5046
|
+
function projectFunctionDsl(fn) {
|
|
5047
|
+
const callsite = fn.cases.filter((c) => c.source === "callsite");
|
|
5048
|
+
const directive = fn.cases.filter((c) => c.source === "directive");
|
|
5049
|
+
const paramCases = callsite.length > 0 ? callsite : directive;
|
|
5050
|
+
const returnCases = callsite.length > 0 ? callsite : directive.length > 0 ? directive : fn.cases;
|
|
5051
|
+
const paramParts = [];
|
|
5052
|
+
for (let i = 0; i < fn.paramNames.length; i++) {
|
|
5053
|
+
const argAbs = [];
|
|
5054
|
+
for (const c of paramCases) {
|
|
5055
|
+
const a = c.args[i];
|
|
5056
|
+
if (a === void 0) continue;
|
|
5057
|
+
argAbs.push(safeTypeValueToAbs(a));
|
|
5058
|
+
}
|
|
5059
|
+
if (argAbs.length === 0) continue;
|
|
5060
|
+
const constraint = joinThenProject(argAbs);
|
|
5061
|
+
if (constraint === void 0) return void 0;
|
|
5062
|
+
paramParts.push(`${fn.paramNames[i]}: ${formatConstraint2(constraint)}`);
|
|
5063
|
+
}
|
|
5064
|
+
const retAbs = [];
|
|
5065
|
+
for (const c of returnCases) {
|
|
5066
|
+
if (c.throws !== void 0 && c.throws.kind !== "never") continue;
|
|
5067
|
+
retAbs.push(c.abs ?? safeTypeValueToAbs(c.result));
|
|
5068
|
+
}
|
|
5069
|
+
const retConstraint = retAbs.length > 0 ? joinThenProject(retAbs) : void 0;
|
|
5070
|
+
if (retConstraint === void 0 && paramParts.length === 0) return void 0;
|
|
5071
|
+
if (retAbs.length > 0 && retConstraint === void 0) return void 0;
|
|
5072
|
+
const ret = retConstraint === void 0 ? "" : `, ${formatConstraint2(retConstraint)}`;
|
|
5073
|
+
return `fn({ ${paramParts.join(", ")} }${ret})`;
|
|
5074
|
+
}
|
|
5075
|
+
function safeTypeValueToAbs(v) {
|
|
5076
|
+
try {
|
|
5077
|
+
return typeValueToAbs6(v);
|
|
5078
|
+
} catch {
|
|
5079
|
+
return { shape: { k: "unknown" }, conf: "opaque" };
|
|
5080
|
+
}
|
|
5081
|
+
}
|
|
5082
|
+
function sectionText(fn, dsl, srcRel) {
|
|
5083
|
+
return `${GENERATED_HEADER}
|
|
5084
|
+
// source: ${srcRel}:${fn}
|
|
5085
|
+
export const ${fn} = ${dsl};
|
|
5086
|
+
`;
|
|
5087
|
+
}
|
|
5088
|
+
function roundTrips(text, fn) {
|
|
5089
|
+
const since = refineDiagCount2();
|
|
5090
|
+
try {
|
|
5091
|
+
const exports = execNudoModule(text);
|
|
5092
|
+
const v = exports[fn];
|
|
5093
|
+
return isNudoConstraint(v) && v.fn !== void 0;
|
|
5094
|
+
} catch {
|
|
5095
|
+
return false;
|
|
5096
|
+
} finally {
|
|
5097
|
+
takeRefineDiagsSince2(since);
|
|
5098
|
+
}
|
|
5099
|
+
}
|
|
5100
|
+
function collectGeneratedSections(sidecarSrc) {
|
|
5101
|
+
if (sidecarSrc.trim() === "") return [];
|
|
5102
|
+
let ast;
|
|
5103
|
+
try {
|
|
5104
|
+
ast = parseSource2(sidecarSrc);
|
|
5105
|
+
} catch {
|
|
5106
|
+
throw new Error("sidecar is not parseable");
|
|
5107
|
+
}
|
|
5108
|
+
const out = [];
|
|
5109
|
+
for (const stmt of ast.program.body) {
|
|
5110
|
+
if (stmt.type !== "ExportNamedDeclaration" || stmt.source) continue;
|
|
5111
|
+
const d = stmt.declaration;
|
|
5112
|
+
if (!d || stmt.start == null || stmt.end == null) continue;
|
|
5113
|
+
const names = [];
|
|
5114
|
+
if (d.type === "VariableDeclaration") {
|
|
5115
|
+
for (const decl of d.declarations) {
|
|
5116
|
+
if (decl.id.type === "Identifier") names.push(decl.id.name);
|
|
5117
|
+
}
|
|
5118
|
+
} else if (d.type === "FunctionDeclaration" || d.type === "ClassDeclaration") {
|
|
5119
|
+
if (d.id) names.push(d.id.name);
|
|
5120
|
+
}
|
|
5121
|
+
if (names.length === 0) continue;
|
|
5122
|
+
const blockStart = generatedCommentBlockStart(sidecarSrc, stmt.start);
|
|
5123
|
+
if (blockStart === void 0) continue;
|
|
5124
|
+
out.push({ names, start: blockStart, end: stmt.end, text: sidecarSrc.slice(blockStart, stmt.end) });
|
|
5125
|
+
}
|
|
5126
|
+
return out;
|
|
5127
|
+
}
|
|
5128
|
+
function generatedCommentBlockStart(src, pos) {
|
|
5129
|
+
const lines = src.slice(0, pos).split("\n");
|
|
5130
|
+
let blockStartLine = -1;
|
|
5131
|
+
let sawGenerated = false;
|
|
5132
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
5133
|
+
const line = lines[i].trim();
|
|
5134
|
+
if (line === "" || line === "*/") continue;
|
|
5135
|
+
if (line.startsWith("//") || line.startsWith("/*") || line.startsWith("*")) {
|
|
5136
|
+
blockStartLine = i;
|
|
5137
|
+
if (/@generated/.test(line)) sawGenerated = true;
|
|
5138
|
+
continue;
|
|
5139
|
+
}
|
|
5140
|
+
break;
|
|
5141
|
+
}
|
|
5142
|
+
if (!sawGenerated || blockStartLine < 0) return void 0;
|
|
5143
|
+
const prefix = lines.slice(0, blockStartLine);
|
|
5144
|
+
return prefix.length === 0 ? 0 : prefix.join("\n").length + 1;
|
|
5145
|
+
}
|
|
5146
|
+
function topLevelDeclaredNames(sidecarSrc) {
|
|
5147
|
+
if (sidecarSrc.trim() === "") return /* @__PURE__ */ new Set();
|
|
5148
|
+
let ast;
|
|
5149
|
+
try {
|
|
5150
|
+
ast = parseSource2(sidecarSrc);
|
|
5151
|
+
} catch {
|
|
5152
|
+
return void 0;
|
|
5153
|
+
}
|
|
5154
|
+
const names = /* @__PURE__ */ new Set();
|
|
5155
|
+
const collect = (stmt) => {
|
|
5156
|
+
if (stmt.type === "VariableDeclaration") {
|
|
5157
|
+
for (const d of stmt.declarations) {
|
|
5158
|
+
if (d.id.type === "Identifier") names.add(d.id.name);
|
|
5159
|
+
}
|
|
5160
|
+
} else if (stmt.type === "FunctionDeclaration" || stmt.type === "ClassDeclaration") {
|
|
5161
|
+
if (stmt.id) names.add(stmt.id.name);
|
|
5162
|
+
}
|
|
5163
|
+
};
|
|
5164
|
+
for (const stmt of ast.program.body) {
|
|
5165
|
+
if (stmt.type === "ExportNamedDeclaration") {
|
|
5166
|
+
if (stmt.declaration) collect(stmt.declaration);
|
|
5167
|
+
if (!stmt.source) {
|
|
5168
|
+
for (const spec of stmt.specifiers) {
|
|
5169
|
+
names.add(spec.exported.type === "Identifier" ? spec.exported.name : spec.exported.value);
|
|
5170
|
+
}
|
|
5171
|
+
}
|
|
5172
|
+
} else {
|
|
5173
|
+
collect(stmt);
|
|
5174
|
+
}
|
|
5175
|
+
}
|
|
5176
|
+
return names;
|
|
5177
|
+
}
|
|
5178
|
+
function removeSections(src, sections) {
|
|
5179
|
+
if (sections.length === 0) return src;
|
|
5180
|
+
const sorted = [...sections].sort((a, b) => a.start - b.start);
|
|
5181
|
+
let out = "";
|
|
5182
|
+
let pos = 0;
|
|
5183
|
+
for (const s of sorted) {
|
|
5184
|
+
out += src.slice(pos, s.start);
|
|
5185
|
+
pos = Math.max(pos, s.end);
|
|
5186
|
+
}
|
|
5187
|
+
out += src.slice(pos);
|
|
5188
|
+
return out;
|
|
5189
|
+
}
|
|
5190
|
+
function normalizeSection(text) {
|
|
5191
|
+
return text.replace(/\s*$/, "") + "\n";
|
|
5192
|
+
}
|
|
5193
|
+
function joinSections(base, sectionTexts) {
|
|
5194
|
+
const normalized = sectionTexts.map(normalizeSection).filter((t) => t.trim() !== "");
|
|
5195
|
+
const tailTrimmed = base.replace(/\s+$/, "");
|
|
5196
|
+
if (normalized.length === 0) return tailTrimmed === "" ? "" : `${tailTrimmed}
|
|
5197
|
+
`;
|
|
5198
|
+
const trimmed = tailTrimmed.replace(/^\s+/, "");
|
|
5199
|
+
const lead = trimmed === "" ? "" : `${trimmed}
|
|
5200
|
+
|
|
5201
|
+
`;
|
|
5202
|
+
return lead + normalized.join("\n");
|
|
5203
|
+
}
|
|
4732
5204
|
export {
|
|
4733
5205
|
SEMANTIC_TOKEN_MODIFIERS,
|
|
4734
5206
|
SEMANTIC_TOKEN_TYPES,
|
|
@@ -4761,6 +5233,7 @@ export {
|
|
|
4761
5233
|
computeDirtySet,
|
|
4762
5234
|
defaultAbsLoadModule,
|
|
4763
5235
|
defaultLoadModule,
|
|
5236
|
+
emitInterface,
|
|
4764
5237
|
encodeSemanticTokens,
|
|
4765
5238
|
envValueToAbs,
|
|
4766
5239
|
evalAbsModuleGraph,
|
|
@@ -4770,7 +5243,10 @@ export {
|
|
|
4770
5243
|
evictAnalysisFileCacheForFiles,
|
|
4771
5244
|
evictBPathCacheForFiles,
|
|
4772
5245
|
evictFnAnalysisCacheForFiles,
|
|
5246
|
+
findProjectConfig,
|
|
5247
|
+
formatEmitSummary,
|
|
4773
5248
|
formatHarvestSummary,
|
|
5249
|
+
formatInterfaceSurfaceLine,
|
|
4774
5250
|
generateDts,
|
|
4775
5251
|
generateFunctionDtsLines,
|
|
4776
5252
|
generateGuardFunction,
|
|
@@ -4787,6 +5263,8 @@ export {
|
|
|
4787
5263
|
harvestToAbsModules,
|
|
4788
5264
|
harvestedValueToAbs,
|
|
4789
5265
|
insertGeneratedCaseDirectives,
|
|
5266
|
+
interfaceConfig,
|
|
5267
|
+
interfaceSurface,
|
|
4790
5268
|
isBPathCapable,
|
|
4791
5269
|
isNudoTargetPath,
|
|
4792
5270
|
lookupHarvested,
|