@suss/adapter-typescript 0.21.0 → 0.22.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/README.md +1 -35
- package/dist/index.cjs +949 -1507
- package/dist/index.d.cts +74 -414
- package/dist/index.d.ts +74 -414
- package/dist/index.js +913 -1453
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/adapter.ts
|
|
2
|
-
import
|
|
2
|
+
import path10 from "path";
|
|
3
3
|
import {
|
|
4
4
|
Node as Node64,
|
|
5
5
|
Project,
|
|
@@ -21,7 +21,10 @@ import {
|
|
|
21
21
|
import { Database as Database5 } from "@suss/datalog";
|
|
22
22
|
import {
|
|
23
23
|
assembleSummary as assembleSummary3,
|
|
24
|
-
composeWrappers
|
|
24
|
+
composeWrappers,
|
|
25
|
+
createCacheLayer,
|
|
26
|
+
runDigest,
|
|
27
|
+
stampModuleImports
|
|
25
28
|
} from "@suss/extractor";
|
|
26
29
|
|
|
27
30
|
// src/assembly.ts
|
|
@@ -40,6 +43,7 @@ import { Node as Node5, SyntaxKind as SyntaxKind2 } from "ts-morph";
|
|
|
40
43
|
import { Node as Node2, SyntaxKind } from "ts-morph";
|
|
41
44
|
|
|
42
45
|
// src/walk/nodeKeys.ts
|
|
46
|
+
import { declarationKey } from "@suss/behavioral-ir";
|
|
43
47
|
function offsetKeyOf(node) {
|
|
44
48
|
return offsetKeyFor(node.getSourceFile().getFilePath(), {
|
|
45
49
|
start: node.getStart(),
|
|
@@ -47,7 +51,7 @@ function offsetKeyOf(node) {
|
|
|
47
51
|
});
|
|
48
52
|
}
|
|
49
53
|
function offsetKeyFor(file, span) {
|
|
50
|
-
return
|
|
54
|
+
return declarationKey(file, span);
|
|
51
55
|
}
|
|
52
56
|
function lineRangeKey(file, startLine, endLine) {
|
|
53
57
|
return `${file}:${startLine}-${endLine}`;
|
|
@@ -1867,8 +1871,8 @@ function loadImportGraphsDepthFirstFromPaths(project, rootPaths) {
|
|
|
1867
1871
|
stack.pop();
|
|
1868
1872
|
loadOrder.push(top.path);
|
|
1869
1873
|
let deepest = 0;
|
|
1870
|
-
for (const
|
|
1871
|
-
deepest = Math.max(deepest, chainDepth.get(
|
|
1874
|
+
for (const path14 of top.deps) {
|
|
1875
|
+
deepest = Math.max(deepest, chainDepth.get(path14) ?? 0);
|
|
1872
1876
|
}
|
|
1873
1877
|
chainDepth.set(top.path, deepest + 1);
|
|
1874
1878
|
}
|
|
@@ -1915,6 +1919,20 @@ function importedFilePathsOf(project, filePath) {
|
|
|
1915
1919
|
cache.set(filePath, paths);
|
|
1916
1920
|
return paths;
|
|
1917
1921
|
}
|
|
1922
|
+
function resolutionCacheFor(project, options) {
|
|
1923
|
+
const existing = resolutionCachesByProject.get(project);
|
|
1924
|
+
if (existing !== void 0) {
|
|
1925
|
+
return existing;
|
|
1926
|
+
}
|
|
1927
|
+
const fresh = ts.createModuleResolutionCache(
|
|
1928
|
+
project.getFileSystem().getCurrentDirectory(),
|
|
1929
|
+
(name) => name,
|
|
1930
|
+
options
|
|
1931
|
+
);
|
|
1932
|
+
resolutionCachesByProject.set(project, fresh);
|
|
1933
|
+
return fresh;
|
|
1934
|
+
}
|
|
1935
|
+
var resolutionCachesByProject = /* @__PURE__ */ new WeakMap();
|
|
1918
1936
|
function specifierCacheFor(project) {
|
|
1919
1937
|
const existing = specifiersByProject.get(project);
|
|
1920
1938
|
if (existing !== void 0) {
|
|
@@ -1941,13 +1959,15 @@ function readSpecifierPaths(project, filePath, options, host) {
|
|
|
1941
1959
|
return [];
|
|
1942
1960
|
}
|
|
1943
1961
|
const scanned = ts.preProcessFile(text, true, true);
|
|
1962
|
+
const cache = resolutionCacheFor(project, options);
|
|
1944
1963
|
const paths = [];
|
|
1945
1964
|
for (const imported of scanned.importedFiles) {
|
|
1946
1965
|
const resolved = ts.resolveModuleName(
|
|
1947
1966
|
imported.fileName,
|
|
1948
1967
|
filePath,
|
|
1949
1968
|
options,
|
|
1950
|
-
host
|
|
1969
|
+
host,
|
|
1970
|
+
cache
|
|
1951
1971
|
).resolvedModule?.resolvedFileName;
|
|
1952
1972
|
if (resolved !== void 0 && !resolved.includes("/node_modules/")) {
|
|
1953
1973
|
paths.push(resolved);
|
|
@@ -2204,11 +2224,11 @@ function walkAndWarm(root, reach) {
|
|
|
2204
2224
|
if (warmedFiles.get(target) !== void 0) {
|
|
2205
2225
|
continue;
|
|
2206
2226
|
}
|
|
2207
|
-
const
|
|
2208
|
-
if (visited.has(
|
|
2227
|
+
const path14 = target.getFilePath();
|
|
2228
|
+
if (visited.has(path14)) {
|
|
2209
2229
|
continue;
|
|
2210
2230
|
}
|
|
2211
|
-
visited.add(
|
|
2231
|
+
visited.add(path14);
|
|
2212
2232
|
stack.push(frameOf(target, reach));
|
|
2213
2233
|
continue;
|
|
2214
2234
|
}
|
|
@@ -2225,7 +2245,7 @@ function sitsOnWarmChains(file) {
|
|
|
2225
2245
|
return false;
|
|
2226
2246
|
}
|
|
2227
2247
|
const imported = importedFilePathsOf(project, file.getFilePath());
|
|
2228
|
-
return imported.length > 0 && imported.every((
|
|
2248
|
+
return imported.length > 0 && imported.every((path14) => warmed.has(path14));
|
|
2229
2249
|
}
|
|
2230
2250
|
function markWarmed(file) {
|
|
2231
2251
|
warmedFiles.set(file, true);
|
|
@@ -2542,6 +2562,32 @@ function writesToBindingUncached(declaration) {
|
|
|
2542
2562
|
}
|
|
2543
2563
|
return { values, inOrder: writesRunInOrder(declaration, writes) };
|
|
2544
2564
|
}
|
|
2565
|
+
function sameConstructionAcrossWrites(values) {
|
|
2566
|
+
const candidates = values.filter((value) => !isNotYetPlaceholder(value));
|
|
2567
|
+
if (candidates.length === 0) {
|
|
2568
|
+
return null;
|
|
2569
|
+
}
|
|
2570
|
+
let construction = null;
|
|
2571
|
+
for (const value of candidates) {
|
|
2572
|
+
if (!isConstruction(value)) {
|
|
2573
|
+
return null;
|
|
2574
|
+
}
|
|
2575
|
+
if (construction !== null && sourceOf(value) !== sourceOf(construction)) {
|
|
2576
|
+
return null;
|
|
2577
|
+
}
|
|
2578
|
+
construction = value;
|
|
2579
|
+
}
|
|
2580
|
+
return construction;
|
|
2581
|
+
}
|
|
2582
|
+
function isNotYetPlaceholder(value) {
|
|
2583
|
+
return value.getKind() === SyntaxKind3.NullKeyword || Node13.isIdentifier(value) && value.getText() === "undefined";
|
|
2584
|
+
}
|
|
2585
|
+
function isConstruction(value) {
|
|
2586
|
+
return Node13.isCallExpression(value) || Node13.isNewExpression(value);
|
|
2587
|
+
}
|
|
2588
|
+
function sourceOf(node) {
|
|
2589
|
+
return node.getText().replace(/\s+/g, " ").trim();
|
|
2590
|
+
}
|
|
2545
2591
|
function writesToField(declaration) {
|
|
2546
2592
|
const remembered = byField.get(declaration);
|
|
2547
2593
|
if (remembered !== void 0) {
|
|
@@ -2632,6 +2678,9 @@ function assignmentsInFile(sourceFile) {
|
|
|
2632
2678
|
function isAssignmentOperator(kind) {
|
|
2633
2679
|
return kind >= SyntaxKind3.FirstAssignment && kind <= SyntaxKind3.LastAssignment;
|
|
2634
2680
|
}
|
|
2681
|
+
function writesItsWholeValue(kind) {
|
|
2682
|
+
return kind === SyntaxKind3.EqualsToken || kind === SyntaxKind3.QuestionQuestionEqualsToken || kind === SyntaxKind3.BarBarEqualsToken;
|
|
2683
|
+
}
|
|
2635
2684
|
function writeAt(node) {
|
|
2636
2685
|
if (Node13.isBinaryExpression(node)) {
|
|
2637
2686
|
const operator = node.getOperatorToken().getKind();
|
|
@@ -2639,7 +2688,7 @@ function writeAt(node) {
|
|
|
2639
2688
|
if (!isAssignmentOperator(operator) || !(Node13.isIdentifier(target) || Node13.isPropertyAccessExpression(target))) {
|
|
2640
2689
|
return null;
|
|
2641
2690
|
}
|
|
2642
|
-
const value = operator
|
|
2691
|
+
const value = writesItsWholeValue(operator) ? node.getRight() : null;
|
|
2643
2692
|
return { target, write: { value, node } };
|
|
2644
2693
|
}
|
|
2645
2694
|
if (Node13.isPrefixUnaryExpression(node) || Node13.isPostfixUnaryExpression(node)) {
|
|
@@ -3030,11 +3079,16 @@ function emitBindingValues(db, table, declaration) {
|
|
|
3030
3079
|
}
|
|
3031
3080
|
const { values, inOrder } = writesToBinding(declaration);
|
|
3032
3081
|
const last = values[values.length - 1];
|
|
3033
|
-
if (
|
|
3034
|
-
|
|
3082
|
+
if (inOrder && last !== void 0) {
|
|
3083
|
+
fact(db, "endsHolding", declarationId, emitValue(db, table, last));
|
|
3035
3084
|
return;
|
|
3036
3085
|
}
|
|
3037
|
-
|
|
3086
|
+
const construction = sameConstructionAcrossWrites(values);
|
|
3087
|
+
if (construction !== null) {
|
|
3088
|
+
fact(db, "endsHolding", declarationId, emitValue(db, table, construction));
|
|
3089
|
+
return;
|
|
3090
|
+
}
|
|
3091
|
+
reassignedUnstated.add(declarationId);
|
|
3038
3092
|
}
|
|
3039
3093
|
var reassignedUnstated = /* @__PURE__ */ new Set();
|
|
3040
3094
|
function reassignedNamesUnstated() {
|
|
@@ -3046,23 +3100,31 @@ function forgetReassignedNamesUnstated() {
|
|
|
3046
3100
|
function emitFieldValues(db, table, declaration) {
|
|
3047
3101
|
const { values, inOrder } = writesToField(declaration);
|
|
3048
3102
|
const last = values[values.length - 1];
|
|
3049
|
-
if (
|
|
3103
|
+
if (inOrder && last !== void 0 && Node14.isExpression(last)) {
|
|
3104
|
+
fact(db, "binds", nodeId(declaration), emitValue(db, table, last));
|
|
3050
3105
|
return;
|
|
3051
3106
|
}
|
|
3052
|
-
|
|
3107
|
+
const construction = sameConstructionAcrossWrites(values);
|
|
3108
|
+
if (construction !== null) {
|
|
3109
|
+
fact(db, "binds", nodeId(declaration), emitValue(db, table, construction));
|
|
3110
|
+
}
|
|
3053
3111
|
}
|
|
3054
3112
|
function emitParameterPropertyRead(db, table, referenceId, declaration) {
|
|
3055
3113
|
const { values, inOrder } = writesToField(declaration);
|
|
3056
3114
|
const last = values[values.length - 1];
|
|
3057
|
-
if (
|
|
3115
|
+
if (inOrder && last !== void 0 && Node14.isExpression(last)) {
|
|
3116
|
+
fact(db, "binds", referenceId, emitValue(db, table, last));
|
|
3058
3117
|
return;
|
|
3059
3118
|
}
|
|
3060
|
-
if (
|
|
3119
|
+
if (inOrder && last !== void 0) {
|
|
3061
3120
|
table.byId.set(nodeId(last), last);
|
|
3062
3121
|
fact(db, "binds", referenceId, nodeId(last));
|
|
3063
3122
|
return;
|
|
3064
3123
|
}
|
|
3065
|
-
|
|
3124
|
+
const construction = sameConstructionAcrossWrites(values);
|
|
3125
|
+
if (construction !== null) {
|
|
3126
|
+
fact(db, "binds", referenceId, emitValue(db, table, construction));
|
|
3127
|
+
}
|
|
3066
3128
|
}
|
|
3067
3129
|
function emitBindingElementFacts(db, table, element) {
|
|
3068
3130
|
const id = nodeId(element);
|
|
@@ -3526,7 +3588,8 @@ var JS_RULES = [
|
|
|
3526
3588
|
];
|
|
3527
3589
|
var RESOLUTION_PROGRAM = process.env.SUSS_RESOLUTION_ON_DEMAND === "0" ? {
|
|
3528
3590
|
rules: [...SHARED_RULES, ...JS_RULES, ...RESOLUTION_QUESTIONS],
|
|
3529
|
-
demandDriven: []
|
|
3591
|
+
demandDriven: [],
|
|
3592
|
+
demands: []
|
|
3530
3593
|
} : deriveOnDemand(
|
|
3531
3594
|
[...SHARED_RULES, ...JS_RULES, ...RESOLUTION_QUESTIONS],
|
|
3532
3595
|
ANSWER_RELATIONS
|
|
@@ -3553,7 +3616,9 @@ var QUERY_FACTS = RESOLUTION_PROGRAM.demandDriven.length === 0 ? [] : [
|
|
|
3553
3616
|
"wantedAnchor",
|
|
3554
3617
|
"wantedSubject"
|
|
3555
3618
|
];
|
|
3556
|
-
var
|
|
3619
|
+
var MODULE_DEMANDS = RESOLUTION_PROGRAM.demands.filter(
|
|
3620
|
+
(one) => (one.relation === "moduleExport" || one.relation === "moduleForwards") && one.bound[0] === true
|
|
3621
|
+
).map((one) => one.demand);
|
|
3557
3622
|
var ResolutionStore = class {
|
|
3558
3623
|
db = new Database2();
|
|
3559
3624
|
table = createNodeTable();
|
|
@@ -3569,7 +3634,7 @@ var ResolutionStore = class {
|
|
|
3569
3634
|
* extraction elsewhere cannot change an entry.
|
|
3570
3635
|
*/
|
|
3571
3636
|
exportTables = /* @__PURE__ */ new Map();
|
|
3572
|
-
/** Files the most recent
|
|
3637
|
+
/** Files the most recent query read, for the memo to keep. */
|
|
3573
3638
|
lastQueryWalked = [];
|
|
3574
3639
|
declarations = /* @__PURE__ */ new Map();
|
|
3575
3640
|
graph = new ModuleGraph();
|
|
@@ -3600,19 +3665,19 @@ var ResolutionStore = class {
|
|
|
3600
3665
|
*/
|
|
3601
3666
|
resolveCallableSources(value, alsoFrom) {
|
|
3602
3667
|
const target = factKeyOf(value);
|
|
3603
|
-
return this.
|
|
3668
|
+
return this.askAbout(
|
|
3604
3669
|
target,
|
|
3605
3670
|
"wanted",
|
|
3606
3671
|
() => this.lookupSources(target),
|
|
3607
3672
|
alsoFrom
|
|
3608
|
-
)
|
|
3673
|
+
);
|
|
3609
3674
|
}
|
|
3610
3675
|
/**
|
|
3611
3676
|
* The witness proof behind `resolveCallable`'s answer. Resolves the
|
|
3612
|
-
* value first, which
|
|
3613
|
-
*
|
|
3614
|
-
*
|
|
3615
|
-
*
|
|
3677
|
+
* value first, which extracts the files the question demands, then
|
|
3678
|
+
* re-evaluates the rules over those base facts under the witness
|
|
3679
|
+
* algebra and rebuilds the proof of the answer. Null when the value
|
|
3680
|
+
* does not resolve at all.
|
|
3616
3681
|
*/
|
|
3617
3682
|
explainCallable(value, options = {}) {
|
|
3618
3683
|
const resolved = this.resolveCallable(value, options.alsoFrom);
|
|
@@ -3649,11 +3714,7 @@ var ResolutionStore = class {
|
|
|
3649
3714
|
}
|
|
3650
3715
|
resolveObject(value) {
|
|
3651
3716
|
const target = factKeyOf(value);
|
|
3652
|
-
return this.
|
|
3653
|
-
target,
|
|
3654
|
-
"wanted",
|
|
3655
|
-
() => this.lookupObject(target)
|
|
3656
|
-
);
|
|
3717
|
+
return this.askAbout(target, "wanted", () => this.lookupObject(target));
|
|
3657
3718
|
}
|
|
3658
3719
|
/**
|
|
3659
3720
|
* For a value that is neither a function nor an object, such as a
|
|
@@ -3669,17 +3730,17 @@ var ResolutionStore = class {
|
|
|
3669
3730
|
}
|
|
3670
3731
|
return cached.written;
|
|
3671
3732
|
}
|
|
3672
|
-
const
|
|
3733
|
+
const written = this.askAbout(
|
|
3673
3734
|
target,
|
|
3674
3735
|
"wanted",
|
|
3675
3736
|
() => this.lookupWritten(target)
|
|
3676
3737
|
);
|
|
3677
3738
|
this.writtenValues.set(key, {
|
|
3678
|
-
written
|
|
3739
|
+
written,
|
|
3679
3740
|
walked: [...this.lastQueryWalked],
|
|
3680
3741
|
extractedAt: this.fullyExtracted.size
|
|
3681
3742
|
});
|
|
3682
|
-
return
|
|
3743
|
+
return written;
|
|
3683
3744
|
}
|
|
3684
3745
|
/**
|
|
3685
3746
|
* The construction behind a registration subject: the call or `new`
|
|
@@ -3699,18 +3760,29 @@ var ResolutionStore = class {
|
|
|
3699
3760
|
}
|
|
3700
3761
|
return cached.construction;
|
|
3701
3762
|
}
|
|
3702
|
-
const settled2 = this.
|
|
3763
|
+
const settled2 = this.askAbout(
|
|
3703
3764
|
target,
|
|
3704
3765
|
"wantedSubject",
|
|
3705
3766
|
() => this.lookupSubjectConstruction(target, importModule, importName)
|
|
3706
3767
|
);
|
|
3768
|
+
const walked = [...this.lastQueryWalked];
|
|
3769
|
+
let resolved = settled2;
|
|
3770
|
+
if (settled2.construction === null && settled2.candidate !== null && Node15.isCallExpression(settled2.candidate) && this.resolveCallable(settled2.candidate.getExpression()) !== null) {
|
|
3771
|
+
const call = settled2.candidate;
|
|
3772
|
+
resolved = this.askAbout(
|
|
3773
|
+
call,
|
|
3774
|
+
"wantedSubject",
|
|
3775
|
+
() => this.lookupSubjectConstruction(call, importModule, importName)
|
|
3776
|
+
);
|
|
3777
|
+
walked.push(...this.lastQueryWalked);
|
|
3778
|
+
}
|
|
3707
3779
|
this.subjectConstructions.set(key, {
|
|
3708
|
-
construction:
|
|
3709
|
-
candidates:
|
|
3710
|
-
walked
|
|
3780
|
+
construction: resolved.construction,
|
|
3781
|
+
candidates: resolved.candidates,
|
|
3782
|
+
walked,
|
|
3711
3783
|
extractedAt: this.fullyExtracted.size
|
|
3712
3784
|
});
|
|
3713
|
-
return
|
|
3785
|
+
return resolved.construction;
|
|
3714
3786
|
}
|
|
3715
3787
|
/**
|
|
3716
3788
|
* How many constructions this value was written as, which is what
|
|
@@ -3724,10 +3796,8 @@ var ResolutionStore = class {
|
|
|
3724
3796
|
)?.candidates ?? 0;
|
|
3725
3797
|
}
|
|
3726
3798
|
/**
|
|
3727
|
-
* The single-answer policy over the written-value
|
|
3728
|
-
* origin check on the one candidate.
|
|
3729
|
-
* is settled refusal rather than a reason to widen: its own file's
|
|
3730
|
-
* import facts are already in, and more files only add candidates.
|
|
3799
|
+
* The single-answer policy over the written-value chain, then the
|
|
3800
|
+
* origin check on the one candidate.
|
|
3731
3801
|
*/
|
|
3732
3802
|
lookupSubjectConstruction(value, importModule, importName) {
|
|
3733
3803
|
this.derive();
|
|
@@ -3743,6 +3813,7 @@ var ResolutionStore = class {
|
|
|
3743
3813
|
if (candidates.size > 1) {
|
|
3744
3814
|
return {
|
|
3745
3815
|
construction: null,
|
|
3816
|
+
candidate: null,
|
|
3746
3817
|
candidates: [...candidates].filter(
|
|
3747
3818
|
(one) => this.constructionComesFrom(valueId, one, importModule, importName)
|
|
3748
3819
|
).length
|
|
@@ -3750,7 +3821,7 @@ var ResolutionStore = class {
|
|
|
3750
3821
|
}
|
|
3751
3822
|
const single = [...candidates][0];
|
|
3752
3823
|
if (single === void 0) {
|
|
3753
|
-
return
|
|
3824
|
+
return { construction: null, candidate: null, candidates: 0 };
|
|
3754
3825
|
}
|
|
3755
3826
|
return {
|
|
3756
3827
|
construction: this.constructionComesFrom(
|
|
@@ -3759,6 +3830,7 @@ var ResolutionStore = class {
|
|
|
3759
3830
|
importModule,
|
|
3760
3831
|
importName
|
|
3761
3832
|
) ? single : null,
|
|
3833
|
+
candidate: single,
|
|
3762
3834
|
candidates: 1
|
|
3763
3835
|
};
|
|
3764
3836
|
}
|
|
@@ -3818,11 +3890,11 @@ var ResolutionStore = class {
|
|
|
3818
3890
|
return cached.names;
|
|
3819
3891
|
}
|
|
3820
3892
|
const target = factKeyOf(value);
|
|
3821
|
-
const found = this.
|
|
3893
|
+
const found = this.askAbout(
|
|
3822
3894
|
target,
|
|
3823
3895
|
"wantedOrigin",
|
|
3824
3896
|
() => this.lookupImportedNames(target, modules)
|
|
3825
|
-
)
|
|
3897
|
+
);
|
|
3826
3898
|
this.importedNames.set(declaration, {
|
|
3827
3899
|
names: found,
|
|
3828
3900
|
walked: [...this.lastQueryWalked]
|
|
@@ -3841,8 +3913,8 @@ var ResolutionStore = class {
|
|
|
3841
3913
|
return this.importOriginsOfMany([value], modules).get(value) ?? [];
|
|
3842
3914
|
}
|
|
3843
3915
|
/**
|
|
3844
|
-
* The batched form: one demand set, one derivation, one
|
|
3845
|
-
*
|
|
3916
|
+
* The batched form: one demand set, one derivation, one round of
|
|
3917
|
+
* extraction shared by every value. A discovery pass asks about every
|
|
3846
3918
|
* callee in a file, and per-value queries would re-pay demand
|
|
3847
3919
|
* clearing and re-derivation once per call site.
|
|
3848
3920
|
*/
|
|
@@ -3870,49 +3942,12 @@ var ResolutionStore = class {
|
|
|
3870
3942
|
this.wantValue("wantedCallOrigin", one.target);
|
|
3871
3943
|
this.seedValue(one.target);
|
|
3872
3944
|
}
|
|
3873
|
-
|
|
3874
|
-
this.lastQueryWalked = [];
|
|
3875
|
-
let frontier = [];
|
|
3945
|
+
this.extractDemanded(pending.map((one) => one.target.getSourceFile()));
|
|
3876
3946
|
for (const one of pending) {
|
|
3877
|
-
const
|
|
3878
|
-
|
|
3879
|
-
frontier.push(file);
|
|
3880
|
-
}
|
|
3881
|
-
}
|
|
3882
|
-
const open = new Set(pending);
|
|
3883
|
-
for (let hop = 0; hop <= MAX_MODULE_HOPS && open.size > 0; hop++) {
|
|
3884
|
-
const next = [];
|
|
3885
|
-
for (const sourceFile of frontier) {
|
|
3886
|
-
if (walked.has(sourceFile.getFilePath())) {
|
|
3887
|
-
continue;
|
|
3888
|
-
}
|
|
3889
|
-
walked.add(sourceFile.getFilePath());
|
|
3890
|
-
this.lastQueryWalked.push(sourceFile.getFilePath());
|
|
3891
|
-
recordFileDependency(sourceFile.getFilePath());
|
|
3892
|
-
this.extractFile(sourceFile);
|
|
3893
|
-
next.push(...this.graph.importedFilesOf(sourceFile));
|
|
3894
|
-
}
|
|
3895
|
-
for (const one of [...open]) {
|
|
3896
|
-
const found = this.lookupImportOrigins(one.target, modules);
|
|
3897
|
-
if (found !== null) {
|
|
3898
|
-
results.set(one.value, found);
|
|
3899
|
-
this.importOrigins.set(one.key, {
|
|
3900
|
-
origins: found,
|
|
3901
|
-
walked: [...this.lastQueryWalked],
|
|
3902
|
-
extractedAt: this.fullyExtracted.size
|
|
3903
|
-
});
|
|
3904
|
-
open.delete(one);
|
|
3905
|
-
}
|
|
3906
|
-
}
|
|
3907
|
-
if (next.length === 0) {
|
|
3908
|
-
break;
|
|
3909
|
-
}
|
|
3910
|
-
frontier = next;
|
|
3911
|
-
}
|
|
3912
|
-
for (const one of open) {
|
|
3913
|
-
results.set(one.value, []);
|
|
3947
|
+
const origins = this.lookupImportOrigins(one.target, modules);
|
|
3948
|
+
results.set(one.value, origins);
|
|
3914
3949
|
this.importOrigins.set(one.key, {
|
|
3915
|
-
origins
|
|
3950
|
+
origins,
|
|
3916
3951
|
walked: [...this.lastQueryWalked],
|
|
3917
3952
|
extractedAt: this.fullyExtracted.size
|
|
3918
3953
|
});
|
|
@@ -3971,67 +4006,89 @@ var ResolutionStore = class {
|
|
|
3971
4006
|
const refined = new Set(
|
|
3972
4007
|
[...byPath.values()].filter((one) => one.path.length > 1).map((one) => tupleKey(one.path.slice(0, -1)))
|
|
3973
4008
|
);
|
|
3974
|
-
|
|
3975
|
-
if (origins.length > 0) {
|
|
3976
|
-
return origins;
|
|
3977
|
-
}
|
|
3978
|
-
return all.some((one) => namesAPackage(one.module)) ? [] : null;
|
|
4009
|
+
return [...byPath.values()].filter((one) => !refined.has(tupleKey(one.path))).sort((a, b) => a.path.join(".").localeCompare(b.path.join(".")));
|
|
3979
4010
|
}
|
|
3980
|
-
/**
|
|
3981
|
-
* Null tells the walk to keep widening. A value that landed in a
|
|
3982
|
-
* package returns empty instead, because a library's own body is not
|
|
3983
|
-
* here to read and widening would never find anything.
|
|
3984
|
-
*/
|
|
3985
4011
|
lookupImportedNames(value, modules) {
|
|
3986
4012
|
this.derive();
|
|
3987
|
-
const
|
|
3988
|
-
|
|
4013
|
+
const reached = new Set(
|
|
4014
|
+
this.answerPairsFor("wantedComesFrom", nodeId(value))
|
|
4015
|
+
);
|
|
3989
4016
|
for (const target of this.answersFor("wantedComesTo", nodeId(value))) {
|
|
3990
4017
|
for (const entry of this.answerPairsFor("wantedCallsInto", target)) {
|
|
3991
4018
|
reached.add(entry);
|
|
3992
4019
|
}
|
|
3993
4020
|
}
|
|
3994
|
-
|
|
3995
|
-
if (names.length > 0) {
|
|
3996
|
-
return names;
|
|
3997
|
-
}
|
|
3998
|
-
return origins.some((pair) => namesAPackage(pairHalves(pair).module)) ? [] : null;
|
|
4021
|
+
return namesFrom([...reached], modules);
|
|
3999
4022
|
}
|
|
4000
|
-
|
|
4023
|
+
askAbout(value, question, read2, alsoFrom) {
|
|
4001
4024
|
try {
|
|
4002
|
-
|
|
4025
|
+
this.wantValue(question, value);
|
|
4026
|
+
this.seedValue(value);
|
|
4027
|
+
this.extractDemanded(
|
|
4028
|
+
alsoFrom === void 0 ? [value.getSourceFile()] : [value.getSourceFile(), alsoFrom]
|
|
4029
|
+
);
|
|
4030
|
+
return read2();
|
|
4003
4031
|
} finally {
|
|
4004
4032
|
this.forgetQuery();
|
|
4005
4033
|
}
|
|
4006
4034
|
}
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4035
|
+
/**
|
|
4036
|
+
* Extract the seed files, then every module the rules ask for, until
|
|
4037
|
+
* they ask for nothing new. The files a question reads are then fixed
|
|
4038
|
+
* by the question alone, so its answer does not depend on what an
|
|
4039
|
+
* earlier question left in the store.
|
|
4040
|
+
*/
|
|
4041
|
+
extractDemanded(seeds) {
|
|
4042
|
+
const project = seeds[0]?.getProject();
|
|
4043
|
+
if (project === void 0) {
|
|
4044
|
+
return;
|
|
4045
|
+
}
|
|
4046
|
+
const read2 = /* @__PURE__ */ new Set();
|
|
4047
|
+
const considered = /* @__PURE__ */ new Set();
|
|
4011
4048
|
this.lastQueryWalked = [];
|
|
4012
|
-
let
|
|
4013
|
-
|
|
4014
|
-
const
|
|
4015
|
-
for (const sourceFile of
|
|
4016
|
-
|
|
4049
|
+
let pending = [...seeds];
|
|
4050
|
+
while (pending.length > 0) {
|
|
4051
|
+
const readThisRound = [];
|
|
4052
|
+
for (const sourceFile of pending) {
|
|
4053
|
+
const filePath = sourceFile.getFilePath();
|
|
4054
|
+
if (read2.has(filePath)) {
|
|
4017
4055
|
continue;
|
|
4018
4056
|
}
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4057
|
+
read2.add(filePath);
|
|
4058
|
+
readThisRound.push(filePath);
|
|
4059
|
+
this.lastQueryWalked.push(filePath);
|
|
4060
|
+
recordFileDependency(filePath);
|
|
4022
4061
|
this.extractFile(sourceFile);
|
|
4023
|
-
next.push(...this.graph.importedFilesOf(sourceFile));
|
|
4024
4062
|
}
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4063
|
+
this.derive();
|
|
4064
|
+
pending = [];
|
|
4065
|
+
for (const moduleKey of this.demandedModules(readThisRound)) {
|
|
4066
|
+
if (considered.has(moduleKey)) {
|
|
4067
|
+
continue;
|
|
4068
|
+
}
|
|
4069
|
+
considered.add(moduleKey);
|
|
4070
|
+
const sourceFile = sourceFileFor(project, moduleKey);
|
|
4071
|
+
if (sourceFile !== void 0 && !read2.has(sourceFile.getFilePath())) {
|
|
4072
|
+
pending.push(sourceFile);
|
|
4073
|
+
}
|
|
4031
4074
|
}
|
|
4032
|
-
frontier = next;
|
|
4033
4075
|
}
|
|
4034
|
-
|
|
4076
|
+
}
|
|
4077
|
+
/**
|
|
4078
|
+
* Module keys the rules are waiting on. Demand facts are cleared
|
|
4079
|
+
* between queries, so the demand relations contain this query's
|
|
4080
|
+
* alone; the unrestricted program has none, and follows the imports
|
|
4081
|
+
* of the files read this round instead.
|
|
4082
|
+
*/
|
|
4083
|
+
demandedModules(readThisRound) {
|
|
4084
|
+
if (RESOLUTION_PROGRAM.demands.length === 0) {
|
|
4085
|
+
return readThisRound.flatMap(
|
|
4086
|
+
(filePath) => this.db.lookup("importsModule", 0, filePath).map((tuple) => String(tuple[1]))
|
|
4087
|
+
);
|
|
4088
|
+
}
|
|
4089
|
+
return MODULE_DEMANDS.flatMap(
|
|
4090
|
+
(relation) => this.db.facts(relation).map((tuple) => String(tuple[0]))
|
|
4091
|
+
);
|
|
4035
4092
|
}
|
|
4036
4093
|
/**
|
|
4037
4094
|
* The question and the chain that settled it are dropped. The files
|
|
@@ -4050,6 +4107,27 @@ var ResolutionStore = class {
|
|
|
4050
4107
|
this.stale = true;
|
|
4051
4108
|
}
|
|
4052
4109
|
}
|
|
4110
|
+
wantExportsOf(filePath) {
|
|
4111
|
+
if (this.db.add("wantedExportsOf", [filePath]) === "added") {
|
|
4112
|
+
this.stale = true;
|
|
4113
|
+
}
|
|
4114
|
+
}
|
|
4115
|
+
/**
|
|
4116
|
+
* Discovery walks the table in order, and the rounds derive it in
|
|
4117
|
+
* the order files arrived. Deriving it again from the demand alone,
|
|
4118
|
+
* over the loaded closure, orders it by the file's own statements.
|
|
4119
|
+
*/
|
|
4120
|
+
rederiveExportsOf(filePath) {
|
|
4121
|
+
if (QUERY_FACTS.length === 0) {
|
|
4122
|
+
return;
|
|
4123
|
+
}
|
|
4124
|
+
this.db.retract("wantedModuleExport", [
|
|
4125
|
+
...this.db.lookup("wantedModuleExport", 0, filePath)
|
|
4126
|
+
]);
|
|
4127
|
+
this.forgetQuery();
|
|
4128
|
+
this.wantExportsOf(filePath);
|
|
4129
|
+
this.derive();
|
|
4130
|
+
}
|
|
4053
4131
|
/**
|
|
4054
4132
|
* File extraction only reaches values hanging off exports, and a query
|
|
4055
4133
|
* can be rooted anywhere, so the queried value gets its own facts.
|
|
@@ -4099,24 +4177,18 @@ var ResolutionStore = class {
|
|
|
4099
4177
|
}
|
|
4100
4178
|
candidates.add(node);
|
|
4101
4179
|
}
|
|
4102
|
-
if (candidates.size
|
|
4103
|
-
return
|
|
4104
|
-
}
|
|
4105
|
-
if (candidates.size > 1) {
|
|
4106
|
-
return { written: null };
|
|
4107
|
-
}
|
|
4108
|
-
if (neverWritable(value)) {
|
|
4109
|
-
return { written: null };
|
|
4180
|
+
if (candidates.size !== 1) {
|
|
4181
|
+
return null;
|
|
4110
4182
|
}
|
|
4111
|
-
return
|
|
4183
|
+
return [...candidates][0];
|
|
4112
4184
|
}
|
|
4113
4185
|
/**
|
|
4114
4186
|
* Read these files' facts now, before anything asks a question.
|
|
4115
4187
|
*
|
|
4116
|
-
* A query
|
|
4117
|
-
*
|
|
4188
|
+
* A query follows the imports of the file it starts in, which is
|
|
4189
|
+
* where a value's definition is. What a caller passed is the other
|
|
4118
4190
|
* direction, so a question about a parameter is answered only by
|
|
4119
|
-
* files the
|
|
4191
|
+
* files the query never reaches on its own.
|
|
4120
4192
|
*/
|
|
4121
4193
|
extractFiles(sourceFiles) {
|
|
4122
4194
|
for (const sourceFile of sourceFiles) {
|
|
@@ -4132,15 +4204,14 @@ var ResolutionStore = class {
|
|
|
4132
4204
|
return this.graph.filesReachingAnyPackage(fileSets);
|
|
4133
4205
|
}
|
|
4134
4206
|
/**
|
|
4135
|
-
* The calls behind a receiver that `matches` accepts
|
|
4136
|
-
*
|
|
4137
|
-
*
|
|
4138
|
-
*
|
|
4139
|
-
* cannot rank a nearer call above a farther one.
|
|
4207
|
+
* The calls behind a receiver that `matches` accepts. The caller
|
|
4208
|
+
* applies the single-answer policy to the set; the resolution
|
|
4209
|
+
* README's anchor section says why the rules cannot rank a nearer
|
|
4210
|
+
* call above a farther one.
|
|
4140
4211
|
*/
|
|
4141
4212
|
anchorCallsOf(value, matches) {
|
|
4142
4213
|
const target = factKeyOf(value);
|
|
4143
|
-
return this.
|
|
4214
|
+
return this.askAbout(target, "wantedAnchor", () => {
|
|
4144
4215
|
this.derive();
|
|
4145
4216
|
const found = [];
|
|
4146
4217
|
for (const id of this.answersFor("wantedAnchorCall", nodeId(target))) {
|
|
@@ -4149,12 +4220,12 @@ var ResolutionStore = class {
|
|
|
4149
4220
|
found.push(node);
|
|
4150
4221
|
}
|
|
4151
4222
|
}
|
|
4152
|
-
return found
|
|
4153
|
-
})
|
|
4223
|
+
return found;
|
|
4224
|
+
});
|
|
4154
4225
|
}
|
|
4155
4226
|
/**
|
|
4156
4227
|
* Every name a module exports and the values behind each, with
|
|
4157
|
-
* re-export chains flattened by the rules. The
|
|
4228
|
+
* re-export chains flattened by the rules. The demand follows
|
|
4158
4229
|
* re-export targets only, so a barrel of barrels extracts its own
|
|
4159
4230
|
* chain and nothing beside it, to any depth.
|
|
4160
4231
|
*/
|
|
@@ -4174,33 +4245,10 @@ var ResolutionStore = class {
|
|
|
4174
4245
|
}
|
|
4175
4246
|
}
|
|
4176
4247
|
collectExports(sourceFile) {
|
|
4177
|
-
const project = sourceFile.getProject();
|
|
4178
|
-
const walked = /* @__PURE__ */ new Set();
|
|
4179
|
-
let frontier = [sourceFile];
|
|
4180
|
-
while (frontier.length > 0) {
|
|
4181
|
-
const next = [];
|
|
4182
|
-
for (const file of frontier) {
|
|
4183
|
-
const filePath2 = file.getFilePath();
|
|
4184
|
-
if (walked.has(filePath2)) {
|
|
4185
|
-
continue;
|
|
4186
|
-
}
|
|
4187
|
-
walked.add(filePath2);
|
|
4188
|
-
recordFileDependency(filePath2);
|
|
4189
|
-
this.extractFile(file);
|
|
4190
|
-
for (const target of this.reExportTargetsOf(filePath2)) {
|
|
4191
|
-
const targetFile = sourceFileFor(project, target);
|
|
4192
|
-
if (targetFile !== void 0) {
|
|
4193
|
-
next.push(targetFile);
|
|
4194
|
-
}
|
|
4195
|
-
}
|
|
4196
|
-
}
|
|
4197
|
-
frontier = next;
|
|
4198
|
-
}
|
|
4199
4248
|
const filePath = sourceFile.getFilePath();
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
this.derive();
|
|
4249
|
+
this.wantExportsOf(filePath);
|
|
4250
|
+
this.extractDemanded([sourceFile]);
|
|
4251
|
+
this.rederiveExportsOf(filePath);
|
|
4204
4252
|
const exports = /* @__PURE__ */ new Map();
|
|
4205
4253
|
for (const tuple of this.db.lookup("wantedModuleExport", 0, filePath)) {
|
|
4206
4254
|
const node = this.table.byId.get(String(tuple[2]));
|
|
@@ -4217,22 +4265,9 @@ var ResolutionStore = class {
|
|
|
4217
4265
|
}
|
|
4218
4266
|
return exports;
|
|
4219
4267
|
}
|
|
4220
|
-
/** Module keys this file re-exports from, read off its own facts. */
|
|
4221
|
-
reExportTargetsOf(filePath) {
|
|
4222
|
-
const targets = /* @__PURE__ */ new Set();
|
|
4223
|
-
for (const tuple of this.db.lookup("reExports", 0, filePath)) {
|
|
4224
|
-
targets.add(String(tuple[2]));
|
|
4225
|
-
}
|
|
4226
|
-
for (const tuple of this.db.lookup("reExportsAll", 0, filePath)) {
|
|
4227
|
-
targets.add(String(tuple[1]));
|
|
4228
|
-
}
|
|
4229
|
-
return [...targets];
|
|
4230
|
-
}
|
|
4231
4268
|
/**
|
|
4232
|
-
*
|
|
4233
|
-
*
|
|
4234
|
-
* single-answer policy stays with the callers: picking one of two
|
|
4235
|
-
* would make the result depend on the order the facts came in.
|
|
4269
|
+
* The single-answer policy stays with the callers: a value with two
|
|
4270
|
+
* sources is something to say at the site, not something to pick from.
|
|
4236
4271
|
*/
|
|
4237
4272
|
lookupSources(value) {
|
|
4238
4273
|
this.derive();
|
|
@@ -4246,7 +4281,7 @@ var ResolutionStore = class {
|
|
|
4246
4281
|
candidates.add(resolved);
|
|
4247
4282
|
}
|
|
4248
4283
|
}
|
|
4249
|
-
return
|
|
4284
|
+
return [...candidates];
|
|
4250
4285
|
}
|
|
4251
4286
|
derive() {
|
|
4252
4287
|
if (!this.stale) {
|
|
@@ -4305,18 +4340,6 @@ function pairHalves(pair) {
|
|
|
4305
4340
|
const [module = "", name = ""] = tupleKeyParts(pair);
|
|
4306
4341
|
return { module, name };
|
|
4307
4342
|
}
|
|
4308
|
-
function namesAPackage(moduleKey) {
|
|
4309
|
-
return !moduleKey.startsWith("/") || moduleKey.includes("/node_modules/");
|
|
4310
|
-
}
|
|
4311
|
-
function neverWritable(value) {
|
|
4312
|
-
if (!Node15.isIdentifier(value)) {
|
|
4313
|
-
return false;
|
|
4314
|
-
}
|
|
4315
|
-
const declarations = value.getSymbol()?.getDeclarations() ?? [];
|
|
4316
|
-
return declarations.some(
|
|
4317
|
-
(declaration) => declaration.getSourceFile().getFilePath().includes("/node_modules/")
|
|
4318
|
-
);
|
|
4319
|
-
}
|
|
4320
4343
|
function namesPackage2(moduleKey, packages) {
|
|
4321
4344
|
return namesAnyPackage(
|
|
4322
4345
|
[moduleKey, ...packagesDeclaring(moduleKey)],
|
|
@@ -4337,35 +4360,12 @@ import path3 from "path";
|
|
|
4337
4360
|
// src/diagnostics.ts
|
|
4338
4361
|
import path2 from "path";
|
|
4339
4362
|
import { ts as ts3 } from "ts-morph";
|
|
4340
|
-
import {
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
unitsClaimed: 0,
|
|
4347
|
-
selfCollisions: 0,
|
|
4348
|
-
summariesProduced: 0,
|
|
4349
|
-
failures: [],
|
|
4350
|
-
helpersMatched: /* @__PURE__ */ new Set()
|
|
4351
|
-
});
|
|
4352
|
-
function recordPackFailure(tally, failure) {
|
|
4353
|
-
const message = failure.error instanceof Error ? failure.error.message : String(failure.error);
|
|
4354
|
-
tally?.failures.push({
|
|
4355
|
-
hook: failure.hook,
|
|
4356
|
-
file: failure.file,
|
|
4357
|
-
message
|
|
4358
|
-
});
|
|
4359
|
-
return `[suss] pack "${failure.pack}" threw from ${failure.hook} while reading ${failure.file}: ${message}
|
|
4360
|
-
`;
|
|
4361
|
-
}
|
|
4362
|
-
function createPackTallies(packs) {
|
|
4363
|
-
const tallies = /* @__PURE__ */ new Map();
|
|
4364
|
-
for (const pack of packs) {
|
|
4365
|
-
tallies.set(pack.name, emptyTally());
|
|
4366
|
-
}
|
|
4367
|
-
return tallies;
|
|
4368
|
-
}
|
|
4363
|
+
import {
|
|
4364
|
+
createPackTallies,
|
|
4365
|
+
emptyTally,
|
|
4366
|
+
recordPackFailure,
|
|
4367
|
+
summaryCountsByPack
|
|
4368
|
+
} from "@suss/extractor";
|
|
4369
4369
|
function commonDirectoryOf(files) {
|
|
4370
4370
|
const absolute = files.filter((f) => path2.isAbsolute(f));
|
|
4371
4371
|
const first = absolute[0];
|
|
@@ -4442,30 +4442,6 @@ function unresolvedGatesFor(gates, args) {
|
|
|
4442
4442
|
}
|
|
4443
4443
|
return unresolved;
|
|
4444
4444
|
}
|
|
4445
|
-
var emptyCounts = () => ({
|
|
4446
|
-
bound: 0,
|
|
4447
|
-
providers: 0,
|
|
4448
|
-
withBehavior: 0
|
|
4449
|
-
});
|
|
4450
|
-
function summaryCountsByPack(summaries) {
|
|
4451
|
-
const counts = /* @__PURE__ */ new Map();
|
|
4452
|
-
for (const summary of summaries) {
|
|
4453
|
-
const recognition = summary.identity.boundaryBinding?.recognition;
|
|
4454
|
-
if (recognition === void 0 || recognition === null) {
|
|
4455
|
-
continue;
|
|
4456
|
-
}
|
|
4457
|
-
const entry = counts.get(recognition) ?? emptyCounts();
|
|
4458
|
-
entry.bound += 1;
|
|
4459
|
-
if (BOUNDARY_ROLE[summary.kind] === "provider") {
|
|
4460
|
-
entry.providers += 1;
|
|
4461
|
-
}
|
|
4462
|
-
if (summary.transitions.length > 0) {
|
|
4463
|
-
entry.withBehavior += 1;
|
|
4464
|
-
}
|
|
4465
|
-
counts.set(recognition, entry);
|
|
4466
|
-
}
|
|
4467
|
-
return counts;
|
|
4468
|
-
}
|
|
4469
4445
|
function helpersWithNoCall(patterns, matched, projectRoot) {
|
|
4470
4446
|
const missing = [];
|
|
4471
4447
|
for (const pattern of patterns) {
|
|
@@ -4490,7 +4466,11 @@ function buildExtractionReport(args) {
|
|
|
4490
4466
|
const packFunnels = args.packs.map((pack) => {
|
|
4491
4467
|
const gates = packIsUngated(pack) ? [] : collectPackGates(pack);
|
|
4492
4468
|
const tally = args.tallies.get(pack.name) ?? emptyTally();
|
|
4493
|
-
const counted = bySummary.get(pack.name) ??
|
|
4469
|
+
const counted = bySummary.get(pack.name) ?? {
|
|
4470
|
+
bound: 0,
|
|
4471
|
+
providers: 0,
|
|
4472
|
+
withBehavior: 0
|
|
4473
|
+
};
|
|
4494
4474
|
return {
|
|
4495
4475
|
pack: pack.name,
|
|
4496
4476
|
version: pack.version ?? null,
|
|
@@ -4719,13 +4699,26 @@ function propertiesReached(object, resolution, seen) {
|
|
|
4719
4699
|
}
|
|
4720
4700
|
function writtenNodeOf(value, resolution) {
|
|
4721
4701
|
const written = factKeyOf(value);
|
|
4722
|
-
if (Node16.
|
|
4702
|
+
if (Node16.isNewExpression(written)) {
|
|
4723
4703
|
return written;
|
|
4724
4704
|
}
|
|
4705
|
+
if (Node16.isCallExpression(written)) {
|
|
4706
|
+
return writtenThroughCall(written, resolution);
|
|
4707
|
+
}
|
|
4725
4708
|
if (resolution === void 0 || !couldNameAValue(written)) {
|
|
4726
4709
|
return null;
|
|
4727
4710
|
}
|
|
4728
|
-
|
|
4711
|
+
const resolved = resolution.resolveWrittenValue(written);
|
|
4712
|
+
return resolved !== null && Node16.isCallExpression(resolved) ? writtenThroughCall(resolved, resolution) : resolved;
|
|
4713
|
+
}
|
|
4714
|
+
function writtenThroughCall(call, resolution) {
|
|
4715
|
+
if (resolution !== void 0 && resolution.resolveCallable(call.getExpression()) !== null) {
|
|
4716
|
+
const resolved = resolution.resolveWrittenValue(call);
|
|
4717
|
+
if (resolved !== null) {
|
|
4718
|
+
return resolved;
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4721
|
+
return call;
|
|
4729
4722
|
}
|
|
4730
4723
|
function stringValueOf(value, resolution) {
|
|
4731
4724
|
const written = factKeyOf(value);
|
|
@@ -4787,7 +4780,7 @@ function discoverClientCalls(sourceFile, match, kind, resolution) {
|
|
|
4787
4780
|
}
|
|
4788
4781
|
} else if (Node17.isPropertyAccessExpression(callee)) {
|
|
4789
4782
|
const subject = callee.getExpression();
|
|
4790
|
-
if (Node17.isIdentifier(subject) && (isClientImport(subject, match, resolution, true) || clientVarNames.has(subject.getText()) || resolvesToKnownInstance(subject, match, resolution))
|
|
4783
|
+
if (Node17.isIdentifier(subject) && (isClientImport(subject, match, resolution, true) || clientVarNames.has(subject.getText())) || resolvesToKnownInstance(subject, match, resolution)) {
|
|
4791
4784
|
methodName = callee.getName();
|
|
4792
4785
|
if (methodFilter === null || methodFilter.has(methodName)) {
|
|
4793
4786
|
matched = true;
|
|
@@ -6397,14 +6390,14 @@ function unitForJsxRoute(node, match, routeNames, kind, resolution) {
|
|
|
6397
6390
|
}
|
|
6398
6391
|
return routeUnit(node, kind, match.method, resolved.path, target, resolution);
|
|
6399
6392
|
}
|
|
6400
|
-
function routeUnit(at, kind, method,
|
|
6401
|
-
const routeInfo = { method, path:
|
|
6393
|
+
function routeUnit(at, kind, method, path14, target, resolution) {
|
|
6394
|
+
const routeInfo = { method, path: path14 };
|
|
6402
6395
|
if (target.kind === "unreadable") {
|
|
6403
6396
|
return {
|
|
6404
6397
|
func: null,
|
|
6405
6398
|
announcedAt: at,
|
|
6406
6399
|
kind,
|
|
6407
|
-
name:
|
|
6400
|
+
name: path14,
|
|
6408
6401
|
nameKind: "label",
|
|
6409
6402
|
routeInfo
|
|
6410
6403
|
};
|
|
@@ -6603,7 +6596,6 @@ function labelOfExpression(node) {
|
|
|
6603
6596
|
}
|
|
6604
6597
|
|
|
6605
6598
|
// src/discovery/packageExports.ts
|
|
6606
|
-
import fs4 from "fs";
|
|
6607
6599
|
import { Node as Node29 } from "ts-morph";
|
|
6608
6600
|
|
|
6609
6601
|
// src/packageExports.ts
|
|
@@ -6632,7 +6624,8 @@ function resolvePackageExports(packageJsonPath, opts = {}) {
|
|
|
6632
6624
|
packageName,
|
|
6633
6625
|
subPath,
|
|
6634
6626
|
exportPathPrefix: subPathToPrefix(subPath),
|
|
6635
|
-
sourceFile: src
|
|
6627
|
+
sourceFile: src,
|
|
6628
|
+
publishedFile: path5.resolve(pkgDir, distPath)
|
|
6636
6629
|
});
|
|
6637
6630
|
};
|
|
6638
6631
|
const exportsField = pkg.exports;
|
|
@@ -6652,6 +6645,53 @@ function resolvePackageExports(packageJsonPath, opts = {}) {
|
|
|
6652
6645
|
}
|
|
6653
6646
|
return { packageName, entries, warnings };
|
|
6654
6647
|
}
|
|
6648
|
+
var packageExportsCache = /* @__PURE__ */ new Map();
|
|
6649
|
+
var PATH_STAMP_SEPARATOR = "";
|
|
6650
|
+
function packageJsonStamp(packageJsonPath) {
|
|
6651
|
+
try {
|
|
6652
|
+
const stat = fs3.statSync(packageJsonPath);
|
|
6653
|
+
return `${stat.mtimeMs}:${stat.size}`;
|
|
6654
|
+
} catch {
|
|
6655
|
+
return "unreadable";
|
|
6656
|
+
}
|
|
6657
|
+
}
|
|
6658
|
+
function resolvePackageExportsCached(packageJsonPath) {
|
|
6659
|
+
const stamp2 = packageJsonStamp(packageJsonPath);
|
|
6660
|
+
const key = `${packageJsonPath}${PATH_STAMP_SEPARATOR}${stamp2}`;
|
|
6661
|
+
const cached = packageExportsCache.get(key);
|
|
6662
|
+
if (cached !== void 0) {
|
|
6663
|
+
return cached;
|
|
6664
|
+
}
|
|
6665
|
+
const fresh = resolvePackageExports(packageJsonPath);
|
|
6666
|
+
packageExportsCache.set(key, fresh);
|
|
6667
|
+
return fresh;
|
|
6668
|
+
}
|
|
6669
|
+
function namedPackageDirAbove(start) {
|
|
6670
|
+
let at = path5.resolve(start);
|
|
6671
|
+
for (let up = 0; up < 12; up += 1) {
|
|
6672
|
+
if (packageNameAt(at) !== null) {
|
|
6673
|
+
return at;
|
|
6674
|
+
}
|
|
6675
|
+
const parent = path5.dirname(at);
|
|
6676
|
+
if (parent === at) {
|
|
6677
|
+
break;
|
|
6678
|
+
}
|
|
6679
|
+
at = parent;
|
|
6680
|
+
}
|
|
6681
|
+
return null;
|
|
6682
|
+
}
|
|
6683
|
+
function packageNameAt(dir) {
|
|
6684
|
+
try {
|
|
6685
|
+
const manifest = JSON.parse(
|
|
6686
|
+
fs3.readFileSync(path5.join(dir, "package.json"), "utf8")
|
|
6687
|
+
);
|
|
6688
|
+
if (typeof manifest.name === "string" && manifest.name.length > 0) {
|
|
6689
|
+
return manifest.name;
|
|
6690
|
+
}
|
|
6691
|
+
} catch {
|
|
6692
|
+
}
|
|
6693
|
+
return null;
|
|
6694
|
+
}
|
|
6655
6695
|
function pickConditional(value, warnings, key) {
|
|
6656
6696
|
if (value === void 0 || value === null) {
|
|
6657
6697
|
if (value === null) {
|
|
@@ -6701,8 +6741,55 @@ function resolveSourceFor(pkgDir, distPath, srcRoot) {
|
|
|
6701
6741
|
return null;
|
|
6702
6742
|
}
|
|
6703
6743
|
|
|
6744
|
+
// src/resolve/sourceDeclaration.ts
|
|
6745
|
+
import path6 from "path";
|
|
6746
|
+
function sourceDeclarationsBehind(decl, resolution, visited = /* @__PURE__ */ new Set()) {
|
|
6747
|
+
const file = decl.getSourceFile();
|
|
6748
|
+
if (!file.isDeclarationFile() || file.isInNodeModules()) {
|
|
6749
|
+
return [decl];
|
|
6750
|
+
}
|
|
6751
|
+
const packageDir = namedPackageDirAbove(path6.dirname(file.getFilePath()));
|
|
6752
|
+
if (packageDir === null || visited.has(packageDir)) {
|
|
6753
|
+
return [decl];
|
|
6754
|
+
}
|
|
6755
|
+
visited.add(packageDir);
|
|
6756
|
+
const project = file.getProject();
|
|
6757
|
+
const { entries } = resolvePackageExportsCached(
|
|
6758
|
+
path6.join(packageDir, "package.json")
|
|
6759
|
+
);
|
|
6760
|
+
const found = [];
|
|
6761
|
+
for (const entry of entries) {
|
|
6762
|
+
const published = sourceFileFor(project, entry.publishedFile);
|
|
6763
|
+
const source = sourceFileFor(project, entry.sourceFile);
|
|
6764
|
+
if (published === void 0 || source === void 0) {
|
|
6765
|
+
continue;
|
|
6766
|
+
}
|
|
6767
|
+
for (const [name, nodes] of resolution.exportsOf(published)) {
|
|
6768
|
+
if (!nodes.some((node) => sameDeclaration(node, decl))) {
|
|
6769
|
+
continue;
|
|
6770
|
+
}
|
|
6771
|
+
for (const behind of resolution.exportsOf(source).get(name) ?? []) {
|
|
6772
|
+
for (const candidate of sourceDeclarationsBehind(
|
|
6773
|
+
behind,
|
|
6774
|
+
resolution,
|
|
6775
|
+
visited
|
|
6776
|
+
)) {
|
|
6777
|
+
if (!found.includes(candidate)) {
|
|
6778
|
+
found.push(candidate);
|
|
6779
|
+
}
|
|
6780
|
+
}
|
|
6781
|
+
}
|
|
6782
|
+
}
|
|
6783
|
+
}
|
|
6784
|
+
return found.length > 0 ? found : [decl];
|
|
6785
|
+
}
|
|
6786
|
+
function sameDeclaration(a, b) {
|
|
6787
|
+
return a === b || a.getSourceFile().getFilePath() === b.getSourceFile().getFilePath() && a.getStart() === b.getStart() && a.getEnd() === b.getEnd();
|
|
6788
|
+
}
|
|
6789
|
+
|
|
6704
6790
|
// src/discovery/factorySurface.ts
|
|
6705
6791
|
import { Node as Node28, SyntaxKind as SyntaxKind6 } from "ts-morph";
|
|
6792
|
+
var MAX_BUILDER_HOPS = 2;
|
|
6706
6793
|
function surfaceMethods(decl) {
|
|
6707
6794
|
if (Node28.isClassDeclaration(decl)) {
|
|
6708
6795
|
return surfaceClassMethods(decl);
|
|
@@ -6742,16 +6829,20 @@ function surfaceClassMethods(cls) {
|
|
|
6742
6829
|
function surfaceFactoryReturnMethods(fn) {
|
|
6743
6830
|
const out = [];
|
|
6744
6831
|
const seen = /* @__PURE__ */ new Set();
|
|
6832
|
+
collectFromFunctionReturns(fn, out, seen, 0);
|
|
6833
|
+
return out;
|
|
6834
|
+
}
|
|
6835
|
+
function collectFromFunctionReturns(fn, out, seen, depth) {
|
|
6745
6836
|
if (Node28.isArrowFunction(fn)) {
|
|
6746
6837
|
const body2 = fn.getBody();
|
|
6747
6838
|
if (Node28.isExpression(body2)) {
|
|
6748
|
-
|
|
6749
|
-
return
|
|
6839
|
+
collectFromReturnValue(body2, out, seen, depth);
|
|
6840
|
+
return;
|
|
6750
6841
|
}
|
|
6751
6842
|
}
|
|
6752
6843
|
const body = fn.getBody?.();
|
|
6753
6844
|
if (body === void 0) {
|
|
6754
|
-
return
|
|
6845
|
+
return;
|
|
6755
6846
|
}
|
|
6756
6847
|
const returns = [];
|
|
6757
6848
|
body.forEachDescendant((node, traversal) => {
|
|
@@ -6768,9 +6859,53 @@ function surfaceFactoryReturnMethods(fn) {
|
|
|
6768
6859
|
if (expr === void 0) {
|
|
6769
6860
|
continue;
|
|
6770
6861
|
}
|
|
6862
|
+
collectFromReturnValue(expr, out, seen, depth);
|
|
6863
|
+
}
|
|
6864
|
+
}
|
|
6865
|
+
function collectFromReturnValue(node, out, seen, depth) {
|
|
6866
|
+
const expr = peelParens(node);
|
|
6867
|
+
if (Node28.isObjectLiteralExpression(expr)) {
|
|
6771
6868
|
collectFromObjectLiteral(expr, out, seen);
|
|
6869
|
+
return;
|
|
6772
6870
|
}
|
|
6773
|
-
|
|
6871
|
+
if (depth >= MAX_BUILDER_HOPS || !Node28.isCallExpression(expr)) {
|
|
6872
|
+
return;
|
|
6873
|
+
}
|
|
6874
|
+
const callee = expr.getExpression();
|
|
6875
|
+
if (!Node28.isIdentifier(callee)) {
|
|
6876
|
+
return;
|
|
6877
|
+
}
|
|
6878
|
+
const helper = sameFileFunctionBehind(callee);
|
|
6879
|
+
if (helper !== null) {
|
|
6880
|
+
collectFromFunctionReturns(helper, out, seen, depth + 1);
|
|
6881
|
+
}
|
|
6882
|
+
}
|
|
6883
|
+
function sameFileFunctionBehind(id) {
|
|
6884
|
+
const symbol = id.getSymbol();
|
|
6885
|
+
if (symbol === void 0) {
|
|
6886
|
+
return null;
|
|
6887
|
+
}
|
|
6888
|
+
return functionAmong(
|
|
6889
|
+
symbol.getDeclarations(),
|
|
6890
|
+
id.getSourceFile().getFilePath()
|
|
6891
|
+
);
|
|
6892
|
+
}
|
|
6893
|
+
function functionAmong(declarations, filePath) {
|
|
6894
|
+
for (const decl of declarations) {
|
|
6895
|
+
if (decl.getSourceFile().getFilePath() !== filePath) {
|
|
6896
|
+
continue;
|
|
6897
|
+
}
|
|
6898
|
+
if (Node28.isFunctionDeclaration(decl)) {
|
|
6899
|
+
return decl;
|
|
6900
|
+
}
|
|
6901
|
+
if (Node28.isVariableDeclaration(decl)) {
|
|
6902
|
+
const init = decl.getInitializer();
|
|
6903
|
+
if (init !== void 0 && (Node28.isArrowFunction(init) || Node28.isFunctionExpression(init))) {
|
|
6904
|
+
return init;
|
|
6905
|
+
}
|
|
6906
|
+
}
|
|
6907
|
+
}
|
|
6908
|
+
return null;
|
|
6774
6909
|
}
|
|
6775
6910
|
function collectFromObjectLiteral(node, out, seen) {
|
|
6776
6911
|
const e = peelParens(node);
|
|
@@ -6805,31 +6940,31 @@ function collectFromObjectLiteral(node, out, seen) {
|
|
|
6805
6940
|
out.push({ func: v3, name });
|
|
6806
6941
|
}
|
|
6807
6942
|
}
|
|
6943
|
+
if (Node28.isShorthandPropertyAssignment(prop)) {
|
|
6944
|
+
collectShorthandFunction(prop, out, seen);
|
|
6945
|
+
}
|
|
6808
6946
|
}
|
|
6809
6947
|
}
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
function packageJsonStamp(packageJsonPath) {
|
|
6815
|
-
try {
|
|
6816
|
-
const stat = fs4.statSync(packageJsonPath);
|
|
6817
|
-
return `${stat.mtimeMs}:${stat.size}`;
|
|
6818
|
-
} catch {
|
|
6819
|
-
return "unreadable";
|
|
6948
|
+
function collectShorthandFunction(prop, out, seen) {
|
|
6949
|
+
const name = prop.getName();
|
|
6950
|
+
if (seen.has(name)) {
|
|
6951
|
+
return;
|
|
6820
6952
|
}
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
const
|
|
6826
|
-
|
|
6827
|
-
|
|
6953
|
+
const symbol = prop.getValueSymbol();
|
|
6954
|
+
if (symbol === void 0) {
|
|
6955
|
+
return;
|
|
6956
|
+
}
|
|
6957
|
+
const fn = functionAmong(
|
|
6958
|
+
symbol.getDeclarations(),
|
|
6959
|
+
prop.getSourceFile().getFilePath()
|
|
6960
|
+
);
|
|
6961
|
+
if (fn !== null) {
|
|
6962
|
+
seen.add(name);
|
|
6963
|
+
out.push({ func: fn, name });
|
|
6828
6964
|
}
|
|
6829
|
-
const fresh = resolvePackageExports(packageJsonPath);
|
|
6830
|
-
packageExportsCache.set(key, fresh);
|
|
6831
|
-
return fresh;
|
|
6832
6965
|
}
|
|
6966
|
+
|
|
6967
|
+
// src/discovery/packageExports.ts
|
|
6833
6968
|
function discoverPackageExports(sourceFile, match, kind, resolution) {
|
|
6834
6969
|
if (match.packageJsonPath === void 0) {
|
|
6835
6970
|
return [];
|
|
@@ -6864,7 +6999,10 @@ function discoverPackageExports(sourceFile, match, kind, resolution) {
|
|
|
6864
6999
|
if (seenNames.has(key)) {
|
|
6865
7000
|
continue;
|
|
6866
7001
|
}
|
|
6867
|
-
|
|
7002
|
+
const candidates = decls.flatMap(
|
|
7003
|
+
(decl) => sourceDeclarationsBehind(decl, resolution)
|
|
7004
|
+
);
|
|
7005
|
+
for (const decl of candidates) {
|
|
6868
7006
|
if (Node29.isVariableDeclaration(decl)) {
|
|
6869
7007
|
const init = decl.getInitializer();
|
|
6870
7008
|
if (init !== void 0 && (Node29.isArrowFunction(init) || Node29.isFunctionExpression(init))) {
|
|
@@ -7019,7 +7157,7 @@ function attributedCalls(sourceFile, packages, resolution) {
|
|
|
7019
7157
|
return null;
|
|
7020
7158
|
}
|
|
7021
7159
|
if (!methodComesFromSource(callee)) {
|
|
7022
|
-
return
|
|
7160
|
+
return null;
|
|
7023
7161
|
}
|
|
7024
7162
|
return {
|
|
7025
7163
|
packageName: subject.packageName,
|
|
@@ -7133,12 +7271,12 @@ function originEndOf(flattened) {
|
|
|
7133
7271
|
const slash = flattened.indexOf("/", opener[0].length);
|
|
7134
7272
|
return slash === -1 ? flattened.length : slash;
|
|
7135
7273
|
}
|
|
7136
|
-
function appendPathText(
|
|
7274
|
+
function appendPathText(path14, text) {
|
|
7137
7275
|
const idx = text.search(/[?#]/);
|
|
7138
7276
|
if (idx === -1) {
|
|
7139
|
-
return { path:
|
|
7277
|
+
return { path: path14 + text, stop: false };
|
|
7140
7278
|
}
|
|
7141
|
-
return { path:
|
|
7279
|
+
return { path: path14 + text.slice(0, idx), stop: true };
|
|
7142
7280
|
}
|
|
7143
7281
|
function parseAbsoluteUrl(text) {
|
|
7144
7282
|
try {
|
|
@@ -7162,8 +7300,8 @@ function pathnameOfAbsoluteLiteral(text) {
|
|
|
7162
7300
|
return stripQueryAndFragment(stripOriginManually(text));
|
|
7163
7301
|
}
|
|
7164
7302
|
function pathFromLiteralUrl(text) {
|
|
7165
|
-
const
|
|
7166
|
-
return
|
|
7303
|
+
const path14 = isAbsoluteUrlLiteral(text) ? pathnameOfAbsoluteLiteral(text) : stripQueryAndFragment(text);
|
|
7304
|
+
return path14 === "" ? void 0 : path14;
|
|
7167
7305
|
}
|
|
7168
7306
|
function pathFromTemplateUrl(arg, resolution) {
|
|
7169
7307
|
const headText = arg.getHead().getLiteralText();
|
|
@@ -7171,7 +7309,7 @@ function pathFromTemplateUrl(arg, resolution) {
|
|
|
7171
7309
|
const tails = spans.map((span) => span.getLiteral().getLiteralText());
|
|
7172
7310
|
const originEnd = originEndOf([headText, ...tails].join(SUBSTITUTION));
|
|
7173
7311
|
const head = appendPathText("", headText.slice(originEnd));
|
|
7174
|
-
let
|
|
7312
|
+
let path14 = head.path;
|
|
7175
7313
|
let stop = head.stop;
|
|
7176
7314
|
let at = headText.length;
|
|
7177
7315
|
for (let i = 0; i < spans.length && !stop; i++) {
|
|
@@ -7181,18 +7319,18 @@ function pathFromTemplateUrl(arg, resolution) {
|
|
|
7181
7319
|
continue;
|
|
7182
7320
|
}
|
|
7183
7321
|
if (at >= originEnd) {
|
|
7184
|
-
|
|
7322
|
+
path14 += holeText(span.getExpression(), resolution);
|
|
7185
7323
|
}
|
|
7186
7324
|
at += SUBSTITUTION.length;
|
|
7187
7325
|
const appended = appendPathText(
|
|
7188
|
-
|
|
7326
|
+
path14,
|
|
7189
7327
|
tailText.slice(Math.max(0, originEnd - at))
|
|
7190
7328
|
);
|
|
7191
|
-
|
|
7329
|
+
path14 = appended.path;
|
|
7192
7330
|
stop = appended.stop;
|
|
7193
7331
|
at += tailText.length;
|
|
7194
7332
|
}
|
|
7195
|
-
return
|
|
7333
|
+
return path14 === "" ? void 0 : path14;
|
|
7196
7334
|
}
|
|
7197
7335
|
function pathFromUrlNode(node, resolution) {
|
|
7198
7336
|
if (Node31.isStringLiteral(node)) {
|
|
@@ -7262,12 +7400,12 @@ function discoverRegistrationCalls(sourceFile, match, kind, bindingExtraction, r
|
|
|
7262
7400
|
match.importName,
|
|
7263
7401
|
resolution
|
|
7264
7402
|
);
|
|
7265
|
-
if (registrationSubjects.size === 0 && !storeCanFindSubjects(sourceFile, match, resolution)) {
|
|
7266
|
-
return results;
|
|
7267
|
-
}
|
|
7268
7403
|
const registrationMethods = match.registrationChain.map(
|
|
7269
7404
|
(c) => c.startsWith(".") ? c.slice(1) : c
|
|
7270
7405
|
);
|
|
7406
|
+
if (registrationSubjects.size === 0 && !storeCanFindSubjects(sourceFile, match, resolution, registrationMethods)) {
|
|
7407
|
+
return results;
|
|
7408
|
+
}
|
|
7271
7409
|
sourceFile.forEachDescendant((node) => {
|
|
7272
7410
|
if (!Node32.isCallExpression(node)) {
|
|
7273
7411
|
return;
|
|
@@ -7461,8 +7599,54 @@ function receiverOf(subject, subjects, match, resolution) {
|
|
|
7461
7599
|
);
|
|
7462
7600
|
return candidates > 1 ? { kind: "several", candidates } : NOT_A_ROUTABLE;
|
|
7463
7601
|
}
|
|
7464
|
-
function storeCanFindSubjects(sourceFile, match, resolution) {
|
|
7465
|
-
|
|
7602
|
+
function storeCanFindSubjects(sourceFile, match, resolution, methods = []) {
|
|
7603
|
+
if (resolution === void 0) {
|
|
7604
|
+
return false;
|
|
7605
|
+
}
|
|
7606
|
+
if (importDeclarationsOf(sourceFile, [match.importModule]).length > 0) {
|
|
7607
|
+
return true;
|
|
7608
|
+
}
|
|
7609
|
+
return methods.length > 0 && callsOnCallResult(sourceFile, methods);
|
|
7610
|
+
}
|
|
7611
|
+
function callsOnCallResult(sourceFile, methods) {
|
|
7612
|
+
let called = methodsCalledOnCallResults.get(sourceFile);
|
|
7613
|
+
if (called === void 0) {
|
|
7614
|
+
called = collectMethodsCalledOnCallResults(sourceFile);
|
|
7615
|
+
methodsCalledOnCallResults.set(sourceFile, called);
|
|
7616
|
+
}
|
|
7617
|
+
return methods.some((method) => called.has(method));
|
|
7618
|
+
}
|
|
7619
|
+
var methodsCalledOnCallResults = /* @__PURE__ */ new WeakMap();
|
|
7620
|
+
function collectMethodsCalledOnCallResults(sourceFile) {
|
|
7621
|
+
const setToCalls = /* @__PURE__ */ new Set();
|
|
7622
|
+
const calledOn = /* @__PURE__ */ new Map();
|
|
7623
|
+
sourceFile.forEachDescendant((node) => {
|
|
7624
|
+
if (Node32.isVariableDeclaration(node)) {
|
|
7625
|
+
const init = node.getInitializer();
|
|
7626
|
+
if (init !== void 0 && Node32.isCallExpression(init)) {
|
|
7627
|
+
setToCalls.add(node.getName());
|
|
7628
|
+
}
|
|
7629
|
+
return;
|
|
7630
|
+
}
|
|
7631
|
+
if (!Node32.isCallExpression(node)) {
|
|
7632
|
+
return;
|
|
7633
|
+
}
|
|
7634
|
+
const callee = node.getExpression();
|
|
7635
|
+
if (!Node32.isPropertyAccessExpression(callee) || !Node32.isIdentifier(callee.getExpression())) {
|
|
7636
|
+
return;
|
|
7637
|
+
}
|
|
7638
|
+
const receiver = callee.getExpression().getText();
|
|
7639
|
+
const methods = calledOn.get(receiver) ?? /* @__PURE__ */ new Set();
|
|
7640
|
+
methods.add(callee.getName());
|
|
7641
|
+
calledOn.set(receiver, methods);
|
|
7642
|
+
});
|
|
7643
|
+
const called = /* @__PURE__ */ new Set();
|
|
7644
|
+
for (const name of setToCalls) {
|
|
7645
|
+
for (const method of calledOn.get(name) ?? []) {
|
|
7646
|
+
called.add(method);
|
|
7647
|
+
}
|
|
7648
|
+
}
|
|
7649
|
+
return called;
|
|
7466
7650
|
}
|
|
7467
7651
|
function registrationSubjectsOf(sourceFile, importModule, importName, resolution) {
|
|
7468
7652
|
const importedLocalName = resolveImportedLocalName(
|
|
@@ -7527,9 +7711,33 @@ function registrationSubjectIdsOf(sourceFile, matches, resolution) {
|
|
|
7527
7711
|
).values()) {
|
|
7528
7712
|
ids.add(nodeId(node));
|
|
7529
7713
|
}
|
|
7714
|
+
for (const node of unboundConstructionsOf(
|
|
7715
|
+
sourceFile,
|
|
7716
|
+
match.importModule,
|
|
7717
|
+
match.importName
|
|
7718
|
+
)) {
|
|
7719
|
+
ids.add(nodeId(node));
|
|
7720
|
+
}
|
|
7530
7721
|
}
|
|
7531
7722
|
return ids;
|
|
7532
7723
|
}
|
|
7724
|
+
function unboundConstructionsOf(sourceFile, importModule, importName) {
|
|
7725
|
+
const importedLocalName = resolveImportedLocalName(
|
|
7726
|
+
sourceFile,
|
|
7727
|
+
importModule,
|
|
7728
|
+
importName
|
|
7729
|
+
);
|
|
7730
|
+
if (importedLocalName === null) {
|
|
7731
|
+
return [];
|
|
7732
|
+
}
|
|
7733
|
+
const constructions = [];
|
|
7734
|
+
sourceFile.forEachDescendant((node) => {
|
|
7735
|
+
if ((Node32.isCallExpression(node) || Node32.isNewExpression(node)) && node.getExpression().getText() === importedLocalName && !Node32.isVariableDeclaration(node.getParent())) {
|
|
7736
|
+
constructions.push(node);
|
|
7737
|
+
}
|
|
7738
|
+
});
|
|
7739
|
+
return constructions;
|
|
7740
|
+
}
|
|
7533
7741
|
function discoverMountEdges(sourceFile, match, mount, knownSubjectIds, resolution) {
|
|
7534
7742
|
const subjects = registrationSubjectsOf(
|
|
7535
7743
|
sourceFile,
|
|
@@ -7537,7 +7745,7 @@ function discoverMountEdges(sourceFile, match, mount, knownSubjectIds, resolutio
|
|
|
7537
7745
|
match.importName,
|
|
7538
7746
|
resolution
|
|
7539
7747
|
);
|
|
7540
|
-
if (subjects.size === 0 && !storeCanFindSubjects(sourceFile, match, resolution)) {
|
|
7748
|
+
if (subjects.size === 0 && !storeCanFindSubjects(sourceFile, match, resolution, [mount.method])) {
|
|
7541
7749
|
return [];
|
|
7542
7750
|
}
|
|
7543
7751
|
const edges = [];
|
|
@@ -7618,11 +7826,11 @@ function extractRouteInfoFromBinding(call, methodName, binding, resolution) {
|
|
|
7618
7826
|
return null;
|
|
7619
7827
|
}
|
|
7620
7828
|
const method2 = stringProperty(routeObject, binding.method.property);
|
|
7621
|
-
const
|
|
7622
|
-
if (method2 === null ||
|
|
7829
|
+
const path15 = stringProperty(routeObject, binding.path.property);
|
|
7830
|
+
if (method2 === null || path15 === null) {
|
|
7623
7831
|
return null;
|
|
7624
7832
|
}
|
|
7625
|
-
return { method: method2.toUpperCase(), path:
|
|
7833
|
+
return { method: method2.toUpperCase(), path: path15 };
|
|
7626
7834
|
}
|
|
7627
7835
|
if (binding.method.type !== "fromRegistration" || binding.path.type !== "fromArgument") {
|
|
7628
7836
|
return null;
|
|
@@ -7639,8 +7847,8 @@ function extractRouteInfoFromBinding(call, methodName, binding, resolution) {
|
|
|
7639
7847
|
method = arg.getLiteralValue().toUpperCase();
|
|
7640
7848
|
}
|
|
7641
7849
|
const pathArg = call.getArguments()[binding.path.position];
|
|
7642
|
-
const
|
|
7643
|
-
return
|
|
7850
|
+
const path14 = pathArg === void 0 ? void 0 : pathFromArgument(pathArg, resolution);
|
|
7851
|
+
return path14 === void 0 ? null : { method, path: path14 };
|
|
7644
7852
|
}
|
|
7645
7853
|
|
|
7646
7854
|
// src/discovery/registrationLoop.ts
|
|
@@ -7772,7 +7980,7 @@ function bodyCallsOnRoutable(loop, guard, resolution) {
|
|
|
7772
7980
|
}
|
|
7773
7981
|
function readRouteSpec(element, shape, resolution) {
|
|
7774
7982
|
let method = null;
|
|
7775
|
-
let
|
|
7983
|
+
let path14 = null;
|
|
7776
7984
|
let handler = null;
|
|
7777
7985
|
for (const prop of element.getProperties()) {
|
|
7778
7986
|
if (Node33.isMethodDeclaration(prop) && prop.getName() === shape.handlerKey) {
|
|
@@ -7790,16 +7998,16 @@ function readRouteSpec(element, shape, resolution) {
|
|
|
7790
7998
|
if (name === shape.methodKey) {
|
|
7791
7999
|
method = readStringLiteralValue(init);
|
|
7792
8000
|
} else if (name === shape.pathKey) {
|
|
7793
|
-
|
|
8001
|
+
path14 = readStringLiteralValue(init);
|
|
7794
8002
|
} else if (name === shape.handlerKey) {
|
|
7795
8003
|
const func = functionValueOf(init, resolution);
|
|
7796
8004
|
handler = func === null ? null : { func, name: handlerName(init) };
|
|
7797
8005
|
}
|
|
7798
8006
|
}
|
|
7799
|
-
if (method === null ||
|
|
8007
|
+
if (method === null || path14 === null || handler === null) {
|
|
7800
8008
|
return null;
|
|
7801
8009
|
}
|
|
7802
|
-
return { method, path:
|
|
8010
|
+
return { method, path: path14, handler: handler.func, handlerName: handler.name };
|
|
7803
8011
|
}
|
|
7804
8012
|
function readStringLiteralValue(node) {
|
|
7805
8013
|
if (Node33.isStringLiteral(node) || Node33.isNoSubstitutionTemplateLiteral(node)) {
|
|
@@ -7812,7 +8020,7 @@ function handlerName(value) {
|
|
|
7812
8020
|
}
|
|
7813
8021
|
|
|
7814
8022
|
// src/discovery/registrationTemplate.ts
|
|
7815
|
-
import
|
|
8023
|
+
import path7 from "path";
|
|
7816
8024
|
import { Node as Node35 } from "ts-morph";
|
|
7817
8025
|
import { joinMountedPath as joinMountedPath2 } from "@suss/resolution";
|
|
7818
8026
|
|
|
@@ -7821,6 +8029,10 @@ import {
|
|
|
7821
8029
|
Node as Node34,
|
|
7822
8030
|
SyntaxKind as SyntaxKind7
|
|
7823
8031
|
} from "ts-morph";
|
|
8032
|
+
import {
|
|
8033
|
+
unfollowedCallGap,
|
|
8034
|
+
worthRecording
|
|
8035
|
+
} from "@suss/behavioral-ir";
|
|
7824
8036
|
function isInExternalCode(sourceFile) {
|
|
7825
8037
|
if (sourceFile.isDeclarationFile()) {
|
|
7826
8038
|
return true;
|
|
@@ -7874,6 +8086,19 @@ function declarationsBehind(symbol) {
|
|
|
7874
8086
|
function isParameterOf(declaration, scanning) {
|
|
7875
8087
|
return Node34.isParameterDeclaration(declaration) && declaration.getParent() === scanning;
|
|
7876
8088
|
}
|
|
8089
|
+
function parameterIndexOf(declarations, scanning) {
|
|
8090
|
+
if (scanning === void 0) {
|
|
8091
|
+
return void 0;
|
|
8092
|
+
}
|
|
8093
|
+
const params = scanning.getParameters();
|
|
8094
|
+
for (const declaration of declarations) {
|
|
8095
|
+
const index = params.findIndex((param) => param === declaration);
|
|
8096
|
+
if (index !== -1) {
|
|
8097
|
+
return index;
|
|
8098
|
+
}
|
|
8099
|
+
}
|
|
8100
|
+
return void 0;
|
|
8101
|
+
}
|
|
7877
8102
|
function classifyStop(declarations, scanning) {
|
|
7878
8103
|
if (declarations.length === 0) {
|
|
7879
8104
|
return "noDeclaration";
|
|
@@ -7894,36 +8119,6 @@ function classifyStop(declarations, scanning) {
|
|
|
7894
8119
|
}
|
|
7895
8120
|
return "unsettledValue";
|
|
7896
8121
|
}
|
|
7897
|
-
var RECORDED = {
|
|
7898
|
-
noBody: true,
|
|
7899
|
-
unsettledValue: true,
|
|
7900
|
-
multipleSources: true,
|
|
7901
|
-
outsideRun: false,
|
|
7902
|
-
noDeclaration: false,
|
|
7903
|
-
callerSupplied: false,
|
|
7904
|
-
multipleReceivers: true
|
|
7905
|
-
};
|
|
7906
|
-
function worthRecording(reason) {
|
|
7907
|
-
return RECORDED[reason];
|
|
7908
|
-
}
|
|
7909
|
-
var STOP_SENTENCE = {
|
|
7910
|
-
noBody: ({ callee }) => `The call to ${callee} lands on a declaration with no body, so whatever runs there is missing from this summary`,
|
|
7911
|
-
unsettledValue: ({ callee }) => `The call to ${callee} goes through a value this run could not settle, so whatever runs there is missing from this summary`,
|
|
7912
|
-
multipleSources: ({ callee }) => `The call to ${callee} reaches a value with more than one possible source, so whatever runs there is missing from this summary`,
|
|
7913
|
-
outsideRun: ({ callee }) => `The call to ${callee} lands in a package whose source is not in this run, so whatever runs there is missing from this summary`,
|
|
7914
|
-
noDeclaration: ({ callee }) => `The call to ${callee} has no declaration this run could find, so whatever runs there is missing from this summary`,
|
|
7915
|
-
callerSupplied: ({ callee }) => `The call to ${callee} runs the function this unit's caller passed in, so what happens there is decided at the call site`,
|
|
7916
|
-
multipleReceivers: ({ callee, candidates }) => `The call to ${callee} is made on a receiver this run reads as ${candidates ?? "several"} different values, so nothing says which one it registers on and the registration is left out`
|
|
7917
|
-
};
|
|
7918
|
-
function unfollowedCallGap(stop) {
|
|
7919
|
-
return {
|
|
7920
|
-
type: "unfollowedCall",
|
|
7921
|
-
conditions: [],
|
|
7922
|
-
consequence: "unknown",
|
|
7923
|
-
description: STOP_SENTENCE[stop.reason](stop),
|
|
7924
|
-
callee: stop.callee
|
|
7925
|
-
};
|
|
7926
|
-
}
|
|
7927
8122
|
|
|
7928
8123
|
// src/discovery/registrationTemplate.ts
|
|
7929
8124
|
function discoverRegistrationTemplates(sourceFile, match, kind, resolution, mountPrefixes) {
|
|
@@ -7933,8 +8128,8 @@ function discoverRegistrationTemplates(sourceFile, match, kind, resolution, moun
|
|
|
7933
8128
|
const subject = subjectOfCall(sourceFile, args, match, resolution);
|
|
7934
8129
|
const prefix = subject === void 0 ? "" : mountPrefixes?.effectivePrefixFor(subject) ?? "";
|
|
7935
8130
|
for (const reg of match.registrations) {
|
|
7936
|
-
const
|
|
7937
|
-
if (
|
|
8131
|
+
const path14 = substitutePath(reg.pathTemplate, args);
|
|
8132
|
+
if (path14 === null) {
|
|
7938
8133
|
continue;
|
|
7939
8134
|
}
|
|
7940
8135
|
const handler = resolveHandler(reg.handlerArg, args, resolution);
|
|
@@ -7948,7 +8143,7 @@ function discoverRegistrationTemplates(sourceFile, match, kind, resolution, moun
|
|
|
7948
8143
|
...subject === void 0 ? {} : { registrationSubjectId: nodeId(subject) },
|
|
7949
8144
|
routeInfo: {
|
|
7950
8145
|
method: reg.method.toUpperCase(),
|
|
7951
|
-
path: prefix === "" ?
|
|
8146
|
+
path: prefix === "" ? path14 : joinMountedPath2(prefix, path14)
|
|
7952
8147
|
}
|
|
7953
8148
|
});
|
|
7954
8149
|
}
|
|
@@ -7981,7 +8176,7 @@ function subjectOfCall(sourceFile, args, match, resolution) {
|
|
|
7981
8176
|
}
|
|
7982
8177
|
function helperCallsOf(sourceFile, match, resolution) {
|
|
7983
8178
|
const module = match.importModule;
|
|
7984
|
-
if (module !== void 0 && !
|
|
8179
|
+
if (module !== void 0 && !path7.isAbsolute(module)) {
|
|
7985
8180
|
const store = resolution ?? new ResolutionStore();
|
|
7986
8181
|
return callsResolvingTo(sourceFile, store, {
|
|
7987
8182
|
module,
|
|
@@ -8427,24 +8622,24 @@ function declName(decl) {
|
|
|
8427
8622
|
|
|
8428
8623
|
// src/parameterReads.ts
|
|
8429
8624
|
function pathAbove(reference) {
|
|
8430
|
-
const
|
|
8625
|
+
const path14 = [];
|
|
8431
8626
|
let current2 = reference;
|
|
8432
8627
|
for (; ; ) {
|
|
8433
8628
|
const parent = current2.getParent();
|
|
8434
8629
|
if (parent !== void 0 && Node38.isPropertyAccessExpression(parent) && parent.getExpression() === current2) {
|
|
8435
|
-
|
|
8630
|
+
path14.push(parent.getName());
|
|
8436
8631
|
current2 = parent;
|
|
8437
8632
|
continue;
|
|
8438
8633
|
}
|
|
8439
8634
|
if (parent !== void 0 && Node38.isElementAccessExpression(parent) && parent.getExpression() === current2) {
|
|
8440
8635
|
const argument = parent.getArgumentExpression();
|
|
8441
8636
|
if (argument !== void 0 && Node38.isStringLiteral(argument)) {
|
|
8442
|
-
|
|
8637
|
+
path14.push(argument.getLiteralValue());
|
|
8443
8638
|
current2 = parent;
|
|
8444
8639
|
continue;
|
|
8445
8640
|
}
|
|
8446
8641
|
}
|
|
8447
|
-
return
|
|
8642
|
+
return path14;
|
|
8448
8643
|
}
|
|
8449
8644
|
}
|
|
8450
8645
|
function declaresParameterOf(node, func) {
|
|
@@ -9143,142 +9338,40 @@ function importSpecifierMatches(decl, expectedModule) {
|
|
|
9143
9338
|
return Node41.isImportDeclaration(importDecl) && importDecl.getModuleSpecifierValue() === expectedModule;
|
|
9144
9339
|
}
|
|
9145
9340
|
function extractInvocationEffects(func, barriers = NO_BARRIERS) {
|
|
9146
|
-
const
|
|
9341
|
+
const calls = [];
|
|
9147
9342
|
func.forEachDescendant((node, traversal) => {
|
|
9148
|
-
if (isDescentStop(node, func, barriers)) {
|
|
9343
|
+
if (isDescentStop(node, func, barriers) || Node41.isDecorator(node)) {
|
|
9149
9344
|
traversal.skip();
|
|
9150
9345
|
return;
|
|
9151
9346
|
}
|
|
9152
|
-
if (Node41.isExpressionStatement(node)) {
|
|
9153
|
-
const { call, async } = unwrapCall(node.getExpression());
|
|
9154
|
-
if (call !== null) {
|
|
9155
|
-
const preconditions = collectPreconditions(node, func);
|
|
9156
|
-
results.push({
|
|
9157
|
-
effect: {
|
|
9158
|
-
type: "invocation",
|
|
9159
|
-
callee: call.getExpression().getText(),
|
|
9160
|
-
args: extractArgs(call),
|
|
9161
|
-
async,
|
|
9162
|
-
...preconditions.length > 0 ? { preconditions } : {}
|
|
9163
|
-
},
|
|
9164
|
-
line: startLineOf(node),
|
|
9165
|
-
neverTerminal: false,
|
|
9166
|
-
alwaysRuns: runsOnEveryPath(node, func)
|
|
9167
|
-
});
|
|
9168
|
-
}
|
|
9169
|
-
return;
|
|
9170
|
-
}
|
|
9171
|
-
if (Node41.isSpreadElement(node)) {
|
|
9172
|
-
const parent = node.getParent();
|
|
9173
|
-
if (parent !== void 0 && (Node41.isArrayLiteralExpression(parent) || Node41.isObjectLiteralExpression(parent))) {
|
|
9174
|
-
const { call, async } = unwrapCall(node.getExpression());
|
|
9175
|
-
if (call !== null) {
|
|
9176
|
-
const preconditions = collectPreconditions(node, func);
|
|
9177
|
-
results.push({
|
|
9178
|
-
effect: {
|
|
9179
|
-
type: "invocation",
|
|
9180
|
-
callee: call.getExpression().getText(),
|
|
9181
|
-
args: extractArgs(call),
|
|
9182
|
-
async,
|
|
9183
|
-
...preconditions.length > 0 ? { preconditions } : {}
|
|
9184
|
-
},
|
|
9185
|
-
line: enclosingStatementLine(node),
|
|
9186
|
-
neverTerminal: true,
|
|
9187
|
-
alwaysRuns: runsOnEveryPath(node, func)
|
|
9188
|
-
});
|
|
9189
|
-
}
|
|
9190
|
-
}
|
|
9191
|
-
return;
|
|
9192
|
-
}
|
|
9193
|
-
if (Node41.isVariableDeclaration(node)) {
|
|
9194
|
-
const initializer = node.getInitializer();
|
|
9195
|
-
if (initializer !== void 0) {
|
|
9196
|
-
const { call, async } = unwrapCall(initializer);
|
|
9197
|
-
if (call !== null) {
|
|
9198
|
-
const preconditions = collectPreconditions(node, func);
|
|
9199
|
-
results.push({
|
|
9200
|
-
effect: {
|
|
9201
|
-
type: "invocation",
|
|
9202
|
-
callee: call.getExpression().getText(),
|
|
9203
|
-
args: extractArgs(call),
|
|
9204
|
-
async,
|
|
9205
|
-
...preconditions.length > 0 ? { preconditions } : {}
|
|
9206
|
-
},
|
|
9207
|
-
line: enclosingStatementLine(node),
|
|
9208
|
-
neverTerminal: true,
|
|
9209
|
-
alwaysRuns: runsOnEveryPath(node, func)
|
|
9210
|
-
});
|
|
9211
|
-
}
|
|
9212
|
-
}
|
|
9213
|
-
return;
|
|
9214
|
-
}
|
|
9215
|
-
if (Node41.isReturnStatement(node)) {
|
|
9216
|
-
const returned = node.getExpression();
|
|
9217
|
-
const { call, async } = returned === void 0 ? { call: null, async: false } : unwrapCall(returned);
|
|
9218
|
-
if (call !== null) {
|
|
9219
|
-
const preconditions = collectPreconditions(node, func);
|
|
9220
|
-
results.push({
|
|
9221
|
-
effect: {
|
|
9222
|
-
type: "invocation",
|
|
9223
|
-
callee: call.getExpression().getText(),
|
|
9224
|
-
args: extractArgs(call),
|
|
9225
|
-
async,
|
|
9226
|
-
...preconditions.length > 0 ? { preconditions } : {}
|
|
9227
|
-
},
|
|
9228
|
-
line: startLineOf(node),
|
|
9229
|
-
neverTerminal: false,
|
|
9230
|
-
alwaysRuns: runsOnEveryPath(node, func),
|
|
9231
|
-
node: call
|
|
9232
|
-
});
|
|
9233
|
-
}
|
|
9234
|
-
return;
|
|
9235
|
-
}
|
|
9236
|
-
if (Node41.isArrowFunction(node)) {
|
|
9237
|
-
const body = node.getBody();
|
|
9238
|
-
const { call, async } = Node41.isExpression(body) ? unwrapCall(body) : { call: null, async: false };
|
|
9239
|
-
if (call !== null) {
|
|
9240
|
-
const preconditions = collectPreconditions(node, func);
|
|
9241
|
-
results.push({
|
|
9242
|
-
effect: {
|
|
9243
|
-
type: "invocation",
|
|
9244
|
-
callee: call.getExpression().getText(),
|
|
9245
|
-
args: extractArgs(call),
|
|
9246
|
-
async: async || node.isAsync(),
|
|
9247
|
-
...preconditions.length > 0 ? { preconditions } : {}
|
|
9248
|
-
},
|
|
9249
|
-
line: startLineOf(node),
|
|
9250
|
-
neverTerminal: false,
|
|
9251
|
-
alwaysRuns: runsOnEveryPath(node, func),
|
|
9252
|
-
node: call
|
|
9253
|
-
});
|
|
9254
|
-
}
|
|
9255
|
-
return;
|
|
9256
|
-
}
|
|
9257
9347
|
if (Node41.isCallExpression(node)) {
|
|
9258
|
-
|
|
9259
|
-
if (parent === void 0) {
|
|
9260
|
-
return;
|
|
9261
|
-
}
|
|
9262
|
-
const isArrayElement = Node41.isArrayLiteralExpression(parent);
|
|
9263
|
-
const isPropertyValue = Node41.isPropertyAssignment(parent) && parent.getInitializer() === node;
|
|
9264
|
-
if (isArrayElement || isPropertyValue) {
|
|
9265
|
-
const preconditions = collectPreconditions(node, func);
|
|
9266
|
-
results.push({
|
|
9267
|
-
effect: {
|
|
9268
|
-
type: "invocation",
|
|
9269
|
-
callee: node.getExpression().getText(),
|
|
9270
|
-
args: extractArgs(node),
|
|
9271
|
-
async: false,
|
|
9272
|
-
...preconditions.length > 0 ? { preconditions } : {}
|
|
9273
|
-
},
|
|
9274
|
-
line: enclosingStatementLine(node),
|
|
9275
|
-
neverTerminal: true,
|
|
9276
|
-
alwaysRuns: runsOnEveryPath(node, func)
|
|
9277
|
-
});
|
|
9278
|
-
}
|
|
9348
|
+
calls.push(node);
|
|
9279
9349
|
}
|
|
9280
9350
|
});
|
|
9281
|
-
|
|
9351
|
+
calls.sort((a, b) => a.getEnd() - b.getEnd());
|
|
9352
|
+
return calls.map((call) => invocationAt(call, func));
|
|
9353
|
+
}
|
|
9354
|
+
function invocationAt(call, func) {
|
|
9355
|
+
const preconditions = collectPreconditions(call, func);
|
|
9356
|
+
return {
|
|
9357
|
+
effect: {
|
|
9358
|
+
type: "invocation",
|
|
9359
|
+
callee: call.getExpression().getText(),
|
|
9360
|
+
args: extractArgs(call),
|
|
9361
|
+
async: isAwaited(call),
|
|
9362
|
+
...preconditions.length > 0 ? { preconditions } : {}
|
|
9363
|
+
},
|
|
9364
|
+
node: call,
|
|
9365
|
+
line: enclosingStatementLine(call),
|
|
9366
|
+
alwaysRuns: runsOnEveryPath(call, func)
|
|
9367
|
+
};
|
|
9368
|
+
}
|
|
9369
|
+
function isAwaited(call) {
|
|
9370
|
+
let current2 = call.getParent();
|
|
9371
|
+
while (current2 !== void 0 && Node41.isParenthesizedExpression(current2)) {
|
|
9372
|
+
current2 = current2.getParent();
|
|
9373
|
+
}
|
|
9374
|
+
return current2 !== void 0 && Node41.isAwaitExpression(current2);
|
|
9282
9375
|
}
|
|
9283
9376
|
function runInvocationRecognizers(func, recognizers, barriers = NO_BARRIERS, resolveWrittenValue, originatesFrom, anchorCallsOf) {
|
|
9284
9377
|
if (recognizers.length === 0) {
|
|
@@ -9621,22 +9714,6 @@ function enclosingStatementLine(node) {
|
|
|
9621
9714
|
}
|
|
9622
9715
|
return startLineOf(node);
|
|
9623
9716
|
}
|
|
9624
|
-
function unwrapCall(node) {
|
|
9625
|
-
if (Node41.isAwaitExpression(node)) {
|
|
9626
|
-
const inner = node.getExpression();
|
|
9627
|
-
if (Node41.isCallExpression(inner)) {
|
|
9628
|
-
return { call: inner, async: true };
|
|
9629
|
-
}
|
|
9630
|
-
return { call: null, async: false };
|
|
9631
|
-
}
|
|
9632
|
-
if (Node41.isParenthesizedExpression(node)) {
|
|
9633
|
-
return unwrapCall(node.getExpression());
|
|
9634
|
-
}
|
|
9635
|
-
if (Node41.isCallExpression(node)) {
|
|
9636
|
-
return { call: node, async: false };
|
|
9637
|
-
}
|
|
9638
|
-
return { call: null, async: false };
|
|
9639
|
-
}
|
|
9640
9717
|
|
|
9641
9718
|
// src/terminals/index.ts
|
|
9642
9719
|
import { Node as Node51 } from "ts-morph";
|
|
@@ -11678,6 +11755,22 @@ function firesOn(site, branch) {
|
|
|
11678
11755
|
}
|
|
11679
11756
|
return site.alwaysRuns || runsBefore(site.line, branch.terminal.location.end);
|
|
11680
11757
|
}
|
|
11758
|
+
function writesTerminal(call, terminalNodes) {
|
|
11759
|
+
let current2 = call;
|
|
11760
|
+
for (; ; ) {
|
|
11761
|
+
if (terminalNodes.has(current2)) {
|
|
11762
|
+
return true;
|
|
11763
|
+
}
|
|
11764
|
+
const parent = current2.getParent();
|
|
11765
|
+
if (parent === void 0 || !isReceiverOf(current2, parent)) {
|
|
11766
|
+
return false;
|
|
11767
|
+
}
|
|
11768
|
+
current2 = parent;
|
|
11769
|
+
}
|
|
11770
|
+
}
|
|
11771
|
+
function isReceiverOf(node, parent) {
|
|
11772
|
+
return (Node52.isPropertyAccessExpression(parent) || Node52.isElementAccessExpression(parent) || Node52.isCallExpression(parent)) && parent.getExpression() === node;
|
|
11773
|
+
}
|
|
11681
11774
|
var isDefaultConditionList = (conditions) => conditions.length === 0 || conditions.every(
|
|
11682
11775
|
(c) => c.source === "earlyReturn" || c.source === "earlyThrow"
|
|
11683
11776
|
);
|
|
@@ -11816,11 +11909,8 @@ function extractRawBranches(func, terminalPatterns, invocationRecognizers = [],
|
|
|
11816
11909
|
});
|
|
11817
11910
|
if (invocations.length > 0) {
|
|
11818
11911
|
const terminalNodes = new Set(terminals.map(({ node }) => node));
|
|
11819
|
-
const terminalLines = new Set(
|
|
11820
|
-
distinctBranches.map((b) => b.terminal.location.start)
|
|
11821
|
-
);
|
|
11822
11912
|
const sideEffects = invocations.filter(
|
|
11823
|
-
(i) =>
|
|
11913
|
+
(i) => !writesTerminal(i.node, terminalNodes)
|
|
11824
11914
|
);
|
|
11825
11915
|
for (const branch of distinctBranches) {
|
|
11826
11916
|
branch.effects = sideEffects.filter(
|
|
@@ -11866,9 +11956,9 @@ function createSourceFileLookup(project) {
|
|
|
11866
11956
|
if (sf === null) {
|
|
11867
11957
|
return null;
|
|
11868
11958
|
}
|
|
11869
|
-
const
|
|
11870
|
-
const index = functionsByFile.get(
|
|
11871
|
-
functionsByFile.set(
|
|
11959
|
+
const path14 = sf.getFilePath();
|
|
11960
|
+
const index = functionsByFile.get(path14) ?? indexFunctions(sf);
|
|
11961
|
+
functionsByFile.set(path14, index);
|
|
11872
11962
|
if (location.span !== void 0) {
|
|
11873
11963
|
const bySpan = index.get(spanKey(location.span));
|
|
11874
11964
|
if (bySpan !== void 0) {
|
|
@@ -11913,397 +12003,47 @@ function indexFunctions(sf) {
|
|
|
11913
12003
|
return index;
|
|
11914
12004
|
}
|
|
11915
12005
|
|
|
11916
|
-
// src/
|
|
11917
|
-
import {
|
|
11918
|
-
import
|
|
11919
|
-
|
|
11920
|
-
|
|
11921
|
-
|
|
11922
|
-
var ENTRY_PREFIX = "key-";
|
|
11923
|
-
var ENTRY_DIR_NAME = new RegExp(`^${ENTRY_PREFIX}[0-9a-f]{16}$`);
|
|
11924
|
-
function createCacheLayer(cacheDir) {
|
|
11925
|
-
if (cacheDir === null) {
|
|
11926
|
-
return {
|
|
11927
|
-
tryHit: async () => null,
|
|
11928
|
-
lookup: async () => ({
|
|
11929
|
-
kind: "miss",
|
|
11930
|
-
diagnostic: { kind: "miss", missReason: "no-manifest" }
|
|
11931
|
-
}),
|
|
11932
|
-
plan: async () => null,
|
|
11933
|
-
write: async () => {
|
|
11934
|
-
}
|
|
11935
|
-
};
|
|
11936
|
-
}
|
|
11937
|
-
return {
|
|
11938
|
-
async tryHit(input) {
|
|
11939
|
-
const result = await this.lookup(input);
|
|
11940
|
-
return result.kind === "hit" ? result.summaries : null;
|
|
11941
|
-
},
|
|
11942
|
-
async lookup(input) {
|
|
11943
|
-
const entryDir = entryDirFor(cacheDir, input);
|
|
11944
|
-
const manifest = await readManifest(path7.join(entryDir, "manifest.json"));
|
|
11945
|
-
if (manifest === null) {
|
|
11946
|
-
return missDiag(await describeAbsentEntry(cacheDir));
|
|
11947
|
-
}
|
|
11948
|
-
const currentTsconfigStamp = await stampTsconfig(input.tsconfigPath);
|
|
11949
|
-
if (!fileStampEquals(manifest.tsconfigStamp, currentTsconfigStamp)) {
|
|
11950
|
-
return missDiag("tsconfig-changed");
|
|
11951
|
-
}
|
|
11952
|
-
const currentFiles = await resolveFileStamps(input);
|
|
11953
|
-
if (!fileStampsEqual(manifest.files, currentFiles)) {
|
|
11954
|
-
return missDiag("files-changed");
|
|
11955
|
-
}
|
|
11956
|
-
await markUsed(entryDir);
|
|
11957
|
-
return {
|
|
11958
|
-
kind: "hit",
|
|
11959
|
-
summaries: manifest.summaries,
|
|
11960
|
-
diagnostic: { kind: "hit" }
|
|
11961
|
-
};
|
|
11962
|
-
},
|
|
11963
|
-
async plan(input) {
|
|
11964
|
-
const entryDir = entryDirFor(cacheDir, input);
|
|
11965
|
-
const manifest = await readManifest(path7.join(entryDir, "manifest.json"));
|
|
11966
|
-
if (manifest === null || manifest.roots === void 0 || manifest.rootMeta === void 0 || manifest.owners === void 0) {
|
|
11967
|
-
return null;
|
|
11968
|
-
}
|
|
11969
|
-
const currentTsconfigStamp = await stampTsconfig(input.tsconfigPath);
|
|
11970
|
-
if (!fileStampEquals(manifest.tsconfigStamp, currentTsconfigStamp)) {
|
|
11971
|
-
return null;
|
|
11972
|
-
}
|
|
11973
|
-
await markUsed(entryDir);
|
|
11974
|
-
return buildPlan(manifest, await resolveFileStamps(input));
|
|
11975
|
-
},
|
|
11976
|
-
async write(input, summaries, attribution) {
|
|
11977
|
-
const entryDir = entryDirFor(cacheDir, input);
|
|
11978
|
-
const previous = await readManifest(path7.join(entryDir, "manifest.json"));
|
|
11979
|
-
const tsconfigStamp = await stampTsconfig(input.tsconfigPath);
|
|
11980
|
-
const files = await hashStamps(await resolveFileStamps(input), previous);
|
|
11981
|
-
const manifest = {
|
|
11982
|
-
schemaVersion: SCHEMA_VERSION,
|
|
11983
|
-
adapterPacksDigest: input.adapterPacksDigest,
|
|
11984
|
-
tsconfigStamp,
|
|
11985
|
-
files,
|
|
11986
|
-
summaries,
|
|
11987
|
-
...attribution === void 0 ? {} : encodeAttribution(attribution)
|
|
11988
|
-
};
|
|
11989
|
-
await fs5.mkdir(entryDir, { recursive: true });
|
|
11990
|
-
await fs5.writeFile(
|
|
11991
|
-
path7.join(entryDir, "manifest.json"),
|
|
11992
|
-
JSON.stringify(manifest)
|
|
11993
|
-
);
|
|
11994
|
-
await evictOldEntries(cacheDir, entryDir);
|
|
11995
|
-
}
|
|
11996
|
-
};
|
|
11997
|
-
}
|
|
11998
|
-
async function buildPlan(manifest, currentStamps) {
|
|
11999
|
-
const stored = new Map(manifest.files.map((f) => [f.path, f]));
|
|
12000
|
-
const current2 = new Map(currentStamps.map((f) => [f.path, f]));
|
|
12001
|
-
const changed = /* @__PURE__ */ new Set();
|
|
12002
|
-
const removed = /* @__PURE__ */ new Set();
|
|
12003
|
-
for (const p of stored.keys()) {
|
|
12004
|
-
if (!current2.has(p)) {
|
|
12005
|
-
removed.add(p);
|
|
12006
|
-
}
|
|
12007
|
-
}
|
|
12008
|
-
const toVerify = [];
|
|
12009
|
-
for (const [p, stamp] of current2) {
|
|
12010
|
-
const before = stored.get(p);
|
|
12011
|
-
if (before === void 0) {
|
|
12012
|
-
changed.add(p);
|
|
12013
|
-
} else if (!fileStampEquals(before, stamp)) {
|
|
12014
|
-
if (before.contentHash === void 0) {
|
|
12015
|
-
changed.add(p);
|
|
12016
|
-
} else {
|
|
12017
|
-
toVerify.push(p);
|
|
12018
|
-
}
|
|
12019
|
-
}
|
|
12006
|
+
// src/contract.ts
|
|
12007
|
+
import { Node as Node54 } from "ts-morph";
|
|
12008
|
+
import { restBinding } from "@suss/behavioral-ir";
|
|
12009
|
+
function extractDeclaredBody(node) {
|
|
12010
|
+
if (node === void 0) {
|
|
12011
|
+
return null;
|
|
12020
12012
|
}
|
|
12021
|
-
|
|
12022
|
-
|
|
12023
|
-
const hash = await hashFile(p);
|
|
12024
|
-
if (hash === null || hash !== stored.get(p)?.contentHash) {
|
|
12025
|
-
changed.add(p);
|
|
12026
|
-
}
|
|
12027
|
-
})
|
|
12028
|
-
);
|
|
12029
|
-
const roots = /* @__PURE__ */ new Map();
|
|
12030
|
-
const rootNames = manifest.roots ?? [];
|
|
12031
|
-
const depPaths = manifest.depPaths ?? [];
|
|
12032
|
-
let rootsDeclined = 0;
|
|
12033
|
-
rootNames.forEach((rootPath, i) => {
|
|
12034
|
-
const meta = manifest.rootMeta?.[i];
|
|
12035
|
-
if (meta === void 0) {
|
|
12036
|
-
return;
|
|
12037
|
-
}
|
|
12038
|
-
if (!meta.cacheable) {
|
|
12039
|
-
rootsDeclined += 1;
|
|
12040
|
-
}
|
|
12041
|
-
roots.set(rootPath, {
|
|
12042
|
-
path: rootPath,
|
|
12043
|
-
cacheable: meta.cacheable,
|
|
12044
|
-
deps: meta.deps.flatMap((d) => {
|
|
12045
|
-
const p = depPaths[d];
|
|
12046
|
-
return p === void 0 ? [] : [p];
|
|
12047
|
-
}),
|
|
12048
|
-
claims: meta.claims,
|
|
12049
|
-
mountPrefixes: meta.mountPrefixes,
|
|
12050
|
-
packs: meta.packs
|
|
12051
|
-
});
|
|
12052
|
-
});
|
|
12053
|
-
const validRoots = /* @__PURE__ */ new Set();
|
|
12054
|
-
for (const [rootPath, record2] of roots) {
|
|
12055
|
-
if (!record2.cacheable || !current2.has(rootPath)) {
|
|
12056
|
-
continue;
|
|
12057
|
-
}
|
|
12058
|
-
if (changed.has(rootPath)) {
|
|
12059
|
-
continue;
|
|
12060
|
-
}
|
|
12061
|
-
const depMoved = record2.deps.some((d) => changed.has(d) || removed.has(d));
|
|
12062
|
-
if (!depMoved) {
|
|
12063
|
-
validRoots.add(rootPath);
|
|
12064
|
-
}
|
|
12013
|
+
if (!Node54.isCallExpression(node)) {
|
|
12014
|
+
return null;
|
|
12065
12015
|
}
|
|
12066
|
-
const
|
|
12067
|
-
|
|
12068
|
-
changed,
|
|
12069
|
-
removed,
|
|
12070
|
-
roots,
|
|
12071
|
-
validRoots,
|
|
12072
|
-
rootsDeclined,
|
|
12073
|
-
reuse(valid) {
|
|
12074
|
-
const summaries = [];
|
|
12075
|
-
const reusedOwners = [];
|
|
12076
|
-
manifest.summaries.forEach((summary, i) => {
|
|
12077
|
-
const ownerPaths = (owners[i] ?? []).flatMap((o) => {
|
|
12078
|
-
const p = rootNames[o];
|
|
12079
|
-
return p === void 0 ? [] : [p];
|
|
12080
|
-
});
|
|
12081
|
-
if (ownerPaths.some((p) => valid.has(p))) {
|
|
12082
|
-
summaries.push(summary);
|
|
12083
|
-
reusedOwners.push(ownerPaths.filter((p) => valid.has(p)));
|
|
12084
|
-
}
|
|
12085
|
-
});
|
|
12086
|
-
return { summaries, owners: reusedOwners };
|
|
12087
|
-
},
|
|
12088
|
-
allSummaries() {
|
|
12089
|
-
return manifest.summaries;
|
|
12090
|
-
},
|
|
12091
|
-
attribution() {
|
|
12092
|
-
return {
|
|
12093
|
-
roots: [...roots.values()],
|
|
12094
|
-
owners: owners.map(
|
|
12095
|
-
(ownerIds) => ownerIds.flatMap((o) => {
|
|
12096
|
-
const p = rootNames[o];
|
|
12097
|
-
return p === void 0 ? [] : [p];
|
|
12098
|
-
})
|
|
12099
|
-
)
|
|
12100
|
-
};
|
|
12101
|
-
}
|
|
12102
|
-
};
|
|
12103
|
-
}
|
|
12104
|
-
function encodeAttribution(attribution) {
|
|
12105
|
-
const roots = attribution.roots.map((r) => r.path);
|
|
12106
|
-
const rootIndex = new Map(roots.map((p, i) => [p, i]));
|
|
12107
|
-
const depIndex = /* @__PURE__ */ new Map();
|
|
12108
|
-
const depPaths = [];
|
|
12109
|
-
const depIdOf = (p) => {
|
|
12110
|
-
const existing = depIndex.get(p);
|
|
12111
|
-
if (existing !== void 0) {
|
|
12112
|
-
return existing;
|
|
12113
|
-
}
|
|
12114
|
-
const id = depPaths.length;
|
|
12115
|
-
depPaths.push(p);
|
|
12116
|
-
depIndex.set(p, id);
|
|
12117
|
-
return id;
|
|
12118
|
-
};
|
|
12119
|
-
const rootMeta = attribution.roots.map((r) => ({
|
|
12120
|
-
cacheable: r.cacheable,
|
|
12121
|
-
deps: r.deps.map(depIdOf),
|
|
12122
|
-
claims: r.claims,
|
|
12123
|
-
mountPrefixes: r.mountPrefixes,
|
|
12124
|
-
packs: r.packs
|
|
12125
|
-
}));
|
|
12126
|
-
const owners = attribution.owners.map(
|
|
12127
|
-
(ownerPaths) => ownerPaths.flatMap((p) => {
|
|
12128
|
-
const i = rootIndex.get(p);
|
|
12129
|
-
return i === void 0 ? [] : [i];
|
|
12130
|
-
})
|
|
12131
|
-
);
|
|
12132
|
-
return { roots, rootMeta, depPaths, owners };
|
|
12133
|
-
}
|
|
12134
|
-
async function hashStamps(stamps, previous) {
|
|
12135
|
-
const before = new Map((previous?.files ?? []).map((f) => [f.path, f]));
|
|
12136
|
-
return Promise.all(
|
|
12137
|
-
stamps.map(async (stamp) => {
|
|
12138
|
-
const prior = before.get(stamp.path);
|
|
12139
|
-
if (prior !== void 0 && prior.contentHash !== void 0 && fileStampEquals(prior, stamp)) {
|
|
12140
|
-
return { ...stamp, contentHash: prior.contentHash };
|
|
12141
|
-
}
|
|
12142
|
-
const contentHash = await hashFile(stamp.path);
|
|
12143
|
-
return contentHash === null ? stamp : { ...stamp, contentHash };
|
|
12144
|
-
})
|
|
12145
|
-
);
|
|
12146
|
-
}
|
|
12147
|
-
async function hashFile(filePath) {
|
|
12148
|
-
try {
|
|
12149
|
-
const content = await fs5.readFile(filePath);
|
|
12150
|
-
return createHash2("sha256").update(content).digest("hex").slice(0, 16);
|
|
12151
|
-
} catch {
|
|
12016
|
+
const typeArgs = node.getTypeArguments();
|
|
12017
|
+
if (typeArgs.length === 0) {
|
|
12152
12018
|
return null;
|
|
12153
12019
|
}
|
|
12020
|
+
return shapeFromNodeType(typeArgs[0]);
|
|
12154
12021
|
}
|
|
12155
|
-
|
|
12156
|
-
|
|
12157
|
-
|
|
12158
|
-
return
|
|
12159
|
-
} catch {
|
|
12160
|
-
return "no-manifest";
|
|
12022
|
+
function resolveHandlerPropNode(func) {
|
|
12023
|
+
const parent = func.getParent();
|
|
12024
|
+
if (parent === void 0) {
|
|
12025
|
+
return null;
|
|
12161
12026
|
}
|
|
12162
|
-
|
|
12163
|
-
|
|
12164
|
-
const key = [
|
|
12165
|
-
SCHEMA_VERSION,
|
|
12166
|
-
input.adapterPacksDigest,
|
|
12167
|
-
input.tsconfigPath ?? ""
|
|
12168
|
-
].join(" ");
|
|
12169
|
-
const name = createHash2("sha256").update(key).digest("hex").slice(0, 16);
|
|
12170
|
-
return path7.join(cacheDir, `${ENTRY_PREFIX}${name}`);
|
|
12171
|
-
}
|
|
12172
|
-
function isEntryDir(name) {
|
|
12173
|
-
return ENTRY_DIR_NAME.test(name);
|
|
12174
|
-
}
|
|
12175
|
-
async function markUsed(entryDir) {
|
|
12176
|
-
const now = /* @__PURE__ */ new Date();
|
|
12177
|
-
try {
|
|
12178
|
-
await fs5.utimes(entryDir, now, now);
|
|
12179
|
-
} catch {
|
|
12027
|
+
if (Node54.isPropertyAssignment(parent)) {
|
|
12028
|
+
return parent;
|
|
12180
12029
|
}
|
|
12181
|
-
|
|
12182
|
-
|
|
12183
|
-
try {
|
|
12184
|
-
await fs5.rm(path7.join(cacheDir, "manifest.json"), { force: true });
|
|
12185
|
-
const entries = await fs5.readdir(cacheDir, { withFileTypes: true });
|
|
12186
|
-
const dirs = await Promise.all(
|
|
12187
|
-
entries.filter((e) => e.isDirectory() && isEntryDir(e.name)).map(async (e) => {
|
|
12188
|
-
const dir = path7.join(cacheDir, e.name);
|
|
12189
|
-
const stat = await fs5.stat(dir);
|
|
12190
|
-
return { dir, mtimeMs: stat.mtimeMs };
|
|
12191
|
-
})
|
|
12192
|
-
);
|
|
12193
|
-
const doomed = dirs.filter((d) => d.dir !== keepDir).sort((a, b) => b.mtimeMs - a.mtimeMs).slice(MAX_ENTRIES - 1);
|
|
12194
|
-
await Promise.all(
|
|
12195
|
-
doomed.map((d) => fs5.rm(d.dir, { recursive: true, force: true }))
|
|
12196
|
-
);
|
|
12197
|
-
} catch {
|
|
12030
|
+
if (Node54.isMethodDeclaration(func)) {
|
|
12031
|
+
return func;
|
|
12198
12032
|
}
|
|
12033
|
+
return null;
|
|
12199
12034
|
}
|
|
12200
|
-
function
|
|
12201
|
-
|
|
12202
|
-
|
|
12203
|
-
|
|
12204
|
-
};
|
|
12205
|
-
}
|
|
12206
|
-
async function readManifest(manifestPath) {
|
|
12207
|
-
try {
|
|
12208
|
-
const raw = await fs5.readFile(manifestPath, "utf-8");
|
|
12209
|
-
return JSON.parse(raw);
|
|
12210
|
-
} catch {
|
|
12035
|
+
function findRouterCall(unit) {
|
|
12036
|
+
const func = unit.func;
|
|
12037
|
+
const propNode = func === null ? null : resolveHandlerPropNode(func);
|
|
12038
|
+
if (propNode === null) {
|
|
12211
12039
|
return null;
|
|
12212
12040
|
}
|
|
12213
|
-
|
|
12214
|
-
|
|
12215
|
-
if (tsconfigPath === void 0) {
|
|
12041
|
+
const handlersObj = propNode.getParent();
|
|
12042
|
+
if (handlersObj === void 0 || !Node54.isObjectLiteralExpression(handlersObj)) {
|
|
12216
12043
|
return null;
|
|
12217
12044
|
}
|
|
12218
|
-
|
|
12219
|
-
|
|
12220
|
-
return { path: tsconfigPath, mtimeMs: stat.mtimeMs, size: stat.size };
|
|
12221
|
-
} catch {
|
|
12222
|
-
return null;
|
|
12223
|
-
}
|
|
12224
|
-
}
|
|
12225
|
-
async function resolveFileStamps(input) {
|
|
12226
|
-
if (input.files !== void 0) {
|
|
12227
|
-
return stampPaths(input.files);
|
|
12228
|
-
}
|
|
12229
|
-
if (input.project !== void 0) {
|
|
12230
|
-
return stampProjectFiles(input.project);
|
|
12231
|
-
}
|
|
12232
|
-
return [];
|
|
12233
|
-
}
|
|
12234
|
-
async function stampProjectFiles(project) {
|
|
12235
|
-
const files = project.getSourceFiles().filter((sf) => !sf.isDeclarationFile()).map((sf) => sf.getFilePath());
|
|
12236
|
-
return stampPaths(files);
|
|
12237
|
-
}
|
|
12238
|
-
async function stampPaths(paths) {
|
|
12239
|
-
const files = paths;
|
|
12240
|
-
const stamped = await Promise.all(
|
|
12241
|
-
files.map(async (p) => {
|
|
12242
|
-
try {
|
|
12243
|
-
const stat = await fs5.stat(p);
|
|
12244
|
-
return { path: p, mtimeMs: stat.mtimeMs, size: stat.size };
|
|
12245
|
-
} catch {
|
|
12246
|
-
return { path: p, mtimeMs: -1, size: -1 };
|
|
12247
|
-
}
|
|
12248
|
-
})
|
|
12249
|
-
);
|
|
12250
|
-
stamped.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
12251
|
-
return stamped;
|
|
12252
|
-
}
|
|
12253
|
-
function fileStampsEqual(a, b) {
|
|
12254
|
-
return a.length === b.length && a.every((s, i) => fileStampEquals(s, b[i]));
|
|
12255
|
-
}
|
|
12256
|
-
function fileStampEquals(a, b) {
|
|
12257
|
-
if (a === null && b === null) {
|
|
12258
|
-
return true;
|
|
12259
|
-
}
|
|
12260
|
-
if (a === null || b === null) {
|
|
12261
|
-
return false;
|
|
12262
|
-
}
|
|
12263
|
-
return a.path === b.path && a.mtimeMs === b.mtimeMs && a.size === b.size;
|
|
12264
|
-
}
|
|
12265
|
-
|
|
12266
|
-
// src/contract.ts
|
|
12267
|
-
import { Node as Node54 } from "ts-morph";
|
|
12268
|
-
import { restBinding } from "@suss/behavioral-ir";
|
|
12269
|
-
function extractDeclaredBody(node) {
|
|
12270
|
-
if (node === void 0) {
|
|
12271
|
-
return null;
|
|
12272
|
-
}
|
|
12273
|
-
if (!Node54.isCallExpression(node)) {
|
|
12274
|
-
return null;
|
|
12275
|
-
}
|
|
12276
|
-
const typeArgs = node.getTypeArguments();
|
|
12277
|
-
if (typeArgs.length === 0) {
|
|
12278
|
-
return null;
|
|
12279
|
-
}
|
|
12280
|
-
return shapeFromNodeType(typeArgs[0]);
|
|
12281
|
-
}
|
|
12282
|
-
function resolveHandlerPropNode(func) {
|
|
12283
|
-
const parent = func.getParent();
|
|
12284
|
-
if (parent === void 0) {
|
|
12285
|
-
return null;
|
|
12286
|
-
}
|
|
12287
|
-
if (Node54.isPropertyAssignment(parent)) {
|
|
12288
|
-
return parent;
|
|
12289
|
-
}
|
|
12290
|
-
if (Node54.isMethodDeclaration(func)) {
|
|
12291
|
-
return func;
|
|
12292
|
-
}
|
|
12293
|
-
return null;
|
|
12294
|
-
}
|
|
12295
|
-
function findRouterCall(unit) {
|
|
12296
|
-
const func = unit.func;
|
|
12297
|
-
const propNode = func === null ? null : resolveHandlerPropNode(func);
|
|
12298
|
-
if (propNode === null) {
|
|
12299
|
-
return null;
|
|
12300
|
-
}
|
|
12301
|
-
const handlersObj = propNode.getParent();
|
|
12302
|
-
if (handlersObj === void 0 || !Node54.isObjectLiteralExpression(handlersObj)) {
|
|
12303
|
-
return null;
|
|
12304
|
-
}
|
|
12305
|
-
const routerCall = handlersObj.getParent();
|
|
12306
|
-
if (routerCall === void 0 || !Node54.isCallExpression(routerCall)) {
|
|
12045
|
+
const routerCall = handlersObj.getParent();
|
|
12046
|
+
if (routerCall === void 0 || !Node54.isCallExpression(routerCall)) {
|
|
12307
12047
|
return null;
|
|
12308
12048
|
}
|
|
12309
12049
|
const args = routerCall.getArguments();
|
|
@@ -12405,7 +12145,7 @@ function extractEndpointContract(endpointNode, pattern, framework) {
|
|
|
12405
12145
|
}
|
|
12406
12146
|
const responses = [];
|
|
12407
12147
|
let method;
|
|
12408
|
-
let
|
|
12148
|
+
let path14;
|
|
12409
12149
|
for (const prop of endpointNode.getProperties()) {
|
|
12410
12150
|
if (!Node54.isPropertyAssignment(prop)) {
|
|
12411
12151
|
continue;
|
|
@@ -12420,7 +12160,7 @@ function extractEndpointContract(endpointNode, pattern, framework) {
|
|
|
12420
12160
|
if (propName === pattern.pathProperty) {
|
|
12421
12161
|
const val = prop.getInitializer();
|
|
12422
12162
|
if (val !== void 0 && Node54.isStringLiteral(val)) {
|
|
12423
|
-
|
|
12163
|
+
path14 = val.getLiteralValue();
|
|
12424
12164
|
}
|
|
12425
12165
|
}
|
|
12426
12166
|
if (propName === pattern.responseExtraction.property) {
|
|
@@ -12444,10 +12184,10 @@ function extractEndpointContract(endpointNode, pattern, framework) {
|
|
|
12444
12184
|
if (responses.length === 0) {
|
|
12445
12185
|
return null;
|
|
12446
12186
|
}
|
|
12447
|
-
const boundaryBinding = method !== void 0 ||
|
|
12187
|
+
const boundaryBinding = method !== void 0 || path14 !== void 0 ? restBinding({
|
|
12448
12188
|
transport: "http",
|
|
12449
12189
|
method: method ?? null,
|
|
12450
|
-
path:
|
|
12190
|
+
path: path14 ?? null,
|
|
12451
12191
|
recognition: framework
|
|
12452
12192
|
}) : null;
|
|
12453
12193
|
return {
|
|
@@ -13086,8 +12826,8 @@ function noteExpanded(expanded, readings, discovery) {
|
|
|
13086
12826
|
const paths = new Set(match.registrations.map((one) => one.pathTemplate));
|
|
13087
12827
|
const methods = expanded.get(reading.declarationId) ?? /* @__PURE__ */ new Set();
|
|
13088
12828
|
for (const sink of reading.helper.sinks) {
|
|
13089
|
-
const
|
|
13090
|
-
if (sink.method !== null &&
|
|
12829
|
+
const path14 = sink.arguments[0];
|
|
12830
|
+
if (sink.method !== null && path14?.as === "text" && paths.has(path14.text)) {
|
|
13091
12831
|
methods.add(sink.method);
|
|
13092
12832
|
}
|
|
13093
12833
|
}
|
|
@@ -13681,7 +13421,7 @@ function scalarOrRef(node) {
|
|
|
13681
13421
|
}
|
|
13682
13422
|
|
|
13683
13423
|
// src/moduleInit.ts
|
|
13684
|
-
import { assembleSummary } from "@suss/extractor";
|
|
13424
|
+
import { assembleSummary, moduleInitStructure } from "@suss/extractor";
|
|
13685
13425
|
function moduleInitName(sourceFile) {
|
|
13686
13426
|
return sourceFile.getBaseName();
|
|
13687
13427
|
}
|
|
@@ -13689,44 +13429,15 @@ function moduleInitSummary(sourceFile, effects, options) {
|
|
|
13689
13429
|
if (effects.length === 0) {
|
|
13690
13430
|
return null;
|
|
13691
13431
|
}
|
|
13692
|
-
|
|
13693
|
-
|
|
13694
|
-
identity: {
|
|
13432
|
+
return assembleSummary(
|
|
13433
|
+
moduleInitStructure({
|
|
13695
13434
|
name: moduleInitName(sourceFile),
|
|
13696
|
-
kind: "module-init",
|
|
13697
13435
|
file: sourceFile.getFilePath(),
|
|
13698
|
-
range: { start: 0, end },
|
|
13699
|
-
|
|
13700
|
-
|
|
13701
|
-
|
|
13702
|
-
|
|
13703
|
-
parameters: [],
|
|
13704
|
-
branches: [
|
|
13705
|
-
{
|
|
13706
|
-
conditions: [],
|
|
13707
|
-
terminal: {
|
|
13708
|
-
kind: "void",
|
|
13709
|
-
statusCode: null,
|
|
13710
|
-
body: null,
|
|
13711
|
-
exceptionType: null,
|
|
13712
|
-
message: null,
|
|
13713
|
-
component: null,
|
|
13714
|
-
renderTree: null,
|
|
13715
|
-
delegateTarget: null,
|
|
13716
|
-
emitEvent: null,
|
|
13717
|
-
location: { start: 0, end }
|
|
13718
|
-
},
|
|
13719
|
-
effects: [],
|
|
13720
|
-
extraEffects: effects,
|
|
13721
|
-
location: { start: 0, end },
|
|
13722
|
-
isDefault: true
|
|
13723
|
-
}
|
|
13724
|
-
],
|
|
13725
|
-
unmatchedReturns: 0,
|
|
13726
|
-
dependencyCalls: [],
|
|
13727
|
-
declaredContract: null
|
|
13728
|
-
};
|
|
13729
|
-
return assembleSummary(raw, options);
|
|
13436
|
+
range: { start: 0, end: sourceFile.getEnd() },
|
|
13437
|
+
effects
|
|
13438
|
+
}),
|
|
13439
|
+
options
|
|
13440
|
+
);
|
|
13730
13441
|
}
|
|
13731
13442
|
|
|
13732
13443
|
// src/referencedFiles.ts
|
|
@@ -13874,7 +13585,7 @@ function deriveBoundaryEffects(summaries, facts) {
|
|
|
13874
13585
|
|
|
13875
13586
|
// src/resolve/reachableClosure.ts
|
|
13876
13587
|
import { Node as Node60 } from "ts-morph";
|
|
13877
|
-
import { functionCallBinding } from "@suss/behavioral-ir";
|
|
13588
|
+
import { functionCallBinding, TargetPlacements } from "@suss/behavioral-ir";
|
|
13878
13589
|
import { Database as Database3, evaluate as evaluate4, lit as lit4, rule as rule4, variable as variable2 } from "@suss/datalog";
|
|
13879
13590
|
import { assembleSummary as assembleSummary2 } from "@suss/extractor";
|
|
13880
13591
|
var reachablePack = {
|
|
@@ -13898,6 +13609,12 @@ var reachablePack = {
|
|
|
13898
13609
|
function nodeKey(func) {
|
|
13899
13610
|
return offsetKeyOf(func);
|
|
13900
13611
|
}
|
|
13612
|
+
function targetOf(node) {
|
|
13613
|
+
return {
|
|
13614
|
+
file: node.getSourceFile().getFilePath(),
|
|
13615
|
+
span: { start: node.getStart(), end: node.getEnd() }
|
|
13616
|
+
};
|
|
13617
|
+
}
|
|
13901
13618
|
function resolveCallee(call, calleeName, scan) {
|
|
13902
13619
|
if (!Node60.isCallExpression(call)) {
|
|
13903
13620
|
return null;
|
|
@@ -13922,19 +13639,42 @@ function resolveCallee(call, calleeName, scan) {
|
|
|
13922
13639
|
if (sources.length > 1) {
|
|
13923
13640
|
return {
|
|
13924
13641
|
kind: "stopped",
|
|
13925
|
-
stop: { callee: calleeName, reason: "multipleSources" }
|
|
13642
|
+
stop: { callee: calleeName, reason: "multipleSources" },
|
|
13643
|
+
declaration: declarationToReport(declarations, calleeName, scan)
|
|
13926
13644
|
};
|
|
13927
13645
|
}
|
|
13928
13646
|
}
|
|
13647
|
+
const reason = classifyStop(declarations, scan.scanning);
|
|
13648
|
+
const parameterIndex = reason === "callerSupplied" ? parameterIndexOf(declarations, scan.scanning) : void 0;
|
|
13929
13649
|
return {
|
|
13930
13650
|
kind: "stopped",
|
|
13931
|
-
stop: {
|
|
13932
|
-
|
|
13933
|
-
|
|
13934
|
-
}
|
|
13651
|
+
stop: { callee: calleeName, reason },
|
|
13652
|
+
declaration: declarationToReport(declarations, calleeName, scan),
|
|
13653
|
+
...parameterIndex === void 0 ? {} : { parameterIndex }
|
|
13935
13654
|
};
|
|
13936
13655
|
}
|
|
13937
|
-
function
|
|
13656
|
+
function declarationToReport(declarations, calleeName, scan) {
|
|
13657
|
+
const first = declarations[0];
|
|
13658
|
+
if (first === void 0) {
|
|
13659
|
+
return null;
|
|
13660
|
+
}
|
|
13661
|
+
const sources = scan.sourceDeclarationsBehind?.(first) ?? [first];
|
|
13662
|
+
const only = sources.length === 1 ? sources[0] : void 0;
|
|
13663
|
+
if (only === void 0 || only === first) {
|
|
13664
|
+
return first;
|
|
13665
|
+
}
|
|
13666
|
+
return resolveDecl(only, calleeName)?.func ?? only;
|
|
13667
|
+
}
|
|
13668
|
+
function declaredAtOf(outcome) {
|
|
13669
|
+
if (outcome.kind === "followed") {
|
|
13670
|
+
return targetOf(outcome.candidate.func);
|
|
13671
|
+
}
|
|
13672
|
+
return outcome.declaration === null ? null : targetOf(outcome.declaration);
|
|
13673
|
+
}
|
|
13674
|
+
function passedPositionKey(target, position) {
|
|
13675
|
+
return `${offsetKeyFor(target.file, target.span)}#${position}`;
|
|
13676
|
+
}
|
|
13677
|
+
function resolveJsxReference(node, scan) {
|
|
13938
13678
|
if (!Node60.isJsxOpeningElement(node) && !Node60.isJsxSelfClosingElement(node)) {
|
|
13939
13679
|
return null;
|
|
13940
13680
|
}
|
|
@@ -13954,7 +13694,8 @@ function resolveJsxReference(node) {
|
|
|
13954
13694
|
tag: name,
|
|
13955
13695
|
outcome: {
|
|
13956
13696
|
kind: "stopped",
|
|
13957
|
-
stop: { callee: name, reason: classifyStop(declarations) }
|
|
13697
|
+
stop: { callee: name, reason: classifyStop(declarations) },
|
|
13698
|
+
declaration: declarationToReport(declarations, name, scan)
|
|
13958
13699
|
}
|
|
13959
13700
|
};
|
|
13960
13701
|
}
|
|
@@ -13962,7 +13703,11 @@ function collectReachable(func, scan) {
|
|
|
13962
13703
|
const inFunc = { ...scan, scanning: func };
|
|
13963
13704
|
const candidates = [];
|
|
13964
13705
|
const stops = [];
|
|
13706
|
+
const placements = new TargetPlacements();
|
|
13707
|
+
const parameterCalls = [];
|
|
13708
|
+
const passedPositions = /* @__PURE__ */ new Set();
|
|
13965
13709
|
const seen = /* @__PURE__ */ new Set();
|
|
13710
|
+
const parameterCallsSeen = /* @__PURE__ */ new Set();
|
|
13966
13711
|
const record2 = (outcome) => {
|
|
13967
13712
|
if (outcome.kind === "stopped") {
|
|
13968
13713
|
const stopKey = `${outcome.stop.reason}:${outcome.stop.callee}`;
|
|
@@ -13979,8 +13724,28 @@ function collectReachable(func, scan) {
|
|
|
13979
13724
|
seen.add(key);
|
|
13980
13725
|
candidates.push(outcome.candidate);
|
|
13981
13726
|
};
|
|
13727
|
+
const recordPassedArgs = (call, calleeText, calleeOutcome) => {
|
|
13728
|
+
if (!Node60.isCallExpression(call)) {
|
|
13729
|
+
return;
|
|
13730
|
+
}
|
|
13731
|
+
const calleeTarget = declaredAtOf(calleeOutcome);
|
|
13732
|
+
call.getArguments().forEach((arg, position) => {
|
|
13733
|
+
if (!Node60.isIdentifier(arg)) {
|
|
13734
|
+
return;
|
|
13735
|
+
}
|
|
13736
|
+
const resolved = functionTargetOf(arg);
|
|
13737
|
+
if (resolved === null) {
|
|
13738
|
+
return;
|
|
13739
|
+
}
|
|
13740
|
+
record2({ kind: "followed", candidate: resolved });
|
|
13741
|
+
placements.placeArg(calleeText, position, targetOf(resolved.func));
|
|
13742
|
+
if (calleeTarget !== null) {
|
|
13743
|
+
passedPositions.add(passedPositionKey(calleeTarget, position));
|
|
13744
|
+
}
|
|
13745
|
+
});
|
|
13746
|
+
};
|
|
13982
13747
|
func.forEachDescendant((node) => {
|
|
13983
|
-
const jsx = resolveJsxReference(node);
|
|
13748
|
+
const jsx = resolveJsxReference(node, inFunc);
|
|
13984
13749
|
if (jsx !== null) {
|
|
13985
13750
|
record2(jsx.outcome);
|
|
13986
13751
|
return;
|
|
@@ -13994,8 +13759,24 @@ function collectReachable(func, scan) {
|
|
|
13994
13759
|
return;
|
|
13995
13760
|
}
|
|
13996
13761
|
record2(outcome);
|
|
13762
|
+
placements.place(calleeText, declaredAtOf(outcome));
|
|
13763
|
+
recordPassedArgs(node, calleeText, outcome);
|
|
13764
|
+
if (outcome.kind === "stopped" && outcome.stop.reason === "callerSupplied" && outcome.parameterIndex !== void 0 && !parameterCallsSeen.has(calleeText)) {
|
|
13765
|
+
parameterCallsSeen.add(calleeText);
|
|
13766
|
+
parameterCalls.push({
|
|
13767
|
+
callee: calleeText,
|
|
13768
|
+
parameterIndex: outcome.parameterIndex
|
|
13769
|
+
});
|
|
13770
|
+
}
|
|
13997
13771
|
});
|
|
13998
|
-
return {
|
|
13772
|
+
return {
|
|
13773
|
+
candidates,
|
|
13774
|
+
stops,
|
|
13775
|
+
targets: placements.targets,
|
|
13776
|
+
argTargets: placements.argTargets,
|
|
13777
|
+
parameterCalls,
|
|
13778
|
+
passedPositions
|
|
13779
|
+
};
|
|
13999
13780
|
}
|
|
14000
13781
|
function normalizeCallee(text) {
|
|
14001
13782
|
return text.replace(/\s+/g, " ").trim();
|
|
@@ -14070,6 +13851,10 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
|
|
|
14070
13851
|
const seedKeys = /* @__PURE__ */ new Set();
|
|
14071
13852
|
const scanned = /* @__PURE__ */ new Set();
|
|
14072
13853
|
const stopsByKey = /* @__PURE__ */ new Map();
|
|
13854
|
+
const targetsByKey = /* @__PURE__ */ new Map();
|
|
13855
|
+
const argTargetsByKey = /* @__PURE__ */ new Map();
|
|
13856
|
+
const parameterCallsByKey = /* @__PURE__ */ new Map();
|
|
13857
|
+
const passedPositions = /* @__PURE__ */ new Set();
|
|
14073
13858
|
const summariesByKey = /* @__PURE__ */ new Map();
|
|
14074
13859
|
const reachedFrom = /* @__PURE__ */ new Map();
|
|
14075
13860
|
for (const seed of seeds) {
|
|
@@ -14114,9 +13899,17 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
|
|
|
14114
13899
|
const cameFrom = reachedFrom.get(key);
|
|
14115
13900
|
const scan = {
|
|
14116
13901
|
...recognizers.resolveCallableSources === void 0 ? {} : { resolveCallableSources: recognizers.resolveCallableSources },
|
|
13902
|
+
...recognizers.sourceDeclarationsBehind === void 0 ? {} : { sourceDeclarationsBehind: recognizers.sourceDeclarationsBehind },
|
|
14117
13903
|
...cameFrom === void 0 ? {} : { reachedFrom: cameFrom }
|
|
14118
13904
|
};
|
|
14119
|
-
const {
|
|
13905
|
+
const {
|
|
13906
|
+
candidates,
|
|
13907
|
+
stops,
|
|
13908
|
+
targets,
|
|
13909
|
+
argTargets,
|
|
13910
|
+
parameterCalls,
|
|
13911
|
+
passedPositions: scanPassedPositions
|
|
13912
|
+
} = scanWithRecording(
|
|
14120
13913
|
key,
|
|
14121
13914
|
facts,
|
|
14122
13915
|
() => collectReachable(source.func, scan)
|
|
@@ -14124,6 +13917,14 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
|
|
|
14124
13917
|
if (stops.length > 0) {
|
|
14125
13918
|
stopsByKey.set(key, stops);
|
|
14126
13919
|
}
|
|
13920
|
+
targetsByKey.set(key, targets);
|
|
13921
|
+
argTargetsByKey.set(key, argTargets);
|
|
13922
|
+
if (parameterCalls.length > 0) {
|
|
13923
|
+
parameterCallsByKey.set(key, parameterCalls);
|
|
13924
|
+
}
|
|
13925
|
+
for (const position of scanPassedPositions) {
|
|
13926
|
+
passedPositions.add(position);
|
|
13927
|
+
}
|
|
14127
13928
|
for (const candidate of candidates) {
|
|
14128
13929
|
const calleeKey = nodeKey(candidate.func);
|
|
14129
13930
|
if (!functionByKey.has(calleeKey)) {
|
|
@@ -14163,6 +13964,15 @@ function expandReachableClosure(seeds, project, options, projectFileSet, facts,
|
|
|
14163
13964
|
reached.push(summary);
|
|
14164
13965
|
}
|
|
14165
13966
|
recordStops(stopsByKey, summariesByKey, options);
|
|
13967
|
+
recordTargets(targetsByKey, summariesByKey);
|
|
13968
|
+
recordArgTargets(argTargetsByKey, summariesByKey);
|
|
13969
|
+
recordCalleeParameters(parameterCallsByKey, summariesByKey);
|
|
13970
|
+
recordParameterGaps(
|
|
13971
|
+
parameterCallsByKey,
|
|
13972
|
+
summariesByKey,
|
|
13973
|
+
passedPositions,
|
|
13974
|
+
options
|
|
13975
|
+
);
|
|
14166
13976
|
return [...seeds, ...reached];
|
|
14167
13977
|
}
|
|
14168
13978
|
function scanWithRecording(key, facts, fn) {
|
|
@@ -14194,6 +14004,90 @@ function recordStops(stopsByKey, summariesByKey, options) {
|
|
|
14194
14004
|
}
|
|
14195
14005
|
}
|
|
14196
14006
|
}
|
|
14007
|
+
function recordTargets(targetsByKey, summariesByKey) {
|
|
14008
|
+
for (const [key, targets] of targetsByKey) {
|
|
14009
|
+
for (const summary of summariesByKey.get(key) ?? []) {
|
|
14010
|
+
for (const transition of summary.transitions) {
|
|
14011
|
+
for (const effect of transition.effects) {
|
|
14012
|
+
if (effect.type !== "invocation") {
|
|
14013
|
+
continue;
|
|
14014
|
+
}
|
|
14015
|
+
const target = targets.get(normalizeCallee(effect.callee));
|
|
14016
|
+
if (target !== void 0) {
|
|
14017
|
+
effect.declaredAt = target;
|
|
14018
|
+
}
|
|
14019
|
+
}
|
|
14020
|
+
}
|
|
14021
|
+
}
|
|
14022
|
+
}
|
|
14023
|
+
}
|
|
14024
|
+
function recordArgTargets(argTargetsByKey, summariesByKey) {
|
|
14025
|
+
for (const [key, argTargets] of argTargetsByKey) {
|
|
14026
|
+
for (const summary of summariesByKey.get(key) ?? []) {
|
|
14027
|
+
for (const transition of summary.transitions) {
|
|
14028
|
+
for (const effect of transition.effects) {
|
|
14029
|
+
if (effect.type !== "invocation") {
|
|
14030
|
+
continue;
|
|
14031
|
+
}
|
|
14032
|
+
const byPosition = argTargets.get(normalizeCallee(effect.callee));
|
|
14033
|
+
if (byPosition === void 0) {
|
|
14034
|
+
continue;
|
|
14035
|
+
}
|
|
14036
|
+
const argsDeclaredAt = {};
|
|
14037
|
+
for (const [position, target] of byPosition) {
|
|
14038
|
+
argsDeclaredAt[position] = target;
|
|
14039
|
+
}
|
|
14040
|
+
if (Object.keys(argsDeclaredAt).length > 0) {
|
|
14041
|
+
effect.argsDeclaredAt = argsDeclaredAt;
|
|
14042
|
+
}
|
|
14043
|
+
}
|
|
14044
|
+
}
|
|
14045
|
+
}
|
|
14046
|
+
}
|
|
14047
|
+
}
|
|
14048
|
+
function recordCalleeParameters(parameterCallsByKey, summariesByKey) {
|
|
14049
|
+
for (const [key, parameterCalls] of parameterCallsByKey) {
|
|
14050
|
+
const byCallee = new Map(
|
|
14051
|
+
parameterCalls.map(({ callee, parameterIndex }) => [
|
|
14052
|
+
callee,
|
|
14053
|
+
parameterIndex
|
|
14054
|
+
])
|
|
14055
|
+
);
|
|
14056
|
+
for (const summary of summariesByKey.get(key) ?? []) {
|
|
14057
|
+
for (const transition of summary.transitions) {
|
|
14058
|
+
for (const effect of transition.effects) {
|
|
14059
|
+
if (effect.type !== "invocation") {
|
|
14060
|
+
continue;
|
|
14061
|
+
}
|
|
14062
|
+
const parameterIndex = byCallee.get(normalizeCallee(effect.callee));
|
|
14063
|
+
if (parameterIndex !== void 0) {
|
|
14064
|
+
effect.calleeParameter = parameterIndex;
|
|
14065
|
+
}
|
|
14066
|
+
}
|
|
14067
|
+
}
|
|
14068
|
+
}
|
|
14069
|
+
}
|
|
14070
|
+
}
|
|
14071
|
+
function recordParameterGaps(parameterCallsByKey, summariesByKey, passedPositions, options) {
|
|
14072
|
+
if (options?.gapHandling === "silent") {
|
|
14073
|
+
return;
|
|
14074
|
+
}
|
|
14075
|
+
for (const [key, parameterCalls] of parameterCallsByKey) {
|
|
14076
|
+
const unbound = parameterCalls.filter(
|
|
14077
|
+
({ parameterIndex }) => !passedPositions.has(`${key}#${parameterIndex}`)
|
|
14078
|
+
);
|
|
14079
|
+
if (unbound.length === 0) {
|
|
14080
|
+
continue;
|
|
14081
|
+
}
|
|
14082
|
+
for (const summary of summariesByKey.get(key) ?? []) {
|
|
14083
|
+
summary.gaps.push(
|
|
14084
|
+
...unbound.map(
|
|
14085
|
+
({ callee }) => unfollowedCallGap({ callee, reason: "unboundParameter" })
|
|
14086
|
+
)
|
|
14087
|
+
);
|
|
14088
|
+
}
|
|
14089
|
+
}
|
|
14090
|
+
}
|
|
14197
14091
|
|
|
14198
14092
|
// src/resolve/rethrowEnrichment.ts
|
|
14199
14093
|
import { Node as Node61 } from "ts-morph";
|
|
@@ -14929,10 +14823,10 @@ function readArrayLiteralText(node) {
|
|
|
14929
14823
|
}
|
|
14930
14824
|
|
|
14931
14825
|
// src/summaryIdentity.ts
|
|
14932
|
-
import fs6 from "fs";
|
|
14933
14826
|
import path8 from "path";
|
|
14934
14827
|
import {
|
|
14935
14828
|
disambiguateSummaryIds,
|
|
14829
|
+
linkCallsToSummaries,
|
|
14936
14830
|
summaryIdFromParts
|
|
14937
14831
|
} from "@suss/behavioral-ir";
|
|
14938
14832
|
function workspaceRootFor(anchor) {
|
|
@@ -14949,32 +14843,6 @@ function workspaceNameFor(root) {
|
|
|
14949
14843
|
const folder = path8.basename(path8.resolve(root));
|
|
14950
14844
|
return folder.length > 0 ? folder : null;
|
|
14951
14845
|
}
|
|
14952
|
-
function namedPackageDirAbove(start) {
|
|
14953
|
-
let at = path8.resolve(start);
|
|
14954
|
-
for (let up = 0; up < 12; up += 1) {
|
|
14955
|
-
if (packageNameAt(at) !== null) {
|
|
14956
|
-
return at;
|
|
14957
|
-
}
|
|
14958
|
-
const parent = path8.dirname(at);
|
|
14959
|
-
if (parent === at) {
|
|
14960
|
-
break;
|
|
14961
|
-
}
|
|
14962
|
-
at = parent;
|
|
14963
|
-
}
|
|
14964
|
-
return null;
|
|
14965
|
-
}
|
|
14966
|
-
function packageNameAt(dir) {
|
|
14967
|
-
try {
|
|
14968
|
-
const manifest = JSON.parse(
|
|
14969
|
-
fs6.readFileSync(path8.join(dir, "package.json"), "utf8")
|
|
14970
|
-
);
|
|
14971
|
-
if (typeof manifest.name === "string" && manifest.name.length > 0) {
|
|
14972
|
-
return manifest.name;
|
|
14973
|
-
}
|
|
14974
|
-
} catch {
|
|
14975
|
-
}
|
|
14976
|
-
return null;
|
|
14977
|
-
}
|
|
14978
14846
|
function nameSummaries(summaries, args) {
|
|
14979
14847
|
for (const summary of summaries) {
|
|
14980
14848
|
const file = relativeFile(summary.location.file, args.projectRoot);
|
|
@@ -14984,7 +14852,7 @@ function nameSummaries(summaries, args) {
|
|
|
14984
14852
|
summary.identity.id = idFor(args.workspace, file, summary);
|
|
14985
14853
|
}
|
|
14986
14854
|
disambiguateSummaryIds(summaries);
|
|
14987
|
-
|
|
14855
|
+
linkCallsToSummaries(summaries);
|
|
14988
14856
|
}
|
|
14989
14857
|
function relativeFile(file, projectRoot) {
|
|
14990
14858
|
if (projectRoot === void 0 || !path8.isAbsolute(file)) {
|
|
@@ -15001,185 +14869,35 @@ function idFor(workspace, file, summary) {
|
|
|
15001
14869
|
exportPath: summary.identity.exportPath
|
|
15002
14870
|
});
|
|
15003
14871
|
}
|
|
15004
|
-
function nameWhatEachCallReaches(summaries) {
|
|
15005
|
-
const byName = /* @__PURE__ */ new Map();
|
|
15006
|
-
const byFileAndName = /* @__PURE__ */ new Map();
|
|
15007
|
-
const remember = (index, key, summary) => {
|
|
15008
|
-
const found = index.get(key);
|
|
15009
|
-
if (found === void 0) {
|
|
15010
|
-
index.set(key, [summary]);
|
|
15011
|
-
return;
|
|
15012
|
-
}
|
|
15013
|
-
found.push(summary);
|
|
15014
|
-
};
|
|
15015
|
-
for (const summary of summaries) {
|
|
15016
|
-
if (summary.identity.nameKind === "label") {
|
|
15017
|
-
continue;
|
|
15018
|
-
}
|
|
15019
|
-
remember(byName, summary.identity.name, summary);
|
|
15020
|
-
remember(
|
|
15021
|
-
byFileAndName,
|
|
15022
|
-
`${summary.location.file}::${summary.identity.name}`,
|
|
15023
|
-
summary
|
|
15024
|
-
);
|
|
15025
|
-
}
|
|
15026
|
-
const onlyAnswer = (index, key) => {
|
|
15027
|
-
const found = index.get(key) ?? [];
|
|
15028
|
-
return found.length === 1 ? found[0] : null;
|
|
15029
|
-
};
|
|
15030
|
-
for (const summary of summaries) {
|
|
15031
|
-
for (const transition of summary.transitions) {
|
|
15032
|
-
for (const effect of transition.effects) {
|
|
15033
|
-
if (effect.type !== "invocation") {
|
|
15034
|
-
continue;
|
|
15035
|
-
}
|
|
15036
|
-
const called = effect.callee.split(".").pop() ?? effect.callee;
|
|
15037
|
-
const reached = onlyAnswer(byFileAndName, `${summary.location.file}::${called}`) ?? onlyAnswer(byName, called);
|
|
15038
|
-
if (reached?.identity.id !== void 0) {
|
|
15039
|
-
effect.summary = reached.identity.id;
|
|
15040
|
-
}
|
|
15041
|
-
}
|
|
15042
|
-
}
|
|
15043
|
-
}
|
|
15044
|
-
}
|
|
15045
14872
|
|
|
15046
14873
|
// src/timing.ts
|
|
15047
|
-
|
|
15048
|
-
const phases = /* @__PURE__ */ new Map();
|
|
15049
|
-
const start = performance.now();
|
|
15050
|
-
function record2(label, deltaMs) {
|
|
15051
|
-
const existing = phases.get(label);
|
|
15052
|
-
if (existing === void 0) {
|
|
15053
|
-
phases.set(label, { durationMs: deltaMs, calls: 1 });
|
|
15054
|
-
} else {
|
|
15055
|
-
existing.durationMs += deltaMs;
|
|
15056
|
-
existing.calls += 1;
|
|
15057
|
-
}
|
|
15058
|
-
}
|
|
15059
|
-
return {
|
|
15060
|
-
time(label, fn) {
|
|
15061
|
-
const t0 = performance.now();
|
|
15062
|
-
try {
|
|
15063
|
-
return fn();
|
|
15064
|
-
} finally {
|
|
15065
|
-
record2(label, performance.now() - t0);
|
|
15066
|
-
}
|
|
15067
|
-
},
|
|
15068
|
-
async timeAsync(label, fn) {
|
|
15069
|
-
const t0 = performance.now();
|
|
15070
|
-
try {
|
|
15071
|
-
return await fn();
|
|
15072
|
-
} finally {
|
|
15073
|
-
record2(label, performance.now() - t0);
|
|
15074
|
-
}
|
|
15075
|
-
},
|
|
15076
|
-
report() {
|
|
15077
|
-
const totalMs = performance.now() - start;
|
|
15078
|
-
const ordered = [...phases.entries()].map(([label, s]) => ({
|
|
15079
|
-
label,
|
|
15080
|
-
durationMs: s.durationMs,
|
|
15081
|
-
calls: s.calls
|
|
15082
|
-
})).sort((a, b) => b.durationMs - a.durationMs);
|
|
15083
|
-
return { totalMs, phases: ordered };
|
|
15084
|
-
}
|
|
15085
|
-
};
|
|
15086
|
-
}
|
|
14874
|
+
import { createTimer, noopTimer } from "@suss/extractor";
|
|
15087
14875
|
|
|
15088
14876
|
// src/version.ts
|
|
15089
|
-
import {
|
|
15090
|
-
import
|
|
15091
|
-
|
|
15092
|
-
|
|
15093
|
-
|
|
14877
|
+
import { createAdapterStamp } from "@suss/extractor";
|
|
14878
|
+
import {
|
|
14879
|
+
computeContentHash,
|
|
14880
|
+
computeDistHashFrom,
|
|
14881
|
+
projectFileStamp
|
|
14882
|
+
} from "@suss/extractor";
|
|
15094
14883
|
var ADAPTER_VERSION = "0.2.1";
|
|
15095
|
-
var
|
|
15096
|
-
|
|
15097
|
-
|
|
15098
|
-
|
|
15099
|
-
"@suss/behavioral-ir"
|
|
15100
|
-
];
|
|
15101
|
-
var SOURCE_STAMP = { kind: "source" };
|
|
15102
|
-
var cachedCodeStamp = null;
|
|
15103
|
-
function adapterCodeStamp() {
|
|
15104
|
-
if (cachedCodeStamp !== null) {
|
|
15105
|
-
return cachedCodeStamp;
|
|
15106
|
-
}
|
|
15107
|
-
cachedCodeStamp = readAdapterCodeStamp();
|
|
15108
|
-
return cachedCodeStamp;
|
|
15109
|
-
}
|
|
15110
|
-
function readAdapterCodeStamp() {
|
|
15111
|
-
try {
|
|
15112
|
-
const selfPath = fileURLToPath(import.meta.url);
|
|
15113
|
-
const hash = computeDistHashFrom(path9.dirname(selfPath));
|
|
15114
|
-
return hash.length > 0 ? { kind: "bundle", hash } : SOURCE_STAMP;
|
|
15115
|
-
} catch {
|
|
15116
|
-
return SOURCE_STAMP;
|
|
15117
|
-
}
|
|
15118
|
-
}
|
|
15119
|
-
function computeDistHashFrom(dir) {
|
|
15120
|
-
const candidates = [path9.join(dir, "index.js"), path9.join(dir, "index.cjs")];
|
|
15121
|
-
for (const candidate of candidates) {
|
|
15122
|
-
if (!fs7.existsSync(candidate)) {
|
|
15123
|
-
continue;
|
|
15124
|
-
}
|
|
15125
|
-
return computeContentHash([candidate, ...analysisBundles()]);
|
|
15126
|
-
}
|
|
15127
|
-
return "";
|
|
15128
|
-
}
|
|
15129
|
-
function computeContentHash(paths) {
|
|
15130
|
-
if (paths.length === 0) {
|
|
15131
|
-
return "";
|
|
15132
|
-
}
|
|
15133
|
-
const hash = createHash3("sha256");
|
|
15134
|
-
for (const file of paths) {
|
|
15135
|
-
try {
|
|
15136
|
-
hash.update(fs7.readFileSync(file));
|
|
15137
|
-
} catch {
|
|
15138
|
-
return "";
|
|
15139
|
-
}
|
|
15140
|
-
}
|
|
15141
|
-
return hash.digest("hex").slice(0, 16);
|
|
15142
|
-
}
|
|
15143
|
-
function projectFileStamp(paths) {
|
|
15144
|
-
if (paths.length === 0) {
|
|
15145
|
-
return "none";
|
|
15146
|
-
}
|
|
15147
|
-
const hash = createHash3("sha256");
|
|
15148
|
-
for (const file of [...paths].sort()) {
|
|
15149
|
-
hash.update(file);
|
|
15150
|
-
hash.update("\0");
|
|
15151
|
-
try {
|
|
15152
|
-
hash.update(fs7.readFileSync(file));
|
|
15153
|
-
} catch {
|
|
15154
|
-
hash.update("absent");
|
|
15155
|
-
}
|
|
15156
|
-
}
|
|
15157
|
-
return hash.digest("hex").slice(0, 16);
|
|
15158
|
-
}
|
|
15159
|
-
function analysisBundles() {
|
|
15160
|
-
const require2 = createRequire(import.meta.url);
|
|
15161
|
-
const found = [];
|
|
15162
|
-
for (const name of ANALYSIS_PACKAGES) {
|
|
15163
|
-
try {
|
|
15164
|
-
found.push(require2.resolve(name));
|
|
15165
|
-
} catch {
|
|
15166
|
-
}
|
|
15167
|
-
}
|
|
15168
|
-
return found;
|
|
15169
|
-
}
|
|
14884
|
+
var stamp = createAdapterStamp({
|
|
14885
|
+
moduleUrl: import.meta.url,
|
|
14886
|
+
version: ADAPTER_VERSION
|
|
14887
|
+
});
|
|
15170
14888
|
function computeAdapterPacksDigest(packVersions) {
|
|
15171
|
-
|
|
15172
|
-
|
|
15173
|
-
|
|
15174
|
-
return
|
|
14889
|
+
return stamp.packsDigest(packVersions);
|
|
14890
|
+
}
|
|
14891
|
+
function declineWhenRunFromSource(cacheDir) {
|
|
14892
|
+
return stamp.declineWhenRunFromSource(cacheDir);
|
|
15175
14893
|
}
|
|
15176
14894
|
|
|
15177
14895
|
// src/workspacePatterns.ts
|
|
15178
|
-
import
|
|
15179
|
-
import
|
|
14896
|
+
import fs4 from "fs";
|
|
14897
|
+
import path9 from "path";
|
|
15180
14898
|
function readJson(filePath) {
|
|
15181
14899
|
try {
|
|
15182
|
-
return JSON.parse(
|
|
14900
|
+
return JSON.parse(fs4.readFileSync(filePath, "utf8"));
|
|
15183
14901
|
} catch {
|
|
15184
14902
|
return void 0;
|
|
15185
14903
|
}
|
|
@@ -15206,7 +14924,7 @@ function pnpmWorkspaceGlobs(yamlText) {
|
|
|
15206
14924
|
return globs;
|
|
15207
14925
|
}
|
|
15208
14926
|
function workspaceGlobsAt(dir) {
|
|
15209
|
-
const pkg = readJson(
|
|
14927
|
+
const pkg = readJson(path9.join(dir, "package.json"));
|
|
15210
14928
|
if (typeof pkg === "object" && pkg !== null && "workspaces" in pkg) {
|
|
15211
14929
|
const ws = pkg.workspaces;
|
|
15212
14930
|
if (Array.isArray(ws)) {
|
|
@@ -15218,10 +14936,10 @@ function workspaceGlobsAt(dir) {
|
|
|
15218
14936
|
);
|
|
15219
14937
|
}
|
|
15220
14938
|
}
|
|
15221
|
-
const pnpmPath =
|
|
15222
|
-
if (
|
|
14939
|
+
const pnpmPath = path9.join(dir, "pnpm-workspace.yaml");
|
|
14940
|
+
if (fs4.existsSync(pnpmPath)) {
|
|
15223
14941
|
try {
|
|
15224
|
-
return pnpmWorkspaceGlobs(
|
|
14942
|
+
return pnpmWorkspaceGlobs(fs4.readFileSync(pnpmPath, "utf8"));
|
|
15225
14943
|
} catch {
|
|
15226
14944
|
return null;
|
|
15227
14945
|
}
|
|
@@ -15230,14 +14948,14 @@ function workspaceGlobsAt(dir) {
|
|
|
15230
14948
|
}
|
|
15231
14949
|
function isDirectory(candidate) {
|
|
15232
14950
|
try {
|
|
15233
|
-
return
|
|
14951
|
+
return fs4.statSync(candidate).isDirectory();
|
|
15234
14952
|
} catch {
|
|
15235
14953
|
return false;
|
|
15236
14954
|
}
|
|
15237
14955
|
}
|
|
15238
14956
|
function subdirectoriesOf(dir) {
|
|
15239
14957
|
try {
|
|
15240
|
-
return
|
|
14958
|
+
return fs4.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).filter((name) => !name.startsWith(".") && name !== "node_modules");
|
|
15241
14959
|
} catch {
|
|
15242
14960
|
return [];
|
|
15243
14961
|
}
|
|
@@ -15255,13 +14973,13 @@ function directoriesMatchingGlob(root, glob) {
|
|
|
15255
14973
|
for (const dir of current2) {
|
|
15256
14974
|
for (const entry of subdirectoriesOf(dir)) {
|
|
15257
14975
|
if (pattern.test(entry)) {
|
|
15258
|
-
next.push(
|
|
14976
|
+
next.push(path9.join(dir, entry));
|
|
15259
14977
|
}
|
|
15260
14978
|
}
|
|
15261
14979
|
}
|
|
15262
14980
|
} else {
|
|
15263
14981
|
for (const dir of current2) {
|
|
15264
|
-
next.push(
|
|
14982
|
+
next.push(path9.join(dir, segment));
|
|
15265
14983
|
}
|
|
15266
14984
|
}
|
|
15267
14985
|
current2 = next.filter(isDirectory);
|
|
@@ -15269,7 +14987,7 @@ function directoriesMatchingGlob(root, glob) {
|
|
|
15269
14987
|
return current2;
|
|
15270
14988
|
}
|
|
15271
14989
|
function packageAt(dir) {
|
|
15272
|
-
const packageJsonPath =
|
|
14990
|
+
const packageJsonPath = path9.join(dir, "package.json");
|
|
15273
14991
|
const pkg = readJson(packageJsonPath);
|
|
15274
14992
|
if (typeof pkg === "object" && pkg !== null && typeof pkg.name === "string") {
|
|
15275
14993
|
return { name: pkg.name, packageJsonPath };
|
|
@@ -15277,7 +14995,7 @@ function packageAt(dir) {
|
|
|
15277
14995
|
return null;
|
|
15278
14996
|
}
|
|
15279
14997
|
function workspacePackagesFrom(anchor) {
|
|
15280
|
-
let dir =
|
|
14998
|
+
let dir = path9.resolve(anchor);
|
|
15281
14999
|
for (; ; ) {
|
|
15282
15000
|
const globs = workspaceGlobsAt(dir);
|
|
15283
15001
|
if (globs !== null) {
|
|
@@ -15297,7 +15015,7 @@ function workspacePackagesFrom(anchor) {
|
|
|
15297
15015
|
(a, b) => a.packageJsonPath.localeCompare(b.packageJsonPath)
|
|
15298
15016
|
);
|
|
15299
15017
|
}
|
|
15300
|
-
const parent =
|
|
15018
|
+
const parent = path9.dirname(dir);
|
|
15301
15019
|
if (parent === dir) {
|
|
15302
15020
|
return [];
|
|
15303
15021
|
}
|
|
@@ -15839,11 +15557,11 @@ function extractConsumerBinding(unit, pattern, pack, resolution) {
|
|
|
15839
15557
|
return null;
|
|
15840
15558
|
}
|
|
15841
15559
|
const method = extractBindingMethod(binding, callSite, pack, resolution);
|
|
15842
|
-
const
|
|
15560
|
+
const path14 = extractBindingPath(binding, callSite, pack, resolution);
|
|
15843
15561
|
return restBinding2({
|
|
15844
15562
|
transport: pack.protocol,
|
|
15845
15563
|
method: method ?? null,
|
|
15846
|
-
path:
|
|
15564
|
+
path: path14 ?? null,
|
|
15847
15565
|
recognition: pack.name
|
|
15848
15566
|
});
|
|
15849
15567
|
}
|
|
@@ -16319,24 +16037,6 @@ function stampWrappers(raw, unit, wrappers) {
|
|
|
16319
16037
|
recordFileDependency(wrapper.file);
|
|
16320
16038
|
}
|
|
16321
16039
|
}
|
|
16322
|
-
function stampModuleImports(summaries, project) {
|
|
16323
|
-
const byFile2 = /* @__PURE__ */ new Map();
|
|
16324
|
-
for (const summary of summaries) {
|
|
16325
|
-
const file = summary.location.file;
|
|
16326
|
-
let imports = byFile2.get(file);
|
|
16327
|
-
if (imports === void 0) {
|
|
16328
|
-
imports = internalImportsOf(project, file);
|
|
16329
|
-
byFile2.set(file, imports);
|
|
16330
|
-
}
|
|
16331
|
-
if (imports.length === 0) {
|
|
16332
|
-
continue;
|
|
16333
|
-
}
|
|
16334
|
-
summary.metadata = {
|
|
16335
|
-
...summary.metadata ?? {},
|
|
16336
|
-
moduleImports: imports
|
|
16337
|
-
};
|
|
16338
|
-
}
|
|
16339
|
-
}
|
|
16340
16040
|
function emitLibraryEnvReadMarkers(summaries, project, frameworks) {
|
|
16341
16041
|
for (const pack of frameworks) {
|
|
16342
16042
|
for (const declared of pack.libraryEnvVars ?? []) {
|
|
@@ -16511,8 +16211,8 @@ function synthesizeCallerSummaries(wrapper, _project, options, resolution) {
|
|
|
16511
16211
|
if (pathArg === void 0) {
|
|
16512
16212
|
continue;
|
|
16513
16213
|
}
|
|
16514
|
-
const
|
|
16515
|
-
if (
|
|
16214
|
+
const path14 = pathFromArgument(pathArg, resolution);
|
|
16215
|
+
if (path14 === void 0) {
|
|
16516
16216
|
continue;
|
|
16517
16217
|
}
|
|
16518
16218
|
const callerFunc = enclosingFunction(callExpr);
|
|
@@ -16524,7 +16224,7 @@ function synthesizeCallerSummaries(wrapper, _project, options, resolution) {
|
|
|
16524
16224
|
continue;
|
|
16525
16225
|
}
|
|
16526
16226
|
seen.add(dedupKey);
|
|
16527
|
-
out.push(buildCallerSummary(wrapper, callerFunc, callExpr,
|
|
16227
|
+
out.push(buildCallerSummary(wrapper, callerFunc, callExpr, path14, options));
|
|
16528
16228
|
}
|
|
16529
16229
|
return out;
|
|
16530
16230
|
}
|
|
@@ -16581,7 +16281,7 @@ function callerName(func) {
|
|
|
16581
16281
|
}
|
|
16582
16282
|
return "anonymous";
|
|
16583
16283
|
}
|
|
16584
|
-
function buildCallerSummary(wrapper, callerFunc, callExpr,
|
|
16284
|
+
function buildCallerSummary(wrapper, callerFunc, callExpr, path14, options) {
|
|
16585
16285
|
const wrapperBinding = wrapper.summary.identity.boundaryBinding;
|
|
16586
16286
|
const wrapperRest = wrapperBinding?.semantics.name === "rest" ? wrapperBinding.semantics : null;
|
|
16587
16287
|
const syntheticPack = {
|
|
@@ -16613,7 +16313,7 @@ function buildCallerSummary(wrapper, callerFunc, callExpr, path15, options) {
|
|
|
16613
16313
|
raw.boundaryBinding = restBinding2({
|
|
16614
16314
|
transport: wrapperBinding?.transport ?? "http",
|
|
16615
16315
|
method: wrapperRest?.method ?? null,
|
|
16616
|
-
path:
|
|
16316
|
+
path: path14,
|
|
16617
16317
|
recognition: wrapperBinding?.recognition ?? "unknown"
|
|
16618
16318
|
});
|
|
16619
16319
|
const summary = assembleSummary3(raw, options);
|
|
@@ -16635,25 +16335,12 @@ function named(summaries, workspace, runRoot) {
|
|
|
16635
16335
|
});
|
|
16636
16336
|
return summaries;
|
|
16637
16337
|
}
|
|
16638
|
-
var saidWhyNoCache = false;
|
|
16639
16338
|
function extractionConfigStamp(config) {
|
|
16640
16339
|
return [
|
|
16641
16340
|
`includeReachable=${config.includeReachable !== false}`,
|
|
16642
16341
|
`gapHandling=${config.extractorOptions?.gapHandling ?? "default"}`
|
|
16643
16342
|
].join(",");
|
|
16644
16343
|
}
|
|
16645
|
-
function declineWhenRunFromSource(cacheDir) {
|
|
16646
|
-
if (cacheDir === null || adapterCodeStamp().kind === "bundle") {
|
|
16647
|
-
return cacheDir;
|
|
16648
|
-
}
|
|
16649
|
-
if (!saidWhyNoCache) {
|
|
16650
|
-
saidWhyNoCache = true;
|
|
16651
|
-
process.stderr.write(
|
|
16652
|
-
"[suss] extraction cache off: this process loaded the adapter from source, where nothing can tell one build of it from another. Run the built adapter to cache.\n"
|
|
16653
|
-
);
|
|
16654
|
-
}
|
|
16655
|
-
return null;
|
|
16656
|
-
}
|
|
16657
16344
|
function loadRunFiles(project, candidatePaths, timer) {
|
|
16658
16345
|
if (candidatePaths !== null) {
|
|
16659
16346
|
const deep2 = timer.time(
|
|
@@ -16684,7 +16371,7 @@ function createTypeScriptAdapter(suppliedConfig) {
|
|
|
16684
16371
|
...suppliedConfig,
|
|
16685
16372
|
frameworks: expandWorkspacePatterns(
|
|
16686
16373
|
suppliedConfig.frameworks,
|
|
16687
|
-
suppliedConfig.projectRoot ?? (suppliedConfig.tsConfigFilePath !== void 0 ?
|
|
16374
|
+
suppliedConfig.projectRoot ?? (suppliedConfig.tsConfigFilePath !== void 0 ? path10.dirname(suppliedConfig.tsConfigFilePath) : void 0)
|
|
16688
16375
|
)
|
|
16689
16376
|
};
|
|
16690
16377
|
const project = config.project ?? new Project(
|
|
@@ -16693,11 +16380,11 @@ function createTypeScriptAdapter(suppliedConfig) {
|
|
|
16693
16380
|
skipAddingFilesFromTsConfig: true
|
|
16694
16381
|
} : { skipAddingFilesFromTsConfig: true }
|
|
16695
16382
|
);
|
|
16696
|
-
const runRoot = config.projectRoot ?? (config.tsConfigFilePath !== void 0 ? workspaceRootFor(
|
|
16383
|
+
const runRoot = config.projectRoot ?? (config.tsConfigFilePath !== void 0 ? workspaceRootFor(path10.dirname(config.tsConfigFilePath)) : void 0);
|
|
16697
16384
|
let lazyBootstrapped = false;
|
|
16698
16385
|
let projectFileSet;
|
|
16699
16386
|
const cacheDir = declineWhenRunFromSource(
|
|
16700
|
-
config.cacheDir === null ? null : config.cacheDir ?? (config.tsConfigFilePath !== void 0 ?
|
|
16387
|
+
config.cacheDir === null ? null : config.cacheDir ?? (config.tsConfigFilePath !== void 0 ? path10.join(path10.dirname(config.tsConfigFilePath), ".suss", "cache") : null)
|
|
16701
16388
|
);
|
|
16702
16389
|
const cache = createCacheLayer(cacheDir);
|
|
16703
16390
|
const packsDigest = `${computeAdapterPacksDigest(
|
|
@@ -16705,12 +16392,6 @@ function createTypeScriptAdapter(suppliedConfig) {
|
|
|
16705
16392
|
(p) => p.version !== void 0 ? { name: p.name, version: p.version } : { name: p.name }
|
|
16706
16393
|
)
|
|
16707
16394
|
)}|${extractionConfigStamp(config)}|ws:${workspaceExpansionStamp(config.frameworks)}`;
|
|
16708
|
-
const digestFor = (files) => {
|
|
16709
|
-
const inputs = config.frameworks.flatMap(
|
|
16710
|
-
(pack) => pack.discoveryInputs?.(files) ?? []
|
|
16711
|
-
);
|
|
16712
|
-
return `${packsDigest}|reads:${projectFileStamp(inputs)}`;
|
|
16713
|
-
};
|
|
16714
16395
|
const packWrappers = config.frameworks.flatMap(
|
|
16715
16396
|
(pack) => pack.transparentWrappers ?? []
|
|
16716
16397
|
);
|
|
@@ -16754,20 +16435,17 @@ function createTypeScriptAdapter(suppliedConfig) {
|
|
|
16754
16435
|
config.tsConfigFilePath ?? raise("lazy bootstrap requires tsConfigFilePath")
|
|
16755
16436
|
)
|
|
16756
16437
|
) : null;
|
|
16757
|
-
|
|
16758
|
-
|
|
16759
|
-
() =>
|
|
16760
|
-
|
|
16761
|
-
|
|
16762
|
-
|
|
16763
|
-
|
|
16764
|
-
|
|
16765
|
-
|
|
16766
|
-
tsconfigPath: config.tsConfigFilePath ?? raise("lazy bootstrap requires tsConfigFilePath")
|
|
16767
|
-
} : {
|
|
16768
|
-
project,
|
|
16438
|
+
let cacheFiles = null;
|
|
16439
|
+
const adapterPacksDigest = cacheDir === null ? packsDigest : timer.time("cache.digest", () => {
|
|
16440
|
+
cacheFiles = tsconfigFileList ?? project.getSourceFiles().map((sf) => sf.getFilePath());
|
|
16441
|
+
return runDigest(packsDigest, config.frameworks, cacheFiles);
|
|
16442
|
+
});
|
|
16443
|
+
const cacheInput = {
|
|
16444
|
+
// Empty when cacheDir is null: a no-op cache layer never reads
|
|
16445
|
+
// the file list, so there is nothing worth resolving early.
|
|
16446
|
+
files: cacheFiles ?? [],
|
|
16769
16447
|
adapterPacksDigest,
|
|
16770
|
-
...config.tsConfigFilePath !== void 0 ? {
|
|
16448
|
+
...config.tsConfigFilePath !== void 0 ? { configPath: config.tsConfigFilePath } : {}
|
|
16771
16449
|
};
|
|
16772
16450
|
const lookup = await timer.timeAsync(
|
|
16773
16451
|
"cache.lookup",
|
|
@@ -16973,7 +16651,8 @@ function createTypeScriptAdapter(suppliedConfig) {
|
|
|
16973
16651
|
],
|
|
16974
16652
|
access: collectAccessRecognizers(config.frameworks, tallies),
|
|
16975
16653
|
resolveWrittenValue: (value) => resolution.resolveWrittenValue(value),
|
|
16976
|
-
resolveCallableSources: (value, alsoFrom) => resolution.resolveCallableSources(value, alsoFrom)
|
|
16654
|
+
resolveCallableSources: (value, alsoFrom) => resolution.resolveCallableSources(value, alsoFrom),
|
|
16655
|
+
sourceDeclarationsBehind: (declaration) => sourceDeclarationsBehind(declaration, resolution)
|
|
16977
16656
|
},
|
|
16978
16657
|
// Reached units the cache already serves emit nothing,
|
|
16979
16658
|
// the way a cold run's seeds do not.
|
|
@@ -17010,7 +16689,10 @@ function createTypeScriptAdapter(suppliedConfig) {
|
|
|
17010
16689
|
}
|
|
17011
16690
|
timer.time(
|
|
17012
16691
|
"stampModuleImports",
|
|
17013
|
-
() => stampModuleImports(enriched,
|
|
16692
|
+
() => stampModuleImports(enriched, (file) => {
|
|
16693
|
+
const imports = internalImportsOf(project, file);
|
|
16694
|
+
return imports.length === 0 ? void 0 : imports;
|
|
16695
|
+
})
|
|
17014
16696
|
);
|
|
17015
16697
|
timer.time(
|
|
17016
16698
|
"emitLibraryEnvReadMarkers",
|
|
@@ -17101,7 +16783,7 @@ function createTypeScriptAdapter(suppliedConfig) {
|
|
|
17101
16783
|
};
|
|
17102
16784
|
}
|
|
17103
16785
|
function mountAssumptionsAgree(record2, index) {
|
|
17104
|
-
return Object.entries(record2.mountPrefixes).every(
|
|
16786
|
+
return Object.entries(record2.meta.mountPrefixes).every(
|
|
17105
16787
|
([childId, prefix]) => (index.prefixForId?.(childId) ?? "") === prefix
|
|
17106
16788
|
);
|
|
17107
16789
|
}
|
|
@@ -17261,7 +16943,7 @@ function buildCacheAttribution(args) {
|
|
|
17261
16943
|
cacheable,
|
|
17262
16944
|
deps: [...deps].sort(),
|
|
17263
16945
|
claims: sink.claims,
|
|
17264
|
-
mountPrefixes: Object.fromEntries(sink.mountPrefixes),
|
|
16946
|
+
meta: { mountPrefixes: Object.fromEntries(sink.mountPrefixes) },
|
|
17265
16947
|
packs: packNamesByPath.get(rootPath) ?? []
|
|
17266
16948
|
});
|
|
17267
16949
|
}
|
|
@@ -17406,8 +17088,8 @@ function buildSubUnitSummary(subUnit, parent, invocationRecognizers, accessRecog
|
|
|
17406
17088
|
}
|
|
17407
17089
|
|
|
17408
17090
|
// src/bootstrap/noTsconfigProject.ts
|
|
17409
|
-
import
|
|
17410
|
-
import
|
|
17091
|
+
import fs5 from "fs";
|
|
17092
|
+
import path11 from "path";
|
|
17411
17093
|
import {
|
|
17412
17094
|
ModuleKind,
|
|
17413
17095
|
ModuleResolutionKind,
|
|
@@ -17443,7 +17125,7 @@ function findSourceFiles(rootDir, depth = 0) {
|
|
|
17443
17125
|
const found = [];
|
|
17444
17126
|
let entries;
|
|
17445
17127
|
try {
|
|
17446
|
-
entries =
|
|
17128
|
+
entries = fs5.readdirSync(rootDir, { withFileTypes: true });
|
|
17447
17129
|
} catch {
|
|
17448
17130
|
return [];
|
|
17449
17131
|
}
|
|
@@ -17451,10 +17133,10 @@ function findSourceFiles(rootDir, depth = 0) {
|
|
|
17451
17133
|
if (entry.name.startsWith(".") || SKIP_DIRECTORIES.has(entry.name)) {
|
|
17452
17134
|
continue;
|
|
17453
17135
|
}
|
|
17454
|
-
const full =
|
|
17136
|
+
const full = path11.join(rootDir, entry.name);
|
|
17455
17137
|
if (entry.isDirectory()) {
|
|
17456
17138
|
found.push(...findSourceFiles(full, depth + 1));
|
|
17457
|
-
} else if (SOURCE_EXTENSIONS.has(
|
|
17139
|
+
} else if (SOURCE_EXTENSIONS.has(path11.extname(entry.name)) && !entry.name.endsWith(".d.ts")) {
|
|
17458
17140
|
found.push(full);
|
|
17459
17141
|
}
|
|
17460
17142
|
}
|
|
@@ -17489,15 +17171,15 @@ function createProjectWithoutTsconfig(rootDir) {
|
|
|
17489
17171
|
return { project, files };
|
|
17490
17172
|
}
|
|
17491
17173
|
function findNearestTsconfig(startDir) {
|
|
17492
|
-
let dir =
|
|
17174
|
+
let dir = path11.resolve(startDir);
|
|
17493
17175
|
while (true) {
|
|
17494
17176
|
for (const name of ["tsconfig.json", "jsconfig.json"]) {
|
|
17495
|
-
const candidate =
|
|
17496
|
-
if (
|
|
17177
|
+
const candidate = path11.join(dir, name);
|
|
17178
|
+
if (fs5.existsSync(candidate)) {
|
|
17497
17179
|
return candidate;
|
|
17498
17180
|
}
|
|
17499
17181
|
}
|
|
17500
|
-
const parent =
|
|
17182
|
+
const parent = path11.dirname(dir);
|
|
17501
17183
|
if (parent === dir) {
|
|
17502
17184
|
return null;
|
|
17503
17185
|
}
|
|
@@ -17506,7 +17188,7 @@ function findNearestTsconfig(startDir) {
|
|
|
17506
17188
|
}
|
|
17507
17189
|
|
|
17508
17190
|
// src/facts/why.ts
|
|
17509
|
-
import
|
|
17191
|
+
import path12 from "path";
|
|
17510
17192
|
import { Node as Node65, Project as Project3, SyntaxKind as SyntaxKind12 } from "ts-morph";
|
|
17511
17193
|
import { explainResolutionProof, renderExplanation } from "@suss/resolution";
|
|
17512
17194
|
var JS_PHRASES = {
|
|
@@ -17514,12 +17196,12 @@ var JS_PHRASES = {
|
|
|
17514
17196
|
reason: `${describe(tuple[0])} is a .bind of ${describe(tuple[1])}, which runs the same function`
|
|
17515
17197
|
})
|
|
17516
17198
|
};
|
|
17517
|
-
var
|
|
17199
|
+
var TypeScriptWhySession = class {
|
|
17518
17200
|
project;
|
|
17519
17201
|
store;
|
|
17520
17202
|
root;
|
|
17521
17203
|
constructor(options) {
|
|
17522
|
-
this.root =
|
|
17204
|
+
this.root = path12.resolve(options.dir);
|
|
17523
17205
|
const tsconfig = findNearestTsconfig(this.root);
|
|
17524
17206
|
this.project = tsconfig === null ? createProjectWithoutTsconfig(this.root).project : new Project3({ tsConfigFilePath: tsconfig });
|
|
17525
17207
|
this.store = new ResolutionStore();
|
|
@@ -17608,7 +17290,7 @@ var WhySession = class {
|
|
|
17608
17290
|
};
|
|
17609
17291
|
}
|
|
17610
17292
|
sourceFileAt(file) {
|
|
17611
|
-
const absolute =
|
|
17293
|
+
const absolute = path12.resolve(this.root, file);
|
|
17612
17294
|
const loaded = this.project.getSourceFile(absolute);
|
|
17613
17295
|
if (loaded !== void 0) {
|
|
17614
17296
|
return loaded;
|
|
@@ -17622,7 +17304,7 @@ var WhySession = class {
|
|
|
17622
17304
|
/** A module key: a file path said relative to the root, or a package name. */
|
|
17623
17305
|
displayPath(key) {
|
|
17624
17306
|
if (key.startsWith(this.root)) {
|
|
17625
|
-
return
|
|
17307
|
+
return path12.relative(this.root, key);
|
|
17626
17308
|
}
|
|
17627
17309
|
return key;
|
|
17628
17310
|
}
|
|
@@ -17650,228 +17332,8 @@ function displayNameOf(node) {
|
|
|
17650
17332
|
return text.length > 40 ? `${text.slice(0, 40)}...` : text;
|
|
17651
17333
|
}
|
|
17652
17334
|
|
|
17653
|
-
// src/packHealth.ts
|
|
17654
|
-
function stagesOf(funnel) {
|
|
17655
|
-
const stages = [];
|
|
17656
|
-
const gateSaysSomething = funnel.gates.length > 0 && funnel.unresolvedGates.length === 0;
|
|
17657
|
-
if (funnel.recognizes && !funnel.discovers && gateSaysSomething) {
|
|
17658
|
-
stages.push({
|
|
17659
|
-
from: { count: funnel.unitsInGatedFiles, name: "unit bodies" },
|
|
17660
|
-
to: { count: funnel.effectsRecognized, name: "effects" }
|
|
17661
|
-
});
|
|
17662
|
-
}
|
|
17663
|
-
if (funnel.discovers && gateSaysSomething) {
|
|
17664
|
-
stages.push({
|
|
17665
|
-
from: { count: funnel.candidateFiles, name: "source files" },
|
|
17666
|
-
to: { count: funnel.unitsDiscovered, name: "units" }
|
|
17667
|
-
});
|
|
17668
|
-
}
|
|
17669
|
-
stages.push(
|
|
17670
|
-
{
|
|
17671
|
-
from: { count: funnel.unitsClaimed, name: "units" },
|
|
17672
|
-
to: { count: funnel.summariesBound, name: "summaries" }
|
|
17673
|
-
},
|
|
17674
|
-
{
|
|
17675
|
-
from: { count: funnel.summariesBound, name: "summaries" },
|
|
17676
|
-
to: { count: funnel.summariesWithBehavior, name: "transitions" }
|
|
17677
|
-
}
|
|
17678
|
-
);
|
|
17679
|
-
return stages;
|
|
17680
|
-
}
|
|
17681
|
-
function funnelDrops(packs) {
|
|
17682
|
-
const violations = [];
|
|
17683
|
-
for (const funnel of packs) {
|
|
17684
|
-
for (const stage of stagesOf(funnel)) {
|
|
17685
|
-
if (stage.from.count === 0 || stage.to.count > 0) {
|
|
17686
|
-
continue;
|
|
17687
|
-
}
|
|
17688
|
-
violations.push({
|
|
17689
|
-
label: funnel.pack,
|
|
17690
|
-
detail: `${stage.from.count} ${stage.from.name} -> 0 ${stage.to.name}`
|
|
17691
|
-
});
|
|
17692
|
-
}
|
|
17693
|
-
}
|
|
17694
|
-
return violations;
|
|
17695
|
-
}
|
|
17696
|
-
function recognizersWithNoUnits(packs) {
|
|
17697
|
-
return packs.filter(
|
|
17698
|
-
(funnel) => funnel.recognizes && !funnel.discovers && funnel.gates.length > 0 && funnel.unresolvedGates.length === 0 && funnel.candidateFiles > 0 && funnel.unitsInGatedFiles === 0 && // The closure walks a recognizer-only pack's gated exports as
|
|
17699
|
-
// roots, so effects recognized there mean the run worked and
|
|
17700
|
-
// the missing framework pack costs attribution, not existence.
|
|
17701
|
-
funnel.effectsRecognized === 0
|
|
17702
|
-
).map((funnel) => ({
|
|
17703
|
-
label: funnel.pack,
|
|
17704
|
-
detail: `${funnel.candidateFiles} gated files, and no pack in this run discovered a unit in them. ${funnel.pack} reads calls inside units another pack finds, so add the pack that finds this project's handlers (-f express, -f fastify, ...), or run suss init to work out which.`
|
|
17705
|
-
}));
|
|
17706
|
-
}
|
|
17707
|
-
function helpersThatMatchedNothing(packs) {
|
|
17708
|
-
return packs.flatMap(
|
|
17709
|
-
(funnel) => funnel.helpersUnmatched.map((helper) => ({
|
|
17710
|
-
label: funnel.pack,
|
|
17711
|
-
detail: `${helper} matched no call in this run, so whatever it registers is missing. suss read that helper out of the project and then failed to match a call to it, which is a bug in suss rather than in your code.`
|
|
17712
|
-
}))
|
|
17713
|
-
);
|
|
17714
|
-
}
|
|
17715
|
-
function unversionedPacks(packs) {
|
|
17716
|
-
return packs.filter((funnel) => funnel.version === null).map((funnel) => ({
|
|
17717
|
-
label: funnel.pack,
|
|
17718
|
-
detail: "no version declared"
|
|
17719
|
-
}));
|
|
17720
|
-
}
|
|
17721
|
-
function selfCollisions(packs) {
|
|
17722
|
-
return packs.filter((funnel) => funnel.selfCollisions > 0).map((funnel) => ({
|
|
17723
|
-
label: funnel.pack,
|
|
17724
|
-
detail: `${funnel.selfCollisions} ${funnel.selfCollisions === 1 ? "unit" : "units"} matched twice, second dropped`
|
|
17725
|
-
}));
|
|
17726
|
-
}
|
|
17727
|
-
function threwWhileReading(packs) {
|
|
17728
|
-
return packs.filter((funnel) => funnel.failures.length > 0).map((funnel) => {
|
|
17729
|
-
const first = funnel.failures[0];
|
|
17730
|
-
const rest = funnel.failures.length - 1;
|
|
17731
|
-
const alsoIn = rest > 0 ? ` (+${rest} more)` : "";
|
|
17732
|
-
return {
|
|
17733
|
-
label: funnel.pack,
|
|
17734
|
-
detail: `${first.hook} on ${first.file}${alsoIn}: ${first.message}`
|
|
17735
|
-
};
|
|
17736
|
-
});
|
|
17737
|
-
}
|
|
17738
|
-
function packGradients(report) {
|
|
17739
|
-
const gradients = [];
|
|
17740
|
-
for (const funnel of report.packs) {
|
|
17741
|
-
if (funnel.declarations === null) {
|
|
17742
|
-
continue;
|
|
17743
|
-
}
|
|
17744
|
-
gradients.push(gradientOf(funnel.pack, funnel.declarations.declarations));
|
|
17745
|
-
}
|
|
17746
|
-
return gradients;
|
|
17747
|
-
}
|
|
17748
|
-
function gradientOf(pack, declarations) {
|
|
17749
|
-
const gradient = {
|
|
17750
|
-
pack,
|
|
17751
|
-
dataLinks: 0,
|
|
17752
|
-
functionLinks: [],
|
|
17753
|
-
astLinks: [],
|
|
17754
|
-
withoutExample: []
|
|
17755
|
-
};
|
|
17756
|
-
for (const declaration of declarations) {
|
|
17757
|
-
gradient.dataLinks += declaration.dataLinks;
|
|
17758
|
-
for (const question of declaration.functionLinks) {
|
|
17759
|
-
gradient.functionLinks.push(`${declaration.name}.${question}`);
|
|
17760
|
-
}
|
|
17761
|
-
for (const question of declaration.astLinks) {
|
|
17762
|
-
gradient.astLinks.push(`${declaration.name}.${question}`);
|
|
17763
|
-
}
|
|
17764
|
-
if (declaration.example === null) {
|
|
17765
|
-
gradient.withoutExample.push(declaration.name);
|
|
17766
|
-
}
|
|
17767
|
-
}
|
|
17768
|
-
return gradient;
|
|
17769
|
-
}
|
|
17770
|
-
function opaqueLinks(gradients) {
|
|
17771
|
-
return gradients.filter((gradient) => gradient.functionLinks.length > 0).map((gradient) => ({
|
|
17772
|
-
label: gradient.pack,
|
|
17773
|
-
detail: `${gradient.functionLinks.join(", ")} (${gradient.dataLinks} data)`
|
|
17774
|
-
}));
|
|
17775
|
-
}
|
|
17776
|
-
function reachesTheSyntaxTree(gradients) {
|
|
17777
|
-
return gradients.filter((gradient) => gradient.astLinks.length > 0).map((gradient) => ({
|
|
17778
|
-
label: gradient.pack,
|
|
17779
|
-
detail: gradient.astLinks.join(", ")
|
|
17780
|
-
}));
|
|
17781
|
-
}
|
|
17782
|
-
function undocumentedDeclarations(gradients) {
|
|
17783
|
-
return gradients.filter((gradient) => gradient.withoutExample.length > 0).map((gradient) => ({
|
|
17784
|
-
label: gradient.pack,
|
|
17785
|
-
detail: gradient.withoutExample.join(", ")
|
|
17786
|
-
}));
|
|
17787
|
-
}
|
|
17788
|
-
function evaluatePackHealth(report) {
|
|
17789
|
-
const gradients = packGradients(report);
|
|
17790
|
-
return [
|
|
17791
|
-
{
|
|
17792
|
-
name: "no pack throws while it reads",
|
|
17793
|
-
code: "threw",
|
|
17794
|
-
audience: "run",
|
|
17795
|
-
violations: threwWhileReading(report.packs)
|
|
17796
|
-
},
|
|
17797
|
-
{
|
|
17798
|
-
name: "no pack finds something and records nothing",
|
|
17799
|
-
code: "no-output",
|
|
17800
|
-
audience: "run",
|
|
17801
|
-
violations: funnelDrops(report.packs)
|
|
17802
|
-
},
|
|
17803
|
-
{
|
|
17804
|
-
name: "no pack collides with itself",
|
|
17805
|
-
code: "double-match",
|
|
17806
|
-
audience: "run",
|
|
17807
|
-
violations: selfCollisions(report.packs)
|
|
17808
|
-
},
|
|
17809
|
-
{
|
|
17810
|
-
name: "every recognizer had units to look inside",
|
|
17811
|
-
code: "no-units",
|
|
17812
|
-
audience: "run",
|
|
17813
|
-
violations: recognizersWithNoUnits(report.packs)
|
|
17814
|
-
},
|
|
17815
|
-
{
|
|
17816
|
-
name: "every registration helper the run read matched a call",
|
|
17817
|
-
code: "no-helper",
|
|
17818
|
-
audience: "run",
|
|
17819
|
-
violations: helpersThatMatchedNothing(report.packs)
|
|
17820
|
-
},
|
|
17821
|
-
{
|
|
17822
|
-
name: "every pack declares a version",
|
|
17823
|
-
code: "no-version",
|
|
17824
|
-
audience: "pack",
|
|
17825
|
-
violations: unversionedPacks(report.packs)
|
|
17826
|
-
},
|
|
17827
|
-
{
|
|
17828
|
-
name: "every link a declared pack states is data",
|
|
17829
|
-
code: "fn-link",
|
|
17830
|
-
audience: "pack",
|
|
17831
|
-
violations: opaqueLinks(gradients)
|
|
17832
|
-
},
|
|
17833
|
-
{
|
|
17834
|
-
name: "no declared pack reads the syntax tree",
|
|
17835
|
-
code: "ast-link",
|
|
17836
|
-
audience: "pack",
|
|
17837
|
-
violations: reachesTheSyntaxTree(gradients)
|
|
17838
|
-
},
|
|
17839
|
-
{
|
|
17840
|
-
name: "every declaration states an example",
|
|
17841
|
-
code: "no-example",
|
|
17842
|
-
audience: "pack",
|
|
17843
|
-
violations: undocumentedDeclarations(gradients)
|
|
17844
|
-
}
|
|
17845
|
-
];
|
|
17846
|
-
}
|
|
17847
|
-
function formatPackHealth(checks, audiences) {
|
|
17848
|
-
const fired = checks.filter(
|
|
17849
|
-
(check) => check.violations.length > 0 && audiences.includes(check.audience)
|
|
17850
|
-
);
|
|
17851
|
-
if (fired.length === 0) {
|
|
17852
|
-
return "";
|
|
17853
|
-
}
|
|
17854
|
-
const rows = fired.flatMap(
|
|
17855
|
-
(check) => check.violations.map((violation) => ({
|
|
17856
|
-
code: check.code,
|
|
17857
|
-
pack: violation.label,
|
|
17858
|
-
detail: violation.detail
|
|
17859
|
-
}))
|
|
17860
|
-
);
|
|
17861
|
-
const codeWidth = Math.max(...rows.map((row) => row.code.length));
|
|
17862
|
-
const packWidth = Math.max(...rows.map((row) => row.pack.length));
|
|
17863
|
-
const lines = ["", `Pack health (${rows.length}):`];
|
|
17864
|
-
for (const row of rows) {
|
|
17865
|
-
lines.push(
|
|
17866
|
-
` ${row.code.padEnd(codeWidth)} ${row.pack.padEnd(packWidth)} ${row.detail}`
|
|
17867
|
-
);
|
|
17868
|
-
}
|
|
17869
|
-
return `${lines.join("\n")}
|
|
17870
|
-
`;
|
|
17871
|
-
}
|
|
17872
|
-
|
|
17873
17335
|
// src/stubEvidence.ts
|
|
17874
|
-
import
|
|
17336
|
+
import path13 from "path";
|
|
17875
17337
|
import { Project as Project4 } from "ts-morph";
|
|
17876
17338
|
function stubEvidenceIn(project, packageName, root) {
|
|
17877
17339
|
const resolution = new ResolutionStore();
|
|
@@ -17887,7 +17349,7 @@ function stubEvidenceIn(project, packageName, root) {
|
|
|
17887
17349
|
calls: []
|
|
17888
17350
|
};
|
|
17889
17351
|
entry.calls.push({
|
|
17890
|
-
file:
|
|
17352
|
+
file: path13.relative(root, sourceFile.getFilePath()).split(path13.sep).join("/"),
|
|
17891
17353
|
line: one.call.getStartLineNumber(),
|
|
17892
17354
|
args: one.call.getArguments().map((arg) => effectArgOf(arg))
|
|
17893
17355
|
});
|
|
@@ -17902,7 +17364,7 @@ function stubEvidence(options) {
|
|
|
17902
17364
|
const project = new Project4(
|
|
17903
17365
|
options.tsConfigFilePath !== void 0 ? { tsConfigFilePath: options.tsConfigFilePath } : { compilerOptions: { allowJs: true } }
|
|
17904
17366
|
);
|
|
17905
|
-
const root = options.tsConfigFilePath !== void 0 ?
|
|
17367
|
+
const root = options.tsConfigFilePath !== void 0 ? path13.dirname(options.tsConfigFilePath) : path13.resolve(options.directory ?? process.cwd());
|
|
17906
17368
|
if (options.tsConfigFilePath === void 0) {
|
|
17907
17369
|
project.addSourceFilesAtPaths([
|
|
17908
17370
|
`${root}/**/*.{ts,tsx,js,jsx}`,
|
|
@@ -17914,7 +17376,7 @@ function stubEvidence(options) {
|
|
|
17914
17376
|
export {
|
|
17915
17377
|
ADAPTER_VERSION,
|
|
17916
17378
|
ResolutionStore,
|
|
17917
|
-
|
|
17379
|
+
TypeScriptWhySession,
|
|
17918
17380
|
accessContextFor,
|
|
17919
17381
|
buildProjectHelperIndex,
|
|
17920
17382
|
callOpsFor,
|
|
@@ -17927,13 +17389,11 @@ export {
|
|
|
17927
17389
|
createTsSubUnitContext,
|
|
17928
17390
|
createTypeScriptAdapter,
|
|
17929
17391
|
discoverUnits,
|
|
17930
|
-
evaluatePackHealth,
|
|
17931
17392
|
exportedDeclarationsOf,
|
|
17932
17393
|
extractCodeStructure,
|
|
17933
17394
|
extractRawBranches,
|
|
17934
17395
|
findNearestTsconfig,
|
|
17935
17396
|
findTerminals,
|
|
17936
|
-
formatPackHealth,
|
|
17937
17397
|
functionTargetOf,
|
|
17938
17398
|
importDeclarationsOf,
|
|
17939
17399
|
importedNamesOf,
|