@cplieger/deadset-ts 4.0.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -50
- package/dist/bin/component-mapper.js +5 -0
- package/dist/bin/mapper-connection.js +101 -0
- package/dist/bin/node-host.js +24 -2
- package/dist/src/analysis.js +230 -37
- package/dist/src/component-files.js +612 -0
- package/dist/src/component-layer.js +132 -0
- package/dist/src/config.js +9 -2
- package/dist/src/configuration-files.js +186 -0
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/convention-rows.js +383 -0
- package/dist/src/conventions.js +456 -0
- package/dist/src/dependencies.js +30 -3
- package/dist/src/discover.js +11 -4
- package/dist/src/emit-map.js +72 -0
- package/dist/src/entry-point-gaps.js +81 -35
- package/dist/src/entry-points.js +47 -32
- package/dist/src/exempt.js +11 -3
- package/dist/src/findings/completion.js +19 -29
- package/dist/src/findings/emitters.js +22 -1
- package/dist/src/findings/unused-declarations.js +7 -2
- package/dist/src/graph.js +13 -5
- package/dist/src/inventory.js +4 -2
- package/dist/src/kinds.js +2 -2
- package/dist/src/manifest.js +37 -11
- package/dist/src/matrix.js +21 -13
- package/dist/src/partial.js +3 -1
- package/dist/src/pnpm-workspace.js +120 -0
- package/dist/src/position.js +17 -2
- package/dist/src/print.js +2 -0
- package/dist/src/references.js +209 -30
- package/dist/src/report.js +26 -6
- package/dist/src/reporters/annotations.js +10 -3
- package/dist/src/reporters/template.js +6 -3
- package/dist/src/resolve.js +31 -6
- package/dist/src/roots.js +37 -8
- package/dist/src/run.js +13 -3
- package/dist/src/schema.js +4 -2
- package/dist/src/session.js +141 -34
- package/dist/src/setup-failure.js +23 -0
- package/dist/src/source-files.js +4 -61
- package/dist/src/sweep.js +4 -2
- package/dist/src/template-field.js +38 -6
- package/dist/src/test-support.js +59 -0
- package/dist/src/type-errors.js +252 -0
- package/dist/src/type-query-alias.js +92 -0
- package/dist/src/verbs/analyze.js +24 -4
- package/dist/src/verbs/explain.js +4 -1
- package/dist/src/verbs/print-projects.js +17 -56
- package/dist/src/verbs/print-retained.js +7 -2
- package/dist/src/verbs/print-roots.js +7 -2
- package/dist/src/version-range.js +255 -0
- package/dist/src/version.js +1 -1
- package/dist/src/workspace-manifests.js +68 -0
- package/dist/src/workspace-programs.js +342 -0
- package/dist/src/workspace-resolution.js +307 -0
- package/dist/src/workspace-run.js +75 -0
- package/dist/src/workspace.js +358 -0
- package/jsr.json +1 -1
- package/package.json +2 -1
- package/src/analysis.ts +395 -71
- package/src/component-files.ts +718 -0
- package/src/component-layer.ts +160 -0
- package/src/config.ts +11 -2
- package/src/configuration-files.ts +235 -0
- package/src/conformance-record.ts +2 -2
- package/src/convention-rows.ts +440 -0
- package/src/conventions.ts +640 -0
- package/src/dependencies.ts +36 -2
- package/src/discover.ts +11 -7
- package/src/emit-map.ts +93 -0
- package/src/entry-point-gaps.ts +92 -38
- package/src/entry-points.ts +66 -36
- package/src/exempt.ts +21 -3
- package/src/findings/completion.ts +24 -33
- package/src/findings/emitters.ts +32 -1
- package/src/findings/unused-declarations.ts +9 -2
- package/src/graph.ts +15 -4
- package/src/host.ts +23 -0
- package/src/index.ts +1 -1
- package/src/inventory.ts +13 -2
- package/src/kinds.ts +2 -2
- package/src/manifest.ts +47 -14
- package/src/matrix.ts +43 -15
- package/src/partial.ts +6 -1
- package/src/pnpm-workspace.ts +135 -0
- package/src/position.ts +23 -2
- package/src/print.ts +2 -0
- package/src/references.ts +227 -28
- package/src/report.ts +56 -6
- package/src/reporters/annotations.ts +12 -3
- package/src/reporters/template.ts +6 -3
- package/src/resolve.ts +59 -6
- package/src/roots.ts +60 -9
- package/src/run.ts +15 -5
- package/src/schema.ts +4 -2
- package/src/session.ts +218 -37
- package/src/setup-failure.ts +39 -0
- package/src/source-files.ts +4 -66
- package/src/sweep.ts +9 -2
- package/src/template-field.ts +42 -7
- package/src/test-support.ts +77 -0
- package/src/type-errors.ts +340 -0
- package/src/type-query-alias.ts +133 -0
- package/src/verbs/analyze.ts +38 -4
- package/src/verbs/explain.ts +4 -1
- package/src/verbs/print-projects.ts +17 -69
- package/src/verbs/print-retained.ts +7 -2
- package/src/verbs/print-roots.ts +7 -2
- package/src/verbs/verb.ts +6 -3
- package/src/version-range.ts +309 -0
- package/src/version.ts +1 -1
- package/src/workspace-manifests.ts +100 -0
- package/src/workspace-programs.ts +486 -0
- package/src/workspace-resolution.ts +393 -0
- package/src/workspace-run.ts +148 -0
- package/src/workspace.ts +463 -0
package/dist/src/analysis.js
CHANGED
|
@@ -7,15 +7,17 @@
|
|
|
7
7
|
* carries a project's identity in its type so a handle cannot cross to another
|
|
8
8
|
* project's checker, and that holds only for views the session itself hands out.
|
|
9
9
|
*/
|
|
10
|
+
import { lineOf, readComponent } from "./component-files.js";
|
|
10
11
|
import { consumerLoads, consumerReferences, moduleIdentity, } from "./consumers.js";
|
|
11
12
|
import { configuredDeclarations, configuredRef, declarationsNamed, } from "./configured-declarations.js";
|
|
12
13
|
import { decorator } from "./decorator.js";
|
|
13
14
|
import { dependenciesOf, projectNeeds } from "./dependencies.js";
|
|
14
15
|
import { deprecatedDeclarations } from "./deprecation.js";
|
|
15
16
|
import { discoverProjects, DiscoveryError, isGuess, projectErrors, renderDiagnostic, targetRelative, } from "./discover.js";
|
|
17
|
+
import { conventionFailuresFor, readConventions, } from "./conventions.js";
|
|
16
18
|
import { readEdgeSides } from "./edges.js";
|
|
17
19
|
import { enumGroup } from "./enum-group.js";
|
|
18
|
-
import { computeExemptions, disabledClasses, exemptionsOf, isUnansweredRecord, retainedIn, } from "./exempt.js";
|
|
20
|
+
import { computeExemptions, disabledClasses, exemptionsOf, holdingWhile, isUnansweredRecord, retainedIn, } from "./exempt.js";
|
|
19
21
|
import { heldByInclusion, sourceTree } from "./file-facts.js";
|
|
20
22
|
import { findingsPass, recordedFindings, rowKey } from "./findings-pass.js";
|
|
21
23
|
import { NO_SELF_CHECK, unmatchedRootFindings, } from "./findings/self-check.js";
|
|
@@ -26,19 +28,23 @@ import { interfaceSatisfaction } from "./interface-satisfaction.js";
|
|
|
26
28
|
import { intraFunctionFacts, projectParts } from "./intra-function-parts.js";
|
|
27
29
|
import { inventory } from "./inventory.js";
|
|
28
30
|
import { readManifest } from "./manifest.js";
|
|
29
|
-
import { matrixOf, referenceKey, sweepMatrix, } from "./matrix.js";
|
|
30
|
-
import { relativePath, resolvePath } from "./paths.js";
|
|
31
|
-
import { byPosition } from "./position.js";
|
|
31
|
+
import { liveFilesAt, matrixOf, referenceKey, sweepMatrix, } from "./matrix.js";
|
|
32
|
+
import { joinPath, relativePath, resolvePath } from "./paths.js";
|
|
33
|
+
import { byPosition, isComponentFile } from "./position.js";
|
|
34
|
+
import { SetupError, setupLine } from "./setup-failure.js";
|
|
35
|
+
import { pathsOf, readErrors, skippedRoots, skippedUnits, } from "./type-errors.js";
|
|
32
36
|
import { reflectiveLookup } from "./reflective-lookup.js";
|
|
33
37
|
import { references, testFileRulesOf } from "./references.js";
|
|
34
38
|
import { roots, unmatchedEverywhere } from "./roots.js";
|
|
35
39
|
import { serializationContract } from "./serialization-contract.js";
|
|
36
|
-
import {
|
|
40
|
+
import { supportReferences, testSupportFiles } from "./test-support.js";
|
|
41
|
+
import { diagnosticsOf, ownedAs, runSession } from "./session.js";
|
|
37
42
|
import { accessorsOf, storesOf } from "./stores.js";
|
|
38
43
|
import { isPartKind } from "./suppress.js";
|
|
39
44
|
import { bindRows, readBaseline, readIgnoreFile } from "./suppress-file.js";
|
|
40
45
|
import { inlineDirectives, inlineSuppressions } from "./suppress-inline.js";
|
|
41
46
|
import { readTemplates, templateField } from "./template-field.js";
|
|
47
|
+
import { ownErrors, workspaceRun } from "./workspace-run.js";
|
|
42
48
|
/** The setting whose source decides where a finding about a configured root sits. */
|
|
43
49
|
const ROOTS_SETTING = "roots.patterns";
|
|
44
50
|
/**
|
|
@@ -63,12 +69,29 @@ function compare(a, b) {
|
|
|
63
69
|
return a < b ? -1 : 1;
|
|
64
70
|
}
|
|
65
71
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
* The manifests the analysis reads, each once: the target's own, then every workspace
|
|
73
|
+
* member's below the target root.
|
|
74
|
+
*/
|
|
75
|
+
function conventionManifests(host, targetRoot, memberDirs) {
|
|
76
|
+
const found = new Map();
|
|
77
|
+
if (host.kindOf(joinPath(targetRoot, "package.json")) === "file") {
|
|
78
|
+
found.set(targetRoot, { dir: targetRoot, path: "package.json" });
|
|
79
|
+
}
|
|
80
|
+
for (const dir of memberDirs) {
|
|
81
|
+
const below = relativePath(targetRoot, dir);
|
|
82
|
+
if (below !== undefined && !found.has(dir)) {
|
|
83
|
+
found.set(dir, { dir, path: `${below}/package.json` });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return [...found.values()];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Reads every project the scope discovers, and each declared consumer's, in one snapshot,
|
|
90
|
+
* each checked for errors first. `stage` is handed a target project and enumerates and roots
|
|
91
|
+
* it if it reads it; a consumer project is read for its references to the target. A type error
|
|
92
|
+
* in an own file skips the unit holding it. A guess that does not load or meets a setup failure
|
|
93
|
+
* is dropped into `notBuilt` (see {@link isGuess}); any other such project fails the run, as
|
|
94
|
+
* does a run whose every guess was dropped.
|
|
72
95
|
*/
|
|
73
96
|
function readProjects(engine, host, scope, config, stage) {
|
|
74
97
|
const targetRoot = scope.target.path;
|
|
@@ -76,14 +99,19 @@ function readProjects(engine, host, scope, config, stage) {
|
|
|
76
99
|
const ids = new Map(discovered.projects.map((project) => [project.configFile, project.id]));
|
|
77
100
|
const loads = consumerLoads(engine, host, scope, discovered.configFiles);
|
|
78
101
|
const consumerOf = new Map(loads.flatMap((load) => load.configFiles.map((configFile) => [configFile, load.consumer])));
|
|
102
|
+
const absoluteRoot = resolvePath(host.workingDirectory(), targetRoot);
|
|
103
|
+
const workspace = workspaceRun(engine, host, absoluteRoot, discovered.configFiles, readManifest(host, targetRoot));
|
|
104
|
+
const conventions = readConventions(host, (fileName, text) => engine.parseSourceFile(fileName, text), conventionManifests(host, absoluteRoot, workspace?.memberDirs ?? []), config.ts.disabledConventions);
|
|
79
105
|
const options = {
|
|
80
|
-
|
|
106
|
+
host,
|
|
107
|
+
manifest: workspace?.manifest ?? readManifest(host, targetRoot),
|
|
108
|
+
workspaceEntries: workspace?.entries,
|
|
81
109
|
patterns: config.rootPatterns,
|
|
82
110
|
entryFiles: config.ts.entryFiles,
|
|
111
|
+
conventions,
|
|
83
112
|
testFiles: config.ts.testFiles,
|
|
84
113
|
publishedAPI: config.targetKind === "library",
|
|
85
114
|
};
|
|
86
|
-
const absoluteRoot = resolvePath(host.workingDirectory(), targetRoot);
|
|
87
115
|
const notBuilt = [...discovered.notBuilt];
|
|
88
116
|
const dropped = (configFile, error) => {
|
|
89
117
|
notBuilt.push({
|
|
@@ -93,10 +121,36 @@ function readProjects(engine, host, scope, config, stage) {
|
|
|
93
121
|
});
|
|
94
122
|
};
|
|
95
123
|
const failures = [];
|
|
124
|
+
const unbuildable = [];
|
|
125
|
+
const setupDropped = [];
|
|
96
126
|
const consumed = new Map();
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
|
|
127
|
+
const setupMet = (configFile, met) => {
|
|
128
|
+
const [first] = met;
|
|
129
|
+
if (first !== undefined && isGuess(discovered, configFile)) {
|
|
130
|
+
dropped(configFile, setupLine(first));
|
|
131
|
+
setupDropped.push(...met);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
unbuildable.push(...met);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
const session = runSession(engine, [...discovered.configFiles, ...consumerOf.keys()], (opened) => {
|
|
138
|
+
const consumer = consumerOf.get(opened.configFile);
|
|
139
|
+
const setup = consumer === undefined ? workspace?.unbuildable(opened.configFile) : undefined;
|
|
140
|
+
if (setup !== undefined) {
|
|
141
|
+
setupMet(opened.configFile, [
|
|
142
|
+
{ setupClass: "workspace-member-without-source", detail: setup },
|
|
143
|
+
]);
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
const referenceOnly = workspace === undefined || consumer !== undefined
|
|
147
|
+
? () => false
|
|
148
|
+
: workspace.referenceOnly(opened.configFile, opened.rootFiles);
|
|
149
|
+
const project = workspace === undefined || consumer !== undefined
|
|
150
|
+
? opened
|
|
151
|
+
: ownedAs(opened, (file, fromExternalLibrary) => workspace.ownFile(file, fromExternalLibrary));
|
|
152
|
+
const raw = projectErrors(diagnosticsOf(project));
|
|
153
|
+
const errors = { load: raw.load, check: ownErrors(raw.check, referenceOnly) };
|
|
100
154
|
const [unloaded] = errors.load;
|
|
101
155
|
if (unloaded !== undefined &&
|
|
102
156
|
consumer === undefined &&
|
|
@@ -104,8 +158,37 @@ function readProjects(engine, host, scope, config, stage) {
|
|
|
104
158
|
dropped(project.configFile, renderDiagnostic(unloaded, (path) => relativePath(absoluteRoot, path) ?? path));
|
|
105
159
|
return undefined;
|
|
106
160
|
}
|
|
107
|
-
if (errors.load.length > 0
|
|
108
|
-
failures.push(...errors.load
|
|
161
|
+
if (errors.load.length > 0) {
|
|
162
|
+
failures.push(...errors.load);
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
const reading = readErrors(project, errors.check, {
|
|
166
|
+
host,
|
|
167
|
+
targetRoot: consumer === undefined
|
|
168
|
+
? absoluteRoot
|
|
169
|
+
: resolvePath(host.workingDirectory(), consumer.path),
|
|
170
|
+
paths: pathsOf(engine, project.configFile),
|
|
171
|
+
});
|
|
172
|
+
if (reading.failing.length > 0) {
|
|
173
|
+
failures.push(...reading.failing);
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
if (consumer !== undefined && reading.missing.length > 0) {
|
|
177
|
+
unbuildable.push(...reading.missing.map((one) => ({
|
|
178
|
+
setupClass: "missing-consumer",
|
|
179
|
+
detail: `the consumer ${consumer.path} does not build, its dependencies not installed: ${one.detail}`,
|
|
180
|
+
})));
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
if (reading.missing.length > 0) {
|
|
184
|
+
setupMet(project.configFile, reading.missing);
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
const unconventional = consumer === undefined
|
|
188
|
+
? conventionFailuresFor(conventions, project.configFile, project.ownSourceFiles().map((file) => file.fileName))
|
|
189
|
+
: [];
|
|
190
|
+
if (unconventional.length > 0) {
|
|
191
|
+
setupMet(project.configFile, unconventional);
|
|
109
192
|
return undefined;
|
|
110
193
|
}
|
|
111
194
|
if (consumer !== undefined) {
|
|
@@ -117,22 +200,52 @@ function readProjects(engine, host, scope, config, stage) {
|
|
|
117
200
|
}
|
|
118
201
|
return undefined;
|
|
119
202
|
}
|
|
120
|
-
const held = inventory(project, host, targetRoot);
|
|
121
|
-
const rooted = roots(project, held, targetRoot, options);
|
|
122
203
|
const configuration = ids.get(project.configFile) ?? project.configFile;
|
|
123
|
-
|
|
124
|
-
|
|
204
|
+
const read = () => {
|
|
205
|
+
const held = inventory(project, host, targetRoot);
|
|
206
|
+
const found = roots(project, held, targetRoot, options);
|
|
207
|
+
const kept = skippedRoots(project, held, reading.skips);
|
|
208
|
+
const rooted = kept.length === 0
|
|
209
|
+
? found
|
|
210
|
+
: {
|
|
211
|
+
...found,
|
|
212
|
+
liveUnderReachability: [
|
|
213
|
+
...found.liveUnderReachability,
|
|
214
|
+
...kept.map((id) => ({ id, kind: "type-error", source: "" })),
|
|
215
|
+
],
|
|
216
|
+
};
|
|
217
|
+
return {
|
|
218
|
+
configuration,
|
|
219
|
+
configFile: project.configFile,
|
|
220
|
+
held,
|
|
221
|
+
rooted,
|
|
222
|
+
typeErrorSkips: reading.skips.map((one) => one.record),
|
|
223
|
+
skipped: skippedUnits(reading.skips, absoluteRoot),
|
|
224
|
+
referenceOnly: project
|
|
225
|
+
.ownSourceFiles()
|
|
226
|
+
.filter((file) => referenceOnly(file.fileName))
|
|
227
|
+
.flatMap((file) => relativePath(absoluteRoot, file.fileName) ?? []),
|
|
228
|
+
};
|
|
229
|
+
};
|
|
230
|
+
return stage(project, { configuration, read });
|
|
231
|
+
}, discovered.derived ? "record" : "refuse", workspace?.programs);
|
|
125
232
|
for (const configFile of session.unopened) {
|
|
126
233
|
if (consumerOf.has(configFile) || !isGuess(discovered, configFile)) {
|
|
127
234
|
throw new DiscoveryError(`${configFile} opened no project`, []);
|
|
128
235
|
}
|
|
129
236
|
dropped(configFile, "the compiler opened no project for the configuration");
|
|
130
237
|
}
|
|
238
|
+
if (unbuildable.length > 0) {
|
|
239
|
+
throw new SetupError(unbuildable);
|
|
240
|
+
}
|
|
131
241
|
if (failures.length > 0) {
|
|
132
|
-
throw new DiscoveryError(`${String(failures.length)} error(s)
|
|
242
|
+
throw new DiscoveryError(`${String(failures.length)} error(s), so no answer was produced`, failures);
|
|
133
243
|
}
|
|
134
244
|
const answers = session.projects.filter((answer) => answer !== undefined);
|
|
135
245
|
const [first] = notBuilt;
|
|
246
|
+
if (answers.length === 0 && setupDropped.length > 0) {
|
|
247
|
+
throw new SetupError(setupDropped);
|
|
248
|
+
}
|
|
136
249
|
if (answers.length === 0 && first !== undefined) {
|
|
137
250
|
throw new DiscoveryError(`no configuration discovery derived could be built; ${first.id}: ${first.error}`, []);
|
|
138
251
|
}
|
|
@@ -144,8 +257,19 @@ function readProjects(engine, host, scope, config, stage) {
|
|
|
144
257
|
},
|
|
145
258
|
notBuilt,
|
|
146
259
|
unanswered: session.unanswered,
|
|
260
|
+
componentsUnread: workspace?.componentsUnread ?? [],
|
|
261
|
+
conventionsApplied: conventions.applied,
|
|
147
262
|
};
|
|
148
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* The projects the run the scope and the configuration describe analyzes, each read
|
|
266
|
+
* for the errors and setup failures that decide whether it is analyzed, dropped or
|
|
267
|
+
* fails the run, exactly as the analysis reads them.
|
|
268
|
+
*/
|
|
269
|
+
export function runProjects(engine, host, scope, config) {
|
|
270
|
+
const read = readProjects(engine, host, scope, config, (_project, opened) => opened.configuration);
|
|
271
|
+
return { configurations: read.answers, notBuilt: read.notBuilt, unanswered: read.unanswered };
|
|
272
|
+
}
|
|
149
273
|
/**
|
|
150
274
|
* The root set of the run the scope and the configuration describe.
|
|
151
275
|
*
|
|
@@ -154,7 +278,7 @@ function readProjects(engine, host, scope, config, stage) {
|
|
|
154
278
|
*/
|
|
155
279
|
export function runRoots(engine, host, scope, config, provenance) {
|
|
156
280
|
const targetRoot = scope.target.path;
|
|
157
|
-
const read = readProjects(engine, host, scope, config, (_project,
|
|
281
|
+
const read = readProjects(engine, host, scope, config, (_project, opened) => opened.read());
|
|
158
282
|
const projects = read.answers;
|
|
159
283
|
const configurations = [];
|
|
160
284
|
const merged = new Map();
|
|
@@ -217,36 +341,53 @@ function readRun(engine, host, scope, config, mode, detectors, extra) {
|
|
|
217
341
|
? { delimiters: config.analysis.templateDelimiters, files: [] }
|
|
218
342
|
: readTemplates(host, targetRoot, config.analysis);
|
|
219
343
|
const consumers = scope.consumers.map((consumer) => consumer.path);
|
|
220
|
-
const read = readProjects(engine, host, scope, config, (project,
|
|
344
|
+
const read = readProjects(engine, host, scope, config, (project, opened) => {
|
|
345
|
+
const projectRead = opened.read();
|
|
221
346
|
const resolved = references(project, projectRead.held, targetRoot, {
|
|
222
347
|
testFiles: config.ts.testFiles,
|
|
223
348
|
});
|
|
349
|
+
const support = testSupportFiles(projectRead.held.symbols, resolved.references, projectRead.rooted.liveUnderReachability, resolved.testFilePaths);
|
|
350
|
+
const made = supportReferences(resolved.references, support);
|
|
224
351
|
const configured = {
|
|
225
352
|
configuration: projectRead.configuration,
|
|
226
353
|
symbols: projectRead.held.symbols,
|
|
227
|
-
references:
|
|
354
|
+
references: made,
|
|
228
355
|
roots: projectRead.rooted.liveUnderReachability,
|
|
229
356
|
testFiles: resolved.testFilePaths,
|
|
357
|
+
...(support.size === 0 ? {} : { supportFiles: [...support].sort() }),
|
|
358
|
+
...(projectRead.referenceOnly.length === 0
|
|
359
|
+
? {}
|
|
360
|
+
: { referenceOnly: projectRead.referenceOnly }),
|
|
230
361
|
};
|
|
231
362
|
const exempt = computeExemptions({ project, held: projectRead.held, targetRoot, templates, ts: config.ts, consumers }, detectors, {
|
|
232
363
|
disabled,
|
|
233
364
|
mode,
|
|
234
|
-
testFiles: new Set(resolved.testFilePaths),
|
|
365
|
+
testFiles: new Set([...resolved.testFilePaths, ...support]),
|
|
235
366
|
});
|
|
236
367
|
const unanswered = [
|
|
237
368
|
...projectRead.held.unanswered,
|
|
238
369
|
...projectRead.rooted.unanswered,
|
|
239
|
-
...
|
|
370
|
+
...made
|
|
240
371
|
.filter((reference) => reference.resolution === "by-name")
|
|
241
372
|
.map((reference) => reference.to),
|
|
242
373
|
...exempt.filter(isUnansweredRecord).map((record) => record.id),
|
|
243
374
|
];
|
|
375
|
+
const components = project.ownSourceFiles().filter(isComponentFile);
|
|
376
|
+
const pathOf = (file) => relativePath(targetRoot, file.fileName) ?? file.fileName;
|
|
244
377
|
return {
|
|
245
378
|
configFile: projectRead.configFile,
|
|
246
379
|
configured,
|
|
247
380
|
exempt,
|
|
248
381
|
unanswered,
|
|
249
|
-
|
|
382
|
+
typeErrorSkips: projectRead.typeErrorSkips,
|
|
383
|
+
skipped: projectRead.skipped,
|
|
384
|
+
componentFiles: components.map(pathOf),
|
|
385
|
+
componentWarnings: components.flatMap((file) => readComponent(file.originalText).warnings.map((warning) => ({
|
|
386
|
+
path: pathOf(file),
|
|
387
|
+
line: lineOf(file.originalText, warning.offset),
|
|
388
|
+
reason: warning.reason,
|
|
389
|
+
}))),
|
|
390
|
+
extra: extra(project, projectRead, made),
|
|
250
391
|
};
|
|
251
392
|
});
|
|
252
393
|
const consumed = read.consumers.references;
|
|
@@ -272,8 +413,33 @@ function readRun(engine, host, scope, config, mode, detectors, extra) {
|
|
|
272
413
|
consumers: read.consumers.loaded,
|
|
273
414
|
notBuilt: read.notBuilt,
|
|
274
415
|
unanswered: read.unanswered,
|
|
416
|
+
componentsUnread: read.componentsUnread,
|
|
417
|
+
conventionsApplied: read.conventionsApplied,
|
|
275
418
|
};
|
|
276
419
|
}
|
|
420
|
+
/**
|
|
421
|
+
* The run swept under one set of marks with the records that hold. A record a component
|
|
422
|
+
* file's markup holds counts only while the file is live, and what it holds may be what
|
|
423
|
+
* keeps the file live, so each file's markup joins only once a sweep without it finds the
|
|
424
|
+
* file live, until a sweep finds no further file live.
|
|
425
|
+
*/
|
|
426
|
+
function holdingSweep(read, input) {
|
|
427
|
+
const paths = new Set(read.exempt.flatMap((record) => record.whileLive ?? []));
|
|
428
|
+
let live = new Set();
|
|
429
|
+
for (;;) {
|
|
430
|
+
const swept = {
|
|
431
|
+
...input,
|
|
432
|
+
exempt: holdingWhile(read.exempt, live),
|
|
433
|
+
unanswered: read.unansweredHeld,
|
|
434
|
+
};
|
|
435
|
+
const result = sweepMatrix(read.matrix, swept);
|
|
436
|
+
const next = new Set(liveFilesAt(read.matrix, result, paths).map((one) => one.position.path));
|
|
437
|
+
if (next.size === live.size) {
|
|
438
|
+
return { swept, result };
|
|
439
|
+
}
|
|
440
|
+
live = next;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
277
443
|
/**
|
|
278
444
|
* The sweep of a run read once, under one set of marks. Where a question went unanswered,
|
|
279
445
|
* the run is swept once more without what that question could have kept live and without
|
|
@@ -282,8 +448,8 @@ function readRun(engine, host, scope, config, mode, detectors, extra) {
|
|
|
282
448
|
* every declaration that makes such a reference.
|
|
283
449
|
*/
|
|
284
450
|
function sweptOf(read, input) {
|
|
285
|
-
const
|
|
286
|
-
const
|
|
451
|
+
const { swept, result } = holdingSweep(read, input);
|
|
452
|
+
const exempt = swept.exempt ?? [];
|
|
287
453
|
const held = new Set(read.unansweredHeld);
|
|
288
454
|
if (held.size > 0) {
|
|
289
455
|
// What a declaration reads where the checker did not answer is unknown, so a rule
|
|
@@ -302,7 +468,7 @@ function sweptOf(read, input) {
|
|
|
302
468
|
})));
|
|
303
469
|
const bare = {
|
|
304
470
|
...swept,
|
|
305
|
-
exempt:
|
|
471
|
+
exempt: exempt.filter((record) => !isUnansweredRecord(record)),
|
|
306
472
|
unanswered: [],
|
|
307
473
|
};
|
|
308
474
|
for (const candidate of sweepMatrix(answered, bare).candidates) {
|
|
@@ -316,10 +482,13 @@ function sweptOf(read, input) {
|
|
|
316
482
|
return {
|
|
317
483
|
matrix: read.matrix,
|
|
318
484
|
sweep: result,
|
|
485
|
+
exempt,
|
|
319
486
|
retained: retainedIn(read.matrix, swept),
|
|
320
487
|
notBuilt: read.notBuilt,
|
|
321
488
|
unanswered: read.unanswered,
|
|
322
489
|
heldByUnanswered: [...held],
|
|
490
|
+
skipped: read.projects.flatMap((one) => one.skipped),
|
|
491
|
+
componentFiles: [...new Set(read.projects.flatMap((one) => one.componentFiles))].sort(compare),
|
|
323
492
|
};
|
|
324
493
|
}
|
|
325
494
|
/**
|
|
@@ -341,11 +510,11 @@ function readEmitterRun(engine, host, scope, config, mode, detectors) {
|
|
|
341
510
|
const run = readRun(engine, host, scope, config, mode, detectors, (project, read, references) => ({
|
|
342
511
|
deprecation: deprecatedDeclarations(project, read.held),
|
|
343
512
|
accessors: accessorsOf(project, read.held),
|
|
344
|
-
needs: projectNeeds(project, read.held, host),
|
|
513
|
+
needs: projectNeeds(project, read.held, host, targetRoot),
|
|
345
514
|
implementations: implementations(project, read.held),
|
|
346
515
|
heldByInclusion: heldByInclusion(project, targetRoot),
|
|
347
516
|
rooted: read.rooted,
|
|
348
|
-
directives: inlineDirectives(project, read.held, targetRoot),
|
|
517
|
+
directives: inlineDirectives(project, read.held, targetRoot).filter((directive) => !read.referenceOnly.includes(directive.site.path)),
|
|
349
518
|
declarationsNamed: declarationsNamed(project, read.held, configuredDeclarations(config)),
|
|
350
519
|
parts: projectParts(project, read.held, references, targetRoot),
|
|
351
520
|
}));
|
|
@@ -371,7 +540,7 @@ function emitterInputOver(host, scope, config, read, input, selfCheck) {
|
|
|
371
540
|
stores: storesOf(projects.map((one) => ({
|
|
372
541
|
references: one.configured.references,
|
|
373
542
|
accessors: one.extra.accessors,
|
|
374
|
-
})),
|
|
543
|
+
})), swept.exempt, input.mode),
|
|
375
544
|
dependencies: dependenciesOf(host, targetRoot, projects.map((one) => one.extra.needs), swept.sweep.candidates.map((candidate) => candidate.id)),
|
|
376
545
|
implementations: mergeImplementations(projects.map((one) => one.extra.implementations)),
|
|
377
546
|
files: {
|
|
@@ -387,7 +556,7 @@ function emitterInputOver(host, scope, config, read, input, selfCheck) {
|
|
|
387
556
|
edges: readEdgeSides(host, targetRoot),
|
|
388
557
|
},
|
|
389
558
|
selfCheck,
|
|
390
|
-
intraFunction: intraFunctionFacts(projects.map((one) => one.extra.parts), projects.flatMap((one) => one.configured.references), new Set([...
|
|
559
|
+
intraFunction: intraFunctionFacts(projects.map((one) => one.extra.parts), projects.flatMap((one) => one.configured.references), new Set([...swept.exempt.map((one) => one.id), ...swept.heldByUnanswered])),
|
|
391
560
|
};
|
|
392
561
|
}
|
|
393
562
|
/**
|
|
@@ -424,10 +593,18 @@ function readAnalysis(engine, host, scope, config, provenance, mode, detectors)
|
|
|
424
593
|
id: one.configured.configuration,
|
|
425
594
|
configFile: one.configFile,
|
|
426
595
|
})),
|
|
427
|
-
testFileRules: testFileRulesOf(config.ts.testFiles, read.projects.flatMap((one) => one.configured.testFiles)),
|
|
596
|
+
testFileRules: testFileRulesOf(config.ts.testFiles, read.projects.flatMap((one) => one.configured.testFiles), read.projects.flatMap((one) => one.configured.supportFiles ?? [])),
|
|
597
|
+
typeErrorSkips: read.projects.flatMap((one) => one.typeErrorSkips),
|
|
598
|
+
componentWarnings: [
|
|
599
|
+
...new Map(read.projects
|
|
600
|
+
.flatMap((one) => one.componentWarnings)
|
|
601
|
+
.map((one) => [`${one.path}:${String(one.line)}:${one.reason}`, one])).values(),
|
|
602
|
+
],
|
|
428
603
|
consumers: read.consumers,
|
|
429
604
|
notBuilt: read.notBuilt,
|
|
430
605
|
unanswered: read.unanswered,
|
|
606
|
+
componentsUnread: read.componentsUnread,
|
|
607
|
+
conventionsApplied: read.conventionsApplied,
|
|
431
608
|
},
|
|
432
609
|
configured: read.projects.map((one) => one.configured),
|
|
433
610
|
symbols,
|
|
@@ -475,6 +652,22 @@ export function runAnalysis(engine, host, scope, config, provenance, mode, detec
|
|
|
475
652
|
const inputs = reader.inputsUnder(readBaseline(host, scope.target.path, reader.symbols));
|
|
476
653
|
return analysisOf(reader, inputs);
|
|
477
654
|
}
|
|
655
|
+
/**
|
|
656
|
+
* One count per configuration of the run in which the checker left a question unanswered:
|
|
657
|
+
* the questions asked in its project, and the declarations of its own the run holds for them.
|
|
658
|
+
*/
|
|
659
|
+
export function unansweredCounts(analysis) {
|
|
660
|
+
const held = new Set(analysis.swept.heldByUnanswered);
|
|
661
|
+
return analysis.run.projects.flatMap((project) => {
|
|
662
|
+
const questions = analysis.run.unanswered.filter((one) => one.configFile === project.configFile).length;
|
|
663
|
+
if (questions === 0) {
|
|
664
|
+
return [];
|
|
665
|
+
}
|
|
666
|
+
const symbols = analysis.configured.find((one) => one.configuration === project.id)?.symbols;
|
|
667
|
+
const declarations = new Set((symbols ?? []).map((one) => one.id).filter((id) => held.has(id)));
|
|
668
|
+
return [{ configuration: project.id, questions, declarations: declarations.size }];
|
|
669
|
+
});
|
|
670
|
+
}
|
|
478
671
|
function analysisOf(reader, inputs) {
|
|
479
672
|
return {
|
|
480
673
|
result: findingsPass(inputs),
|