@cplieger/deadset-ts 4.0.1 → 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 +4 -4
- 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 +229 -36
- 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 +10 -3
- 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 +3 -0
- 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 +2 -0
- 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 +394 -70
- 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 +10 -6
- 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 +3 -0
- package/src/resolve.ts +59 -6
- package/src/roots.ts +60 -9
- package/src/run.ts +15 -5
- package/src/schema.ts +2 -0
- 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/src/analysis.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* project's checker, and that holds only for views the session itself hands out.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { lineOf, readComponent } from "./component-files.ts";
|
|
11
12
|
import type { Config, Provenance } from "./config.ts";
|
|
12
13
|
import {
|
|
13
14
|
consumerLoads,
|
|
@@ -33,12 +34,19 @@ import {
|
|
|
33
34
|
type LocatedDiagnostic,
|
|
34
35
|
type NotBuilt,
|
|
35
36
|
} from "./discover.ts";
|
|
37
|
+
import {
|
|
38
|
+
conventionFailuresFor,
|
|
39
|
+
readConventions,
|
|
40
|
+
type AppliedConvention,
|
|
41
|
+
type ConventionManifest,
|
|
42
|
+
} from "./conventions.ts";
|
|
36
43
|
import { readEdgeSides } from "./edges.ts";
|
|
37
44
|
import { enumGroup } from "./enum-group.ts";
|
|
38
45
|
import {
|
|
39
46
|
computeExemptions,
|
|
40
47
|
disabledClasses,
|
|
41
48
|
exemptionsOf,
|
|
49
|
+
holdingWhile,
|
|
42
50
|
isUnansweredRecord,
|
|
43
51
|
retainedIn,
|
|
44
52
|
type Detector,
|
|
@@ -63,6 +71,7 @@ import { intraFunctionFacts, projectParts, type ProjectParts } from "./intra-fun
|
|
|
63
71
|
import { inventory, type Inventory, type InventorySymbol } from "./inventory.ts";
|
|
64
72
|
import { readManifest } from "./manifest.ts";
|
|
65
73
|
import {
|
|
74
|
+
liveFilesAt,
|
|
66
75
|
matrixOf,
|
|
67
76
|
referenceKey,
|
|
68
77
|
sweepMatrix,
|
|
@@ -70,21 +79,32 @@ import {
|
|
|
70
79
|
type Matrix,
|
|
71
80
|
type SweepResult,
|
|
72
81
|
} from "./matrix.ts";
|
|
73
|
-
import { relativePath, resolvePath } from "./paths.ts";
|
|
82
|
+
import { joinPath, relativePath, resolvePath } from "./paths.ts";
|
|
74
83
|
import type { UnansweredQuestion } from "./query.ts";
|
|
75
|
-
import { byPosition, type Position } from "./position.ts";
|
|
84
|
+
import { byPosition, isComponentFile, type Position } from "./position.ts";
|
|
85
|
+
import type { TypeErrorSkip, UnansweredCount } from "./report.ts";
|
|
86
|
+
import { SetupError, setupLine, type SetupFailure } from "./setup-failure.ts";
|
|
87
|
+
import {
|
|
88
|
+
pathsOf,
|
|
89
|
+
readErrors,
|
|
90
|
+
skippedRoots,
|
|
91
|
+
skippedUnits,
|
|
92
|
+
type SkippedUnit,
|
|
93
|
+
} from "./type-errors.ts";
|
|
76
94
|
import { reflectiveLookup } from "./reflective-lookup.ts";
|
|
77
95
|
import { references, testFileRulesOf, type Reference, type TestFileRule } from "./references.ts";
|
|
78
96
|
import { roots, unmatchedEverywhere, type RootKind, type Roots } from "./roots.ts";
|
|
79
97
|
import type { Scope } from "./scope.ts";
|
|
80
98
|
import { serializationContract } from "./serialization-contract.ts";
|
|
81
|
-
import {
|
|
99
|
+
import { supportReferences, testSupportFiles } from "./test-support.ts";
|
|
100
|
+
import { diagnosticsOf, ownedAs, runSession, type Engine, type ProjectView } from "./session.ts";
|
|
82
101
|
import { accessorsOf, storesOf } from "./stores.ts";
|
|
83
102
|
import { isPartKind, type SuppressionRecord, type Suppressions, type Verdict } from "./suppress.ts";
|
|
84
103
|
import { bindRows, readBaseline, readIgnoreFile, type Recorded } from "./suppress-file.ts";
|
|
85
104
|
import { inlineDirectives, inlineSuppressions, type InlineDirective } from "./suppress-inline.ts";
|
|
86
105
|
import type { Exemption, Mode, SweepInput } from "./sweep.ts";
|
|
87
106
|
import { readTemplates, templateField } from "./template-field.ts";
|
|
107
|
+
import { ownErrors, workspaceRun } from "./workspace-run.ts";
|
|
88
108
|
|
|
89
109
|
/** The setting whose source decides where a finding about a configured root sits. */
|
|
90
110
|
const ROOTS_SETTING = "roots.patterns";
|
|
@@ -156,6 +176,22 @@ interface ProjectRead {
|
|
|
156
176
|
readonly configFile: string;
|
|
157
177
|
readonly held: Inventory;
|
|
158
178
|
readonly rooted: Roots;
|
|
179
|
+
/**
|
|
180
|
+
* The paths below the target root of the workspace-package files the project holds
|
|
181
|
+
* only for the references they make.
|
|
182
|
+
*/
|
|
183
|
+
readonly referenceOnly: readonly string[];
|
|
184
|
+
/** Every type error of the project's own files that skipped a unit. */
|
|
185
|
+
readonly typeErrorSkips: readonly TypeErrorSkip[];
|
|
186
|
+
/** The lines each of those skips withholds every finding on. */
|
|
187
|
+
readonly skipped: readonly SkippedUnit[];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** One target project the run analyzes, as its stage is handed it. */
|
|
191
|
+
interface OpenedProject {
|
|
192
|
+
readonly configuration: string;
|
|
193
|
+
/** Enumerates and roots the project; called only inside the stage it was handed to. */
|
|
194
|
+
readonly read: () => ProjectRead;
|
|
159
195
|
}
|
|
160
196
|
|
|
161
197
|
/** What a run read of the consumers its scope declares. */
|
|
@@ -174,22 +210,48 @@ interface ReadProjects<Answer> {
|
|
|
174
210
|
readonly notBuilt: readonly NotBuilt[];
|
|
175
211
|
/** Every question the checker could not answer, each once. */
|
|
176
212
|
readonly unanswered: readonly UnansweredQuestion[];
|
|
213
|
+
/** Per configuration whose component files the run cannot read, the reason. */
|
|
214
|
+
readonly componentsUnread: readonly ComponentsUnread[];
|
|
215
|
+
/** The convention rows the run applied. */
|
|
216
|
+
readonly conventionsApplied: readonly AppliedConvention[];
|
|
177
217
|
}
|
|
178
218
|
|
|
179
219
|
/**
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
220
|
+
* The manifests the analysis reads, each once: the target's own, then every workspace
|
|
221
|
+
* member's below the target root.
|
|
222
|
+
*/
|
|
223
|
+
function conventionManifests(
|
|
224
|
+
host: Host,
|
|
225
|
+
targetRoot: string,
|
|
226
|
+
memberDirs: readonly string[],
|
|
227
|
+
): readonly ConventionManifest[] {
|
|
228
|
+
const found = new Map<string, ConventionManifest>();
|
|
229
|
+
if (host.kindOf(joinPath(targetRoot, "package.json")) === "file") {
|
|
230
|
+
found.set(targetRoot, { dir: targetRoot, path: "package.json" });
|
|
231
|
+
}
|
|
232
|
+
for (const dir of memberDirs) {
|
|
233
|
+
const below = relativePath(targetRoot, dir);
|
|
234
|
+
if (below !== undefined && !found.has(dir)) {
|
|
235
|
+
found.set(dir, { dir, path: `${below}/package.json` });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return [...found.values()];
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Reads every project the scope discovers, and each declared consumer's, in one snapshot,
|
|
243
|
+
* each checked for errors first. `stage` is handed a target project and enumerates and roots
|
|
244
|
+
* it if it reads it; a consumer project is read for its references to the target. A type error
|
|
245
|
+
* in an own file skips the unit holding it. A guess that does not load or meets a setup failure
|
|
246
|
+
* is dropped into `notBuilt` (see {@link isGuess}); any other such project fails the run, as
|
|
247
|
+
* does a run whose every guess was dropped.
|
|
186
248
|
*/
|
|
187
249
|
function readProjects<Answer>(
|
|
188
250
|
engine: Engine,
|
|
189
251
|
host: Host,
|
|
190
252
|
scope: Scope,
|
|
191
253
|
config: Config,
|
|
192
|
-
stage: <Brand>(project: ProjectView<Brand>,
|
|
254
|
+
stage: <Brand>(project: ProjectView<Brand>, opened: OpenedProject) => Answer,
|
|
193
255
|
): ReadProjects<Answer> {
|
|
194
256
|
const targetRoot = scope.target.path;
|
|
195
257
|
const discovered = discoverProjects(engine, host, scope, config.analysis.configurations);
|
|
@@ -200,14 +262,30 @@ function readProjects<Answer>(
|
|
|
200
262
|
load.configFiles.map((configFile) => [configFile, load.consumer] as const),
|
|
201
263
|
),
|
|
202
264
|
);
|
|
265
|
+
const absoluteRoot = resolvePath(host.workingDirectory(), targetRoot);
|
|
266
|
+
const workspace = workspaceRun(
|
|
267
|
+
engine,
|
|
268
|
+
host,
|
|
269
|
+
absoluteRoot,
|
|
270
|
+
discovered.configFiles,
|
|
271
|
+
readManifest(host, targetRoot),
|
|
272
|
+
);
|
|
273
|
+
const conventions = readConventions(
|
|
274
|
+
host,
|
|
275
|
+
(fileName, text) => engine.parseSourceFile(fileName, text),
|
|
276
|
+
conventionManifests(host, absoluteRoot, workspace?.memberDirs ?? []),
|
|
277
|
+
config.ts.disabledConventions,
|
|
278
|
+
);
|
|
203
279
|
const options = {
|
|
204
|
-
|
|
280
|
+
host,
|
|
281
|
+
manifest: workspace?.manifest ?? readManifest(host, targetRoot),
|
|
282
|
+
workspaceEntries: workspace?.entries,
|
|
205
283
|
patterns: config.rootPatterns,
|
|
206
284
|
entryFiles: config.ts.entryFiles,
|
|
285
|
+
conventions,
|
|
207
286
|
testFiles: config.ts.testFiles,
|
|
208
287
|
publishedAPI: config.targetKind === "library",
|
|
209
288
|
};
|
|
210
|
-
const absoluteRoot = resolvePath(host.workingDirectory(), targetRoot);
|
|
211
289
|
const notBuilt: NotBuilt[] = [...discovered.notBuilt];
|
|
212
290
|
const dropped = (configFile: string, error: string): void => {
|
|
213
291
|
notBuilt.push({
|
|
@@ -217,13 +295,42 @@ function readProjects<Answer>(
|
|
|
217
295
|
});
|
|
218
296
|
};
|
|
219
297
|
const failures: LocatedDiagnostic[] = [];
|
|
298
|
+
const unbuildable: SetupFailure[] = [];
|
|
299
|
+
const setupDropped: SetupFailure[] = [];
|
|
220
300
|
const consumed = new Map<string, Reference>();
|
|
301
|
+
const setupMet = (configFile: string, met: readonly SetupFailure[]): void => {
|
|
302
|
+
const [first] = met;
|
|
303
|
+
if (first !== undefined && isGuess(discovered, configFile)) {
|
|
304
|
+
dropped(configFile, setupLine(first));
|
|
305
|
+
setupDropped.push(...met);
|
|
306
|
+
} else {
|
|
307
|
+
unbuildable.push(...met);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
221
310
|
const session = runSession(
|
|
222
311
|
engine,
|
|
223
312
|
[...discovered.configFiles, ...consumerOf.keys()],
|
|
224
|
-
(
|
|
225
|
-
const
|
|
226
|
-
const
|
|
313
|
+
(opened) => {
|
|
314
|
+
const consumer = consumerOf.get(opened.configFile);
|
|
315
|
+
const setup = consumer === undefined ? workspace?.unbuildable(opened.configFile) : undefined;
|
|
316
|
+
if (setup !== undefined) {
|
|
317
|
+
setupMet(opened.configFile, [
|
|
318
|
+
{ setupClass: "workspace-member-without-source", detail: setup },
|
|
319
|
+
]);
|
|
320
|
+
return undefined;
|
|
321
|
+
}
|
|
322
|
+
const referenceOnly =
|
|
323
|
+
workspace === undefined || consumer !== undefined
|
|
324
|
+
? () => false
|
|
325
|
+
: workspace.referenceOnly(opened.configFile, opened.rootFiles);
|
|
326
|
+
const project =
|
|
327
|
+
workspace === undefined || consumer !== undefined
|
|
328
|
+
? opened
|
|
329
|
+
: ownedAs(opened, (file, fromExternalLibrary) =>
|
|
330
|
+
workspace.ownFile(file, fromExternalLibrary),
|
|
331
|
+
);
|
|
332
|
+
const raw = projectErrors(diagnosticsOf(project));
|
|
333
|
+
const errors = { load: raw.load, check: ownErrors(raw.check, referenceOnly) };
|
|
227
334
|
const [unloaded] = errors.load;
|
|
228
335
|
if (
|
|
229
336
|
unloaded !== undefined &&
|
|
@@ -236,8 +343,45 @@ function readProjects<Answer>(
|
|
|
236
343
|
);
|
|
237
344
|
return undefined;
|
|
238
345
|
}
|
|
239
|
-
if (errors.load.length > 0
|
|
240
|
-
failures.push(...errors.load
|
|
346
|
+
if (errors.load.length > 0) {
|
|
347
|
+
failures.push(...errors.load);
|
|
348
|
+
return undefined;
|
|
349
|
+
}
|
|
350
|
+
const reading = readErrors(project, errors.check, {
|
|
351
|
+
host,
|
|
352
|
+
targetRoot:
|
|
353
|
+
consumer === undefined
|
|
354
|
+
? absoluteRoot
|
|
355
|
+
: resolvePath(host.workingDirectory(), consumer.path),
|
|
356
|
+
paths: pathsOf(engine, project.configFile),
|
|
357
|
+
});
|
|
358
|
+
if (reading.failing.length > 0) {
|
|
359
|
+
failures.push(...reading.failing);
|
|
360
|
+
return undefined;
|
|
361
|
+
}
|
|
362
|
+
if (consumer !== undefined && reading.missing.length > 0) {
|
|
363
|
+
unbuildable.push(
|
|
364
|
+
...reading.missing.map((one) => ({
|
|
365
|
+
setupClass: "missing-consumer" as const,
|
|
366
|
+
detail: `the consumer ${consumer.path} does not build, its dependencies not installed: ${one.detail}`,
|
|
367
|
+
})),
|
|
368
|
+
);
|
|
369
|
+
return undefined;
|
|
370
|
+
}
|
|
371
|
+
if (reading.missing.length > 0) {
|
|
372
|
+
setupMet(project.configFile, reading.missing);
|
|
373
|
+
return undefined;
|
|
374
|
+
}
|
|
375
|
+
const unconventional =
|
|
376
|
+
consumer === undefined
|
|
377
|
+
? conventionFailuresFor(
|
|
378
|
+
conventions,
|
|
379
|
+
project.configFile,
|
|
380
|
+
project.ownSourceFiles().map((file) => file.fileName),
|
|
381
|
+
)
|
|
382
|
+
: [];
|
|
383
|
+
if (unconventional.length > 0) {
|
|
384
|
+
setupMet(project.configFile, unconventional);
|
|
241
385
|
return undefined;
|
|
242
386
|
}
|
|
243
387
|
if (consumer !== undefined) {
|
|
@@ -255,12 +399,38 @@ function readProjects<Answer>(
|
|
|
255
399
|
}
|
|
256
400
|
return undefined;
|
|
257
401
|
}
|
|
258
|
-
const held = inventory(project, host, targetRoot);
|
|
259
|
-
const rooted = roots(project, held, targetRoot, options);
|
|
260
402
|
const configuration = ids.get(project.configFile) ?? project.configFile;
|
|
261
|
-
|
|
403
|
+
const read = (): ProjectRead => {
|
|
404
|
+
const held = inventory(project, host, targetRoot);
|
|
405
|
+
const found = roots(project, held, targetRoot, options);
|
|
406
|
+
const kept = skippedRoots(project, held, reading.skips);
|
|
407
|
+
const rooted =
|
|
408
|
+
kept.length === 0
|
|
409
|
+
? found
|
|
410
|
+
: {
|
|
411
|
+
...found,
|
|
412
|
+
liveUnderReachability: [
|
|
413
|
+
...found.liveUnderReachability,
|
|
414
|
+
...kept.map((id) => ({ id, kind: "type-error" as const, source: "" })),
|
|
415
|
+
],
|
|
416
|
+
};
|
|
417
|
+
return {
|
|
418
|
+
configuration,
|
|
419
|
+
configFile: project.configFile,
|
|
420
|
+
held,
|
|
421
|
+
rooted,
|
|
422
|
+
typeErrorSkips: reading.skips.map((one) => one.record),
|
|
423
|
+
skipped: skippedUnits(reading.skips, absoluteRoot),
|
|
424
|
+
referenceOnly: project
|
|
425
|
+
.ownSourceFiles()
|
|
426
|
+
.filter((file) => referenceOnly(file.fileName))
|
|
427
|
+
.flatMap((file) => relativePath(absoluteRoot, file.fileName) ?? []),
|
|
428
|
+
};
|
|
429
|
+
};
|
|
430
|
+
return stage(project, { configuration, read });
|
|
262
431
|
},
|
|
263
432
|
discovered.derived ? "record" : "refuse",
|
|
433
|
+
workspace?.programs,
|
|
264
434
|
);
|
|
265
435
|
for (const configFile of session.unopened) {
|
|
266
436
|
if (consumerOf.has(configFile) || !isGuess(discovered, configFile)) {
|
|
@@ -268,6 +438,9 @@ function readProjects<Answer>(
|
|
|
268
438
|
}
|
|
269
439
|
dropped(configFile, "the compiler opened no project for the configuration");
|
|
270
440
|
}
|
|
441
|
+
if (unbuildable.length > 0) {
|
|
442
|
+
throw new SetupError(unbuildable);
|
|
443
|
+
}
|
|
271
444
|
if (failures.length > 0) {
|
|
272
445
|
throw new DiscoveryError(
|
|
273
446
|
`${String(failures.length)} error(s), so no answer was produced`,
|
|
@@ -276,6 +449,9 @@ function readProjects<Answer>(
|
|
|
276
449
|
}
|
|
277
450
|
const answers = session.projects.filter((answer): answer is Answer => answer !== undefined);
|
|
278
451
|
const [first] = notBuilt;
|
|
452
|
+
if (answers.length === 0 && setupDropped.length > 0) {
|
|
453
|
+
throw new SetupError(setupDropped);
|
|
454
|
+
}
|
|
279
455
|
if (answers.length === 0 && first !== undefined) {
|
|
280
456
|
throw new DiscoveryError(
|
|
281
457
|
`no configuration discovery derived could be built; ${first.id}: ${first.error}`,
|
|
@@ -290,9 +466,37 @@ function readProjects<Answer>(
|
|
|
290
466
|
},
|
|
291
467
|
notBuilt,
|
|
292
468
|
unanswered: session.unanswered,
|
|
469
|
+
componentsUnread: workspace?.componentsUnread ?? [],
|
|
470
|
+
conventionsApplied: conventions.applied,
|
|
293
471
|
};
|
|
294
472
|
}
|
|
295
473
|
|
|
474
|
+
/** The projects one run analyzes, and what reading them left out. */
|
|
475
|
+
interface RunProjects {
|
|
476
|
+
/** Each configuration the run analyzes, by the name discovery gives its project, in discovery order. */
|
|
477
|
+
readonly configurations: readonly string[];
|
|
478
|
+
/** The derived configurations the run dropped. */
|
|
479
|
+
readonly notBuilt: readonly NotBuilt[];
|
|
480
|
+
/** Every question the checker could not answer, each once. */
|
|
481
|
+
readonly unanswered: readonly UnansweredQuestion[];
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* The projects the run the scope and the configuration describe analyzes, each read
|
|
486
|
+
* for the errors and setup failures that decide whether it is analyzed, dropped or
|
|
487
|
+
* fails the run, exactly as the analysis reads them.
|
|
488
|
+
*/
|
|
489
|
+
export function runProjects(engine: Engine, host: Host, scope: Scope, config: Config): RunProjects {
|
|
490
|
+
const read = readProjects(
|
|
491
|
+
engine,
|
|
492
|
+
host,
|
|
493
|
+
scope,
|
|
494
|
+
config,
|
|
495
|
+
(_project, opened) => opened.configuration,
|
|
496
|
+
);
|
|
497
|
+
return { configurations: read.answers, notBuilt: read.notBuilt, unanswered: read.unanswered };
|
|
498
|
+
}
|
|
499
|
+
|
|
296
500
|
/**
|
|
297
501
|
* The root set of the run the scope and the configuration describe.
|
|
298
502
|
*
|
|
@@ -307,7 +511,7 @@ export function runRoots(
|
|
|
307
511
|
provenance: Provenance,
|
|
308
512
|
): RunRoots {
|
|
309
513
|
const targetRoot = scope.target.path;
|
|
310
|
-
const read = readProjects(engine, host, scope, config, (_project,
|
|
514
|
+
const read = readProjects(engine, host, scope, config, (_project, opened) => opened.read());
|
|
311
515
|
const projects = read.answers;
|
|
312
516
|
|
|
313
517
|
const configurations: string[] = [];
|
|
@@ -361,6 +565,8 @@ export function runRoots(
|
|
|
361
565
|
export interface RunSweep {
|
|
362
566
|
readonly matrix: Matrix;
|
|
363
567
|
readonly sweep: SweepResult;
|
|
568
|
+
/** The exemption records that hold under the sweep. */
|
|
569
|
+
readonly exempt: readonly Exemption[];
|
|
364
570
|
/**
|
|
365
571
|
* The exemption records that held back a declaration some configuration of the run
|
|
366
572
|
* would otherwise judge dead, in the declarations' site order.
|
|
@@ -375,6 +581,18 @@ export interface RunSweep {
|
|
|
375
581
|
* such a declaration keeps live. The run reports none of them.
|
|
376
582
|
*/
|
|
377
583
|
readonly heldByUnanswered: readonly string[];
|
|
584
|
+
/** The lines of the units type errors skipped, on which the run reports nothing. */
|
|
585
|
+
readonly skipped: readonly SkippedUnit[];
|
|
586
|
+
/** The component files the run read, by path below the target root, each once. */
|
|
587
|
+
readonly componentFiles: readonly string[];
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/** One `<script` start tag of a component file the run read no block for. */
|
|
591
|
+
export interface ComponentWarning {
|
|
592
|
+
/** The file, below the target root. */
|
|
593
|
+
readonly path: string;
|
|
594
|
+
readonly line: number;
|
|
595
|
+
readonly reason: string;
|
|
378
596
|
}
|
|
379
597
|
|
|
380
598
|
/** The exemption classes this analyzer detects, each by its detector; any other retains nothing. */
|
|
@@ -396,6 +614,11 @@ interface SweptProject<Extra> {
|
|
|
396
614
|
readonly exempt: readonly Exemption[];
|
|
397
615
|
/** The declarations of the project an unanswered question could have kept live. */
|
|
398
616
|
readonly unanswered: readonly string[];
|
|
617
|
+
readonly typeErrorSkips: readonly TypeErrorSkip[];
|
|
618
|
+
readonly skipped: readonly SkippedUnit[];
|
|
619
|
+
/** The component files the project holds of its own, by path below the target root. */
|
|
620
|
+
readonly componentFiles: readonly string[];
|
|
621
|
+
readonly componentWarnings: readonly ComponentWarning[];
|
|
399
622
|
readonly extra: Extra;
|
|
400
623
|
}
|
|
401
624
|
|
|
@@ -412,6 +635,10 @@ interface ReadRun<Extra> {
|
|
|
412
635
|
readonly notBuilt: readonly NotBuilt[];
|
|
413
636
|
/** Every question the checker could not answer, each once. */
|
|
414
637
|
readonly unanswered: readonly UnansweredQuestion[];
|
|
638
|
+
/** Per configuration whose component files the run cannot read, the reason. */
|
|
639
|
+
readonly componentsUnread: readonly ComponentsUnread[];
|
|
640
|
+
/** The convention rows the run applied. */
|
|
641
|
+
readonly conventionsApplied: readonly AppliedConvention[];
|
|
415
642
|
}
|
|
416
643
|
|
|
417
644
|
/**
|
|
@@ -439,48 +666,67 @@ function readRun<Extra>(
|
|
|
439
666
|
? { delimiters: config.analysis.templateDelimiters, files: [] }
|
|
440
667
|
: readTemplates(host, targetRoot, config.analysis);
|
|
441
668
|
const consumers = scope.consumers.map((consumer) => consumer.path);
|
|
442
|
-
const read = readProjects<SweptProject<Extra>>(
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
669
|
+
const read = readProjects<SweptProject<Extra>>(engine, host, scope, config, (project, opened) => {
|
|
670
|
+
const projectRead = opened.read();
|
|
671
|
+
const resolved = references(project, projectRead.held, targetRoot, {
|
|
672
|
+
testFiles: config.ts.testFiles,
|
|
673
|
+
});
|
|
674
|
+
const support = testSupportFiles(
|
|
675
|
+
projectRead.held.symbols,
|
|
676
|
+
resolved.references,
|
|
677
|
+
projectRead.rooted.liveUnderReachability,
|
|
678
|
+
resolved.testFilePaths,
|
|
679
|
+
);
|
|
680
|
+
const made = supportReferences(resolved.references, support);
|
|
681
|
+
const configured: Configured = {
|
|
682
|
+
configuration: projectRead.configuration,
|
|
683
|
+
symbols: projectRead.held.symbols,
|
|
684
|
+
references: made,
|
|
685
|
+
roots: projectRead.rooted.liveUnderReachability,
|
|
686
|
+
testFiles: resolved.testFilePaths,
|
|
687
|
+
...(support.size === 0 ? {} : { supportFiles: [...support].sort() }),
|
|
688
|
+
...(projectRead.referenceOnly.length === 0
|
|
689
|
+
? {}
|
|
690
|
+
: { referenceOnly: projectRead.referenceOnly }),
|
|
691
|
+
};
|
|
692
|
+
const exempt = computeExemptions(
|
|
693
|
+
{ project, held: projectRead.held, targetRoot, templates, ts: config.ts, consumers },
|
|
694
|
+
detectors,
|
|
695
|
+
{
|
|
696
|
+
disabled,
|
|
697
|
+
mode,
|
|
698
|
+
testFiles: new Set([...resolved.testFilePaths, ...support]),
|
|
699
|
+
},
|
|
700
|
+
);
|
|
701
|
+
const unanswered = [
|
|
702
|
+
...projectRead.held.unanswered,
|
|
703
|
+
...projectRead.rooted.unanswered,
|
|
704
|
+
...made
|
|
705
|
+
.filter((reference) => reference.resolution === "by-name")
|
|
706
|
+
.map((reference) => reference.to),
|
|
707
|
+
...exempt.filter(isUnansweredRecord).map((record) => record.id),
|
|
708
|
+
];
|
|
709
|
+
const components = project.ownSourceFiles().filter(isComponentFile);
|
|
710
|
+
const pathOf = (file: (typeof components)[number]): string =>
|
|
711
|
+
relativePath(targetRoot, file.fileName) ?? file.fileName;
|
|
712
|
+
return {
|
|
713
|
+
configFile: projectRead.configFile,
|
|
714
|
+
configured,
|
|
715
|
+
exempt,
|
|
716
|
+
unanswered,
|
|
717
|
+
typeErrorSkips: projectRead.typeErrorSkips,
|
|
718
|
+
skipped: projectRead.skipped,
|
|
719
|
+
componentFiles: components.map(pathOf),
|
|
720
|
+
componentWarnings: components.flatMap((file) =>
|
|
721
|
+
readComponent(file.originalText).warnings.map((warning) => ({
|
|
722
|
+
path: pathOf(file),
|
|
723
|
+
line: lineOf(file.originalText, warning.offset),
|
|
724
|
+
reason: warning.reason,
|
|
725
|
+
})),
|
|
726
|
+
),
|
|
727
|
+
extra: extra(project, projectRead, made),
|
|
728
|
+
};
|
|
729
|
+
});
|
|
484
730
|
const consumed = read.consumers.references;
|
|
485
731
|
const projects = read.answers.map((one) =>
|
|
486
732
|
consumed.length === 0
|
|
@@ -506,9 +752,38 @@ function readRun<Extra>(
|
|
|
506
752
|
consumers: read.consumers.loaded,
|
|
507
753
|
notBuilt: read.notBuilt,
|
|
508
754
|
unanswered: read.unanswered,
|
|
755
|
+
componentsUnread: read.componentsUnread,
|
|
756
|
+
conventionsApplied: read.conventionsApplied,
|
|
509
757
|
};
|
|
510
758
|
}
|
|
511
759
|
|
|
760
|
+
/**
|
|
761
|
+
* The run swept under one set of marks with the records that hold. A record a component
|
|
762
|
+
* file's markup holds counts only while the file is live, and what it holds may be what
|
|
763
|
+
* keeps the file live, so each file's markup joins only once a sweep without it finds the
|
|
764
|
+
* file live, until a sweep finds no further file live.
|
|
765
|
+
*/
|
|
766
|
+
function holdingSweep<Extra>(
|
|
767
|
+
read: ReadRun<Extra>,
|
|
768
|
+
input: Pick<SweepInput, "marked" | "mode">,
|
|
769
|
+
): { readonly swept: SweepInput; readonly result: SweepResult } {
|
|
770
|
+
const paths = new Set(read.exempt.flatMap((record) => record.whileLive ?? []));
|
|
771
|
+
let live = new Set<string>();
|
|
772
|
+
for (;;) {
|
|
773
|
+
const swept: SweepInput = {
|
|
774
|
+
...input,
|
|
775
|
+
exempt: holdingWhile(read.exempt, live),
|
|
776
|
+
unanswered: read.unansweredHeld,
|
|
777
|
+
};
|
|
778
|
+
const result = sweepMatrix(read.matrix, swept);
|
|
779
|
+
const next = new Set(liveFilesAt(read.matrix, result, paths).map((one) => one.position.path));
|
|
780
|
+
if (next.size === live.size) {
|
|
781
|
+
return { swept, result };
|
|
782
|
+
}
|
|
783
|
+
live = next;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
512
787
|
/**
|
|
513
788
|
* The sweep of a run read once, under one set of marks. Where a question went unanswered,
|
|
514
789
|
* the run is swept once more without what that question could have kept live and without
|
|
@@ -520,8 +795,8 @@ function sweptOf<Extra>(
|
|
|
520
795
|
read: ReadRun<Extra>,
|
|
521
796
|
input: Pick<SweepInput, "marked" | "mode">,
|
|
522
797
|
): RunSweep {
|
|
523
|
-
const
|
|
524
|
-
const
|
|
798
|
+
const { swept, result } = holdingSweep(read, input);
|
|
799
|
+
const exempt = swept.exempt ?? [];
|
|
525
800
|
const held = new Set(read.unansweredHeld);
|
|
526
801
|
if (held.size > 0) {
|
|
527
802
|
// What a declaration reads where the checker did not answer is unknown, so a rule
|
|
@@ -542,7 +817,7 @@ function sweptOf<Extra>(
|
|
|
542
817
|
);
|
|
543
818
|
const bare: SweepInput = {
|
|
544
819
|
...swept,
|
|
545
|
-
exempt:
|
|
820
|
+
exempt: exempt.filter((record) => !isUnansweredRecord(record)),
|
|
546
821
|
unanswered: [],
|
|
547
822
|
};
|
|
548
823
|
for (const candidate of sweepMatrix(answered, bare).candidates) {
|
|
@@ -558,10 +833,13 @@ function sweptOf<Extra>(
|
|
|
558
833
|
return {
|
|
559
834
|
matrix: read.matrix,
|
|
560
835
|
sweep: result,
|
|
836
|
+
exempt,
|
|
561
837
|
retained: retainedIn(read.matrix, swept),
|
|
562
838
|
notBuilt: read.notBuilt,
|
|
563
839
|
unanswered: read.unanswered,
|
|
564
840
|
heldByUnanswered: [...held],
|
|
841
|
+
skipped: read.projects.flatMap((one) => one.skipped),
|
|
842
|
+
componentFiles: [...new Set(read.projects.flatMap((one) => one.componentFiles))].sort(compare),
|
|
565
843
|
};
|
|
566
844
|
}
|
|
567
845
|
|
|
@@ -622,11 +900,13 @@ function readEmitterRun(
|
|
|
622
900
|
(project, read, references) => ({
|
|
623
901
|
deprecation: deprecatedDeclarations(project, read.held),
|
|
624
902
|
accessors: accessorsOf(project, read.held),
|
|
625
|
-
needs: projectNeeds(project, read.held, host),
|
|
903
|
+
needs: projectNeeds(project, read.held, host, targetRoot),
|
|
626
904
|
implementations: implementations(project, read.held),
|
|
627
905
|
heldByInclusion: heldByInclusion(project, targetRoot),
|
|
628
906
|
rooted: read.rooted,
|
|
629
|
-
directives: inlineDirectives(project, read.held, targetRoot)
|
|
907
|
+
directives: inlineDirectives(project, read.held, targetRoot).filter(
|
|
908
|
+
(directive) => !read.referenceOnly.includes(directive.site.path),
|
|
909
|
+
),
|
|
630
910
|
declarationsNamed: declarationsNamed(project, read.held, configuredDeclarations(config)),
|
|
631
911
|
parts: projectParts(project, read.held, references, targetRoot),
|
|
632
912
|
}),
|
|
@@ -663,7 +943,7 @@ function emitterInputOver(
|
|
|
663
943
|
references: one.configured.references,
|
|
664
944
|
accessors: one.extra.accessors,
|
|
665
945
|
})),
|
|
666
|
-
|
|
946
|
+
swept.exempt,
|
|
667
947
|
input.mode,
|
|
668
948
|
),
|
|
669
949
|
dependencies: dependenciesOf(
|
|
@@ -689,7 +969,7 @@ function emitterInputOver(
|
|
|
689
969
|
intraFunction: intraFunctionFacts(
|
|
690
970
|
projects.map((one) => one.extra.parts),
|
|
691
971
|
projects.flatMap((one) => one.configured.references),
|
|
692
|
-
new Set([...
|
|
972
|
+
new Set([...swept.exempt.map((one) => one.id), ...swept.heldByUnanswered]),
|
|
693
973
|
),
|
|
694
974
|
};
|
|
695
975
|
}
|
|
@@ -768,10 +1048,21 @@ function readAnalysis(
|
|
|
768
1048
|
testFileRules: testFileRulesOf(
|
|
769
1049
|
config.ts.testFiles,
|
|
770
1050
|
read.projects.flatMap((one) => one.configured.testFiles),
|
|
1051
|
+
read.projects.flatMap((one) => one.configured.supportFiles ?? []),
|
|
771
1052
|
),
|
|
1053
|
+
typeErrorSkips: read.projects.flatMap((one) => one.typeErrorSkips),
|
|
1054
|
+
componentWarnings: [
|
|
1055
|
+
...new Map(
|
|
1056
|
+
read.projects
|
|
1057
|
+
.flatMap((one) => one.componentWarnings)
|
|
1058
|
+
.map((one) => [`${one.path}:${String(one.line)}:${one.reason}`, one] as const),
|
|
1059
|
+
).values(),
|
|
1060
|
+
],
|
|
772
1061
|
consumers: read.consumers,
|
|
773
1062
|
notBuilt: read.notBuilt,
|
|
774
1063
|
unanswered: read.unanswered,
|
|
1064
|
+
componentsUnread: read.componentsUnread,
|
|
1065
|
+
conventionsApplied: read.conventionsApplied,
|
|
775
1066
|
},
|
|
776
1067
|
configured: read.projects.map((one) => one.configured),
|
|
777
1068
|
symbols,
|
|
@@ -842,6 +1133,20 @@ export interface RunFacts {
|
|
|
842
1133
|
readonly notBuilt: readonly NotBuilt[];
|
|
843
1134
|
/** Every question the checker could not answer, each once. */
|
|
844
1135
|
readonly unanswered: readonly UnansweredQuestion[];
|
|
1136
|
+
/** Every type error that skipped a unit, in the run's order. */
|
|
1137
|
+
readonly typeErrorSkips: readonly TypeErrorSkip[];
|
|
1138
|
+
/** Every `<script` start tag a component file holds that no block was read for, each once. */
|
|
1139
|
+
readonly componentWarnings: readonly ComponentWarning[];
|
|
1140
|
+
/** Per configuration whose component files the run cannot read, the reason. */
|
|
1141
|
+
readonly componentsUnread: readonly ComponentsUnread[];
|
|
1142
|
+
/** The convention rows the run applied. */
|
|
1143
|
+
readonly conventionsApplied: readonly AppliedConvention[];
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
/** One configuration whose component files a run cannot read. */
|
|
1147
|
+
export interface ComponentsUnread {
|
|
1148
|
+
readonly configFile: string;
|
|
1149
|
+
readonly reason: string;
|
|
845
1150
|
}
|
|
846
1151
|
|
|
847
1152
|
/** The findings of one run, beside what a report states about the run itself. */
|
|
@@ -874,6 +1179,25 @@ export function runAnalysis(
|
|
|
874
1179
|
return analysisOf(reader, inputs);
|
|
875
1180
|
}
|
|
876
1181
|
|
|
1182
|
+
/**
|
|
1183
|
+
* One count per configuration of the run in which the checker left a question unanswered:
|
|
1184
|
+
* the questions asked in its project, and the declarations of its own the run holds for them.
|
|
1185
|
+
*/
|
|
1186
|
+
export function unansweredCounts(analysis: RunAnalysis): readonly UnansweredCount[] {
|
|
1187
|
+
const held = new Set(analysis.swept.heldByUnanswered);
|
|
1188
|
+
return analysis.run.projects.flatMap((project) => {
|
|
1189
|
+
const questions = analysis.run.unanswered.filter(
|
|
1190
|
+
(one) => one.configFile === project.configFile,
|
|
1191
|
+
).length;
|
|
1192
|
+
if (questions === 0) {
|
|
1193
|
+
return [];
|
|
1194
|
+
}
|
|
1195
|
+
const symbols = analysis.configured.find((one) => one.configuration === project.id)?.symbols;
|
|
1196
|
+
const declarations = new Set((symbols ?? []).map((one) => one.id).filter((id) => held.has(id)));
|
|
1197
|
+
return [{ configuration: project.id, questions, declarations: declarations.size }];
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
|
|
877
1201
|
function analysisOf(reader: AnalysisReader, inputs: AnalysisInputs): RunAnalysis {
|
|
878
1202
|
return {
|
|
879
1203
|
result: findingsPass(inputs),
|