@cplieger/deadset-ts 8.0.0-dev.7 → 8.0.0-dev.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/node-host.js +21 -3
- package/dist/src/analysis.js +13 -14
- package/dist/src/config.js +6 -83
- package/dist/src/configured-declarations.js +15 -20
- package/dist/src/consumers.js +2 -2
- package/dist/src/contract/config.generated.js +544 -0
- package/dist/src/contract/report.generated.js +3 -2
- package/dist/src/discover.js +1 -1
- package/dist/src/edges.js +36 -112
- package/dist/src/exempt-classes.js +0 -2
- package/dist/src/exempt.js +1 -1
- package/dist/src/findings/completion.js +5 -4
- package/dist/src/findings/emitters.js +1 -1
- package/dist/src/findings/intra-function.js +1 -1
- package/dist/src/findings/non-code-artifacts.js +2 -2
- package/dist/src/findings/unused-declarations.js +1 -1
- package/dist/src/findings/visibility-narrowing.js +2 -2
- package/dist/src/findings-pass.js +1 -1
- package/dist/src/framework-lifecycle.js +7 -7
- package/dist/src/fs/document.js +59 -0
- package/dist/src/injection-container.js +1 -1
- package/dist/src/json-document.js +194 -67
- package/dist/src/kinds.js +0 -7
- package/dist/src/print.js +10 -79
- package/dist/src/report-schema.js +3 -224
- package/dist/src/resolve.js +245 -715
- package/dist/src/run.js +48 -55
- package/dist/src/schema.js +320 -344
- package/dist/src/scope.js +33 -101
- package/dist/src/serialization-contract.js +1 -1
- package/dist/src/suppress-file.js +15 -17
- package/dist/src/suppress.js +3 -3
- package/dist/src/template-field.js +4 -4
- package/dist/src/usage.js +10 -0
- package/dist/src/verbs/analyze.js +12 -15
- package/dist/src/verbs/explain.js +3 -3
- package/dist/src/verbs/print-projects.js +2 -2
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/analysis.ts +30 -26
- package/src/config.ts +31 -298
- package/src/configured-declarations.ts +18 -23
- package/src/consumers.ts +4 -4
- package/src/contract/config.generated.ts +798 -0
- package/src/contract/report.generated.ts +3 -2
- package/src/discover.ts +1 -1
- package/src/edges.ts +39 -124
- package/src/exempt-classes.ts +0 -5
- package/src/exempt.ts +4 -4
- package/src/findings/completion.ts +5 -6
- package/src/findings/emitter.ts +2 -2
- package/src/findings/emitters.ts +1 -1
- package/src/findings/intra-function.ts +1 -1
- package/src/findings/non-code-artifacts.ts +4 -4
- package/src/findings/unused-declarations.ts +1 -1
- package/src/findings/visibility-narrowing.ts +2 -2
- package/src/findings-pass.ts +3 -3
- package/src/framework-lifecycle.ts +7 -7
- package/src/fs/document.ts +74 -0
- package/src/host.ts +12 -5
- package/src/injection-container.ts +1 -1
- package/src/json-document.ts +236 -76
- package/src/kinds.ts +0 -8
- package/src/print.ts +11 -92
- package/src/report-schema.ts +3 -299
- package/src/resolve.ts +281 -1105
- package/src/run.ts +50 -63
- package/src/schema.ts +391 -389
- package/src/scope.ts +39 -119
- package/src/serialization-contract.ts +1 -1
- package/src/suppress-file.ts +15 -20
- package/src/suppress.ts +3 -3
- package/src/template-field.ts +10 -7
- package/src/usage.ts +10 -0
- package/src/verbs/analyze.ts +12 -23
- package/src/verbs/explain.ts +3 -7
- package/src/verbs/print-projects.ts +2 -2
- package/src/verbs/verb.ts +2 -2
package/src/analysis.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { isStringLiteralLikeNode, type SourceFile } from "@typescript/native/ast";
|
|
10
10
|
import { lineOf, readComponent } from "./component-files.ts";
|
|
11
|
-
import { kindOf, type
|
|
11
|
+
import { kindOf, type PartialConfig, type Provenance, type ResolvedConfig } from "./config.ts";
|
|
12
12
|
import {
|
|
13
13
|
consumerLoads,
|
|
14
14
|
consumerReferences,
|
|
@@ -139,7 +139,6 @@ import { ownErrors, workspaceRun } from "./workspace-run.ts";
|
|
|
139
139
|
import { bytewise } from "./text/compare.ts";
|
|
140
140
|
import { holdsManifest, inInstalledPackages, MANIFEST, manifestAt } from "./fs/manifest.ts";
|
|
141
141
|
import { globExpression } from "./text/glob.ts";
|
|
142
|
-
import { LANGUAGE } from "./product.ts";
|
|
143
142
|
|
|
144
143
|
/** The setting whose source decides where a finding about a configured root sits. */
|
|
145
144
|
const ROOTS_SETTING = "roots.patterns";
|
|
@@ -325,7 +324,7 @@ function readProjects<Answer>(
|
|
|
325
324
|
engine: Engine,
|
|
326
325
|
host: Host,
|
|
327
326
|
scope: Scope,
|
|
328
|
-
config:
|
|
327
|
+
config: PartialConfig,
|
|
329
328
|
stage: <Brand>(project: ProjectView<Brand>, opened: OpenedProject) => Answer,
|
|
330
329
|
): ReadProjects<Answer> {
|
|
331
330
|
const targetRoot = scope.target.path;
|
|
@@ -356,7 +355,7 @@ function readProjects<Answer>(
|
|
|
356
355
|
// The nested packages are known only once every program is open, and every visit reads
|
|
357
356
|
// the conventions and options they decide, so both are set in the session's first step.
|
|
358
357
|
let nestedDirs: readonly string[] = [];
|
|
359
|
-
let conventions = readConventions(host, parse, [], config.ts.
|
|
358
|
+
let conventions = readConventions(host, parse, [], config.ts.disabled_conventions);
|
|
360
359
|
let options: RootOptions & { readonly parse: typeof parse; readonly conventions: Conventions };
|
|
361
360
|
const readManifests = (opened: readonly OpenedFiles[]): void => {
|
|
362
361
|
nestedDirs = nestedPackageDirs(
|
|
@@ -371,7 +370,7 @@ function readProjects<Answer>(
|
|
|
371
370
|
host,
|
|
372
371
|
parse,
|
|
373
372
|
conventionManifests(host, absoluteRoot, memberDirs, nestedDirs),
|
|
374
|
-
config.ts.
|
|
373
|
+
config.ts.disabled_conventions,
|
|
375
374
|
);
|
|
376
375
|
options = {
|
|
377
376
|
host,
|
|
@@ -380,12 +379,12 @@ function readProjects<Answer>(
|
|
|
380
379
|
...(workspace?.entries ?? []),
|
|
381
380
|
...packageEntries(host, normalizePath(absoluteRoot), nestedDirs),
|
|
382
381
|
],
|
|
383
|
-
patterns: config.
|
|
384
|
-
entryFiles: config.ts.
|
|
382
|
+
patterns: config.roots.patterns,
|
|
383
|
+
entryFiles: config.ts.entry_files,
|
|
385
384
|
conventions,
|
|
386
|
-
testFiles: config.ts.
|
|
385
|
+
testFiles: config.ts.test_files,
|
|
387
386
|
parse,
|
|
388
|
-
publishedAPI: config.
|
|
387
|
+
publishedAPI: config.target.kind !== undefined && kindOf(config.target.kind) === "library",
|
|
389
388
|
publishedMembers: new Set(
|
|
390
389
|
(workspace?.entries ?? []).filter((entry) => entry.published).map((entry) => entry.dir),
|
|
391
390
|
),
|
|
@@ -420,7 +419,7 @@ function readProjects<Answer>(
|
|
|
420
419
|
unbuildable.push(...met);
|
|
421
420
|
}
|
|
422
421
|
};
|
|
423
|
-
const testPatterns = config.ts.
|
|
422
|
+
const testPatterns = config.ts.test_files.map(globExpression);
|
|
424
423
|
const joins = discovered.derived
|
|
425
424
|
? joinedTestFiles(engine, host, absoluteRoot, discovered.configFiles, testPatterns)
|
|
426
425
|
: new Map<string, readonly string[]>();
|
|
@@ -657,7 +656,12 @@ interface RunProjects {
|
|
|
657
656
|
* for the errors and setup failures that decide whether it is analyzed, dropped or
|
|
658
657
|
* fails the run, exactly as the analysis reads them.
|
|
659
658
|
*/
|
|
660
|
-
export function runProjects(
|
|
659
|
+
export function runProjects(
|
|
660
|
+
engine: Engine,
|
|
661
|
+
host: Host,
|
|
662
|
+
scope: Scope,
|
|
663
|
+
config: PartialConfig,
|
|
664
|
+
): RunProjects {
|
|
661
665
|
const read = readProjects(
|
|
662
666
|
engine,
|
|
663
667
|
host,
|
|
@@ -678,7 +682,7 @@ export function runRoots(
|
|
|
678
682
|
engine: Engine,
|
|
679
683
|
host: Host,
|
|
680
684
|
scope: Scope,
|
|
681
|
-
config:
|
|
685
|
+
config: ResolvedConfig,
|
|
682
686
|
provenance: Provenance,
|
|
683
687
|
): RunRoots {
|
|
684
688
|
const targetRoot = scope.target.path;
|
|
@@ -719,7 +723,7 @@ export function runRoots(
|
|
|
719
723
|
bytewise(a.source, b.source),
|
|
720
724
|
);
|
|
721
725
|
const unmatched = unmatchedEverywhere(
|
|
722
|
-
config.
|
|
726
|
+
config.roots.patterns,
|
|
723
727
|
projects.map((answer) => answer.rooted),
|
|
724
728
|
);
|
|
725
729
|
return {
|
|
@@ -899,7 +903,7 @@ function readRun<Extra>(
|
|
|
899
903
|
engine: Engine,
|
|
900
904
|
host: Host,
|
|
901
905
|
scope: Scope,
|
|
902
|
-
config:
|
|
906
|
+
config: ResolvedConfig,
|
|
903
907
|
mode: Mode,
|
|
904
908
|
detectors: Detectors,
|
|
905
909
|
extra: <Brand>(
|
|
@@ -912,13 +916,13 @@ function readRun<Extra>(
|
|
|
912
916
|
const targetRoot = scope.target.path;
|
|
913
917
|
const disabled = disabledClasses(config);
|
|
914
918
|
const templates = disabled.has("template-field")
|
|
915
|
-
? { delimiters: config.analysis.
|
|
919
|
+
? { delimiters: config.analysis.template_delimiters, files: [] }
|
|
916
920
|
: readTemplates(host, targetRoot, config.analysis);
|
|
917
921
|
const consumers = scope.consumers.map((consumer) => consumer.path);
|
|
918
922
|
const read = readProjects<ReadProject<Extra>>(engine, host, scope, config, (project, opened) => {
|
|
919
923
|
const projectRead = opened.read();
|
|
920
924
|
const resolved = references(project, projectRead.held, targetRoot, {
|
|
921
|
-
testFiles: config.ts.
|
|
925
|
+
testFiles: config.ts.test_files,
|
|
922
926
|
testFileNames: projectRead.testFileNames,
|
|
923
927
|
});
|
|
924
928
|
const evidence = evidenceRecords(
|
|
@@ -929,7 +933,7 @@ function readRun<Extra>(
|
|
|
929
933
|
templates,
|
|
930
934
|
ts: config.ts,
|
|
931
935
|
consumers,
|
|
932
|
-
...(config.analysis.
|
|
936
|
+
...(config.analysis.generated_files === "include"
|
|
933
937
|
? {}
|
|
934
938
|
: { generated: projectRead.generated }),
|
|
935
939
|
},
|
|
@@ -1281,7 +1285,7 @@ export function runSweep(
|
|
|
1281
1285
|
engine: Engine,
|
|
1282
1286
|
host: Host,
|
|
1283
1287
|
scope: Scope,
|
|
1284
|
-
config:
|
|
1288
|
+
config: ResolvedConfig,
|
|
1285
1289
|
input: Pick<SweepInput, "marked" | "mode">,
|
|
1286
1290
|
detectors: Detectors = DETECTORS,
|
|
1287
1291
|
): RunSweep {
|
|
@@ -1324,7 +1328,7 @@ export function readEmitterRun(
|
|
|
1324
1328
|
engine: Engine,
|
|
1325
1329
|
host: Host,
|
|
1326
1330
|
scope: Scope,
|
|
1327
|
-
config:
|
|
1331
|
+
config: ResolvedConfig,
|
|
1328
1332
|
mode: Mode,
|
|
1329
1333
|
detectors: Detectors,
|
|
1330
1334
|
): ReadRun<EmitterExtra> {
|
|
@@ -1417,7 +1421,7 @@ function nearestManifestDir(host: Host, root: string, file: string): string {
|
|
|
1417
1421
|
export function emitterInputOver(
|
|
1418
1422
|
host: Host,
|
|
1419
1423
|
scope: Scope,
|
|
1420
|
-
config:
|
|
1424
|
+
config: ResolvedConfig,
|
|
1421
1425
|
read: ReadRun<EmitterExtra>,
|
|
1422
1426
|
input: Pick<SweepInput, "marked" | "mode">,
|
|
1423
1427
|
selfCheck: SelfCheckFacts,
|
|
@@ -1505,7 +1509,7 @@ function readAnalysis(
|
|
|
1505
1509
|
engine: Engine,
|
|
1506
1510
|
host: Host,
|
|
1507
1511
|
scope: Scope,
|
|
1508
|
-
config:
|
|
1512
|
+
config: ResolvedConfig,
|
|
1509
1513
|
provenance: Provenance,
|
|
1510
1514
|
mode: Mode,
|
|
1511
1515
|
detectors: Detectors,
|
|
@@ -1526,7 +1530,7 @@ function readAnalysis(
|
|
|
1526
1530
|
configFile: one.configFile,
|
|
1527
1531
|
})),
|
|
1528
1532
|
testFileRules: testFileRulesOf(
|
|
1529
|
-
config.ts.
|
|
1533
|
+
config.ts.test_files,
|
|
1530
1534
|
read.projects.flatMap((one) => one.configured.testFiles),
|
|
1531
1535
|
read.projects.flatMap((one) => one.configured.supportFiles ?? []),
|
|
1532
1536
|
),
|
|
@@ -1557,7 +1561,7 @@ function readAnalysis(
|
|
|
1557
1561
|
function inputsOver(
|
|
1558
1562
|
host: Host,
|
|
1559
1563
|
scope: Scope,
|
|
1560
|
-
config:
|
|
1564
|
+
config: ResolvedConfig,
|
|
1561
1565
|
provenance: Provenance,
|
|
1562
1566
|
mode: Mode,
|
|
1563
1567
|
read: ReadRun<EmitterExtra>,
|
|
@@ -1571,7 +1575,7 @@ function inputsOver(
|
|
|
1571
1575
|
const facts: SelfCheckFacts = {
|
|
1572
1576
|
refusals: suppressions.refusals,
|
|
1573
1577
|
unmatchedRoots: unmatchedEverywhere(
|
|
1574
|
-
config.
|
|
1578
|
+
config.roots.patterns,
|
|
1575
1579
|
read.projects.map((one) => one.extra.rooted),
|
|
1576
1580
|
),
|
|
1577
1581
|
rootsDocument: documentOf(host, provenance, ROOTS_SETTING, targetRoot),
|
|
@@ -1681,7 +1685,7 @@ export function runAnalysis(
|
|
|
1681
1685
|
engine: Engine,
|
|
1682
1686
|
host: Host,
|
|
1683
1687
|
scope: Scope,
|
|
1684
|
-
config:
|
|
1688
|
+
config: ResolvedConfig,
|
|
1685
1689
|
provenance: Provenance,
|
|
1686
1690
|
mode: Mode,
|
|
1687
1691
|
detectors: Detectors = DETECTORS,
|
|
@@ -1737,7 +1741,7 @@ export function runBaselineWrite(
|
|
|
1737
1741
|
engine: Engine,
|
|
1738
1742
|
host: Host,
|
|
1739
1743
|
scope: Scope,
|
|
1740
|
-
config:
|
|
1744
|
+
config: ResolvedConfig,
|
|
1741
1745
|
provenance: Provenance,
|
|
1742
1746
|
mode: Mode,
|
|
1743
1747
|
reason: string,
|
package/src/config.ts
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
type Format,
|
|
5
|
-
type Language,
|
|
6
|
-
type Severity,
|
|
7
|
-
} from "./contract/contract.generated.ts";
|
|
8
|
-
import type { ExemptionClass } from "./exempt-classes.ts";
|
|
9
|
-
import { LANGUAGE, PRODUCT } from "./product.ts";
|
|
1
|
+
import type { ResolvedConfig } from "./contract/config.generated.ts";
|
|
2
|
+
import { SEVERITIES, type Severity } from "./contract/contract.generated.ts";
|
|
3
|
+
import { LANGUAGE } from "./product.ts";
|
|
10
4
|
|
|
11
5
|
/**
|
|
12
6
|
* The name of the repository configuration at the target root, which is also the
|
|
@@ -15,254 +9,32 @@ import { LANGUAGE, PRODUCT } from "./product.ts";
|
|
|
15
9
|
*/
|
|
16
10
|
export const REPOSITORY_DOCUMENT = "deadset.json";
|
|
17
11
|
|
|
18
|
-
|
|
19
|
-
* Whether the target is an application, whose every caller is in the analyzed
|
|
20
|
-
* graph, or a library, whose published API has callers outside it.
|
|
21
|
-
*/
|
|
22
|
-
export type TargetKind = "application" | "library";
|
|
12
|
+
export type { PartialConfig, ResolvedConfig } from "./contract/config.generated.ts";
|
|
23
13
|
|
|
24
|
-
/**
|
|
25
|
-
export type
|
|
14
|
+
/** Whether the target is an application or a library. */
|
|
15
|
+
export type TargetKind = Extract<ResolvedConfig["target"]["kind"], string>;
|
|
16
|
+
export type DeclarationEntry = ResolvedConfig["ts"]["serializers"][number];
|
|
17
|
+
export type LifecycleContract = ResolvedConfig["ts"]["lifecycle_contracts"][number];
|
|
18
|
+
export type BuildConfiguration = ResolvedConfig["analysis"]["configurations"][number];
|
|
19
|
+
export type ProjectConfiguration = Extract<BuildConfiguration, { readonly project: string }>;
|
|
20
|
+
export type TemplateDelimiters = ResolvedConfig["analysis"]["template_delimiters"];
|
|
21
|
+
export type Sort = ResolvedConfig["reporters"]["sort"];
|
|
22
|
+
export type Cascade = ResolvedConfig["reporters"]["cascade"];
|
|
26
23
|
|
|
27
|
-
/** The kind a target kind setting gives
|
|
28
|
-
export function kindOf(
|
|
29
|
-
return typeof
|
|
24
|
+
/** The kind a target kind setting gives this analyzer's declarations. */
|
|
25
|
+
export function kindOf(kind: ResolvedConfig["target"]["kind"]): TargetKind {
|
|
26
|
+
return typeof kind === "string" ? kind : kind[LANGUAGE];
|
|
30
27
|
}
|
|
31
28
|
|
|
32
|
-
/** Whether declarations in generated files are analyzed. */
|
|
33
|
-
export type GeneratedFiles = "exclude" | "include";
|
|
34
|
-
|
|
35
|
-
/** How a reference from a consumer's test file counts. */
|
|
36
|
-
export type ConsumerTests = "test" | "production";
|
|
37
|
-
|
|
38
|
-
/** The severities ranked, the strongest highest. */
|
|
39
|
-
const SEVERITY_RANK: Readonly<Record<Severity, number>> = { allow: 0, warn: 1, deny: 2 };
|
|
40
|
-
|
|
41
29
|
/** Whether a finding at one severity fails a run under the failing severity. */
|
|
42
30
|
export function fails(severity: Severity, failOn: Severity): boolean {
|
|
43
|
-
return
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** The order findings are rendered in. */
|
|
47
|
-
export type Sort = "position" | "size";
|
|
48
|
-
|
|
49
|
-
/** How much of a dead component a rendering names. */
|
|
50
|
-
export type Cascade = "roots" | "full";
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* A platform configuration: an operating system, an architecture and the build
|
|
54
|
-
* tags in effect. It is the shape a language built under build constraints reads,
|
|
55
|
-
* and this analyzer leaves it to that analysis.
|
|
56
|
-
*/
|
|
57
|
-
interface PlatformConfiguration {
|
|
58
|
-
readonly shape: "platform";
|
|
59
|
-
readonly id: string;
|
|
60
|
-
readonly os: string;
|
|
61
|
-
readonly arch: string;
|
|
62
|
-
readonly tags: readonly string[];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* A project configuration: one compiler configuration file, as a path below the
|
|
67
|
-
* target root with `/` between segments.
|
|
68
|
-
*/
|
|
69
|
-
export interface ProjectConfiguration {
|
|
70
|
-
readonly shape: "project";
|
|
71
|
-
readonly id: string;
|
|
72
|
-
readonly project: string;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* One entry of the build matrix: a configuration the target builds under, in
|
|
77
|
-
* exactly one of the two shapes the matrix admits.
|
|
78
|
-
*/
|
|
79
|
-
export type BuildConfiguration = PlatformConfiguration | ProjectConfiguration;
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* The pair of action delimiters a template is parsed with. Both members are
|
|
83
|
-
* required once a document names the object, so the pair is one setting rather
|
|
84
|
-
* than two.
|
|
85
|
-
*/
|
|
86
|
-
export interface TemplateDelimiters {
|
|
87
|
-
readonly left: string;
|
|
88
|
-
readonly right: string;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** How the analysis is scoped and filtered. */
|
|
92
|
-
export interface Analysis {
|
|
93
|
-
readonly languages: readonly Language[];
|
|
94
|
-
readonly minConfidence: Confidence;
|
|
95
|
-
readonly generatedFiles: GeneratedFiles;
|
|
96
|
-
readonly consumerTests: ConsumerTests;
|
|
97
|
-
readonly configurations: readonly BuildConfiguration[];
|
|
98
|
-
readonly matrixComplete: boolean;
|
|
99
|
-
readonly templateDirs: readonly string[];
|
|
100
|
-
readonly templateDelimiters: TemplateDelimiters;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** How findings are rendered. */
|
|
104
|
-
interface Reporters {
|
|
105
|
-
readonly formats: readonly Format[];
|
|
106
|
-
readonly sort: Sort;
|
|
107
|
-
readonly cascade: Cascade;
|
|
108
|
-
readonly maxFindings: number;
|
|
109
|
-
readonly failOn: Severity;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* One declaration a configuration entry names, in exactly one of three shapes: a
|
|
114
|
-
* declaration of the analyzed program by its stable symbol reference, a declaration a
|
|
115
|
-
* module exports by the bare specifier an import writes for the module and the
|
|
116
|
-
* declaration's path from its exports, or a declaration of the global scope by its
|
|
117
|
-
* path from that scope. A path joins the name and each member with dots and spells a
|
|
118
|
-
* static member with `:static` after it.
|
|
119
|
-
*/
|
|
120
|
-
export type DeclarationEntry =
|
|
121
|
-
| { readonly shape: "symbol"; readonly symbol: string }
|
|
122
|
-
| { readonly shape: "module"; readonly module: string; readonly name: string }
|
|
123
|
-
| { readonly shape: "global"; readonly global: string };
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* One framework's lifecycle contract: the declarations whose use makes a class one of
|
|
127
|
-
* the framework's components, the classes whose subclasses are its components, and the
|
|
128
|
-
* names of the members the framework calls on a component, each spelled as the last
|
|
129
|
-
* component of a member's stable symbol reference.
|
|
130
|
-
*/
|
|
131
|
-
export interface LifecycleContract {
|
|
132
|
-
readonly components: readonly DeclarationEntry[];
|
|
133
|
-
readonly bases: readonly DeclarationEntry[];
|
|
134
|
-
readonly members: readonly string[];
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* One analyzer the orchestrator may run, in exactly one of two shapes: an installed
|
|
139
|
-
* analyzer, named by its command alone, or an acquirable one, which also names the
|
|
140
|
-
* artifact acquisition fetches and verifies. This analyzer validates and prints the
|
|
141
|
-
* list and runs nothing from it.
|
|
142
|
-
*/
|
|
143
|
-
export type Provider =
|
|
144
|
-
| {
|
|
145
|
-
readonly shape: "installed";
|
|
146
|
-
readonly name: string;
|
|
147
|
-
readonly languages: readonly Language[];
|
|
148
|
-
readonly command: string;
|
|
149
|
-
}
|
|
150
|
-
| {
|
|
151
|
-
readonly shape: "acquirable";
|
|
152
|
-
readonly name: string;
|
|
153
|
-
readonly languages: readonly Language[];
|
|
154
|
-
readonly command: string;
|
|
155
|
-
readonly source: string;
|
|
156
|
-
readonly version: string;
|
|
157
|
-
readonly digest: string;
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
/** The analyzers the orchestrator runs. */
|
|
161
|
-
interface Providers {
|
|
162
|
-
readonly analyzers: readonly Provider[];
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/** The section the TypeScript analyzer owns. */
|
|
166
|
-
export interface TSSection {
|
|
167
|
-
readonly testFiles: readonly string[];
|
|
168
|
-
readonly entryFiles: readonly string[];
|
|
169
|
-
readonly componentExtensions: readonly string[];
|
|
170
|
-
readonly disabledConventions: readonly string[];
|
|
171
|
-
readonly injectionRegistrations: readonly DeclarationEntry[];
|
|
172
|
-
readonly lifecycleContracts: readonly LifecycleContract[];
|
|
173
|
-
readonly serializers: readonly DeclarationEntry[];
|
|
174
|
-
/** Whether the maintainer declares the roots the analysis finds every root the target has. */
|
|
175
|
-
readonly rootsComplete: boolean;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* The resolved configuration: the closed key list of the Contract's
|
|
180
|
-
* configuration schema, decoded, with every default applied.
|
|
181
|
-
*
|
|
182
|
-
* `targetKind` is empty exactly in the configuration every default states, which
|
|
183
|
-
* is not a configuration a run may use: the field has no default and resolution
|
|
184
|
-
* refuses a result that carries none.
|
|
185
|
-
*
|
|
186
|
-
* `severity` is a map rather than an object because its keys come from a
|
|
187
|
-
* document, and an object keyed by document text answers for names it never
|
|
188
|
-
* held. The order it is written in is the emitter's, not the map's.
|
|
189
|
-
*/
|
|
190
|
-
export interface Config {
|
|
191
|
-
readonly contractVersion: string;
|
|
192
|
-
readonly targetKind: TargetKindSetting | "";
|
|
193
|
-
readonly analysis: Analysis;
|
|
194
|
-
readonly consumersComplete: boolean;
|
|
195
|
-
readonly rootPatterns: readonly string[];
|
|
196
|
-
readonly severity: ReadonlyMap<string, Severity>;
|
|
197
|
-
readonly exemptionsDisabled: readonly ExemptionClass[];
|
|
198
|
-
readonly reporters: Reporters;
|
|
199
|
-
readonly providers: Providers;
|
|
200
|
-
readonly ts: TSSection;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* The configuration every documented default states. `targetKind` is empty: the
|
|
205
|
-
* field has no default and is never inferred.
|
|
206
|
-
*/
|
|
207
|
-
export function defaultConfig(): Config {
|
|
208
|
-
return {
|
|
209
|
-
contractVersion: CONTRACT_VERSION,
|
|
210
|
-
targetKind: "",
|
|
211
|
-
analysis: {
|
|
212
|
-
languages: [],
|
|
213
|
-
minConfidence: "probable",
|
|
214
|
-
generatedFiles: "exclude",
|
|
215
|
-
consumerTests: "test",
|
|
216
|
-
configurations: [],
|
|
217
|
-
matrixComplete: false,
|
|
218
|
-
templateDirs: [],
|
|
219
|
-
templateDelimiters: { left: "{{", right: "}}" },
|
|
220
|
-
},
|
|
221
|
-
consumersComplete: false,
|
|
222
|
-
rootPatterns: [],
|
|
223
|
-
severity: new Map(),
|
|
224
|
-
exemptionsDisabled: [],
|
|
225
|
-
reporters: {
|
|
226
|
-
formats: ["text"],
|
|
227
|
-
sort: "position",
|
|
228
|
-
cascade: "roots",
|
|
229
|
-
maxFindings: 0,
|
|
230
|
-
failOn: "deny",
|
|
231
|
-
},
|
|
232
|
-
providers: {
|
|
233
|
-
analyzers: [
|
|
234
|
-
{ shape: "installed", name: "deadset-go", languages: ["go"], command: "deadset-go" },
|
|
235
|
-
{ shape: "installed", name: PRODUCT, languages: [LANGUAGE], command: PRODUCT },
|
|
236
|
-
],
|
|
237
|
-
},
|
|
238
|
-
ts: {
|
|
239
|
-
testFiles: [
|
|
240
|
-
"**/*.test.{ts,tsx,mts,cts}",
|
|
241
|
-
"**/*.test-d.{ts,tsx,mts,cts}",
|
|
242
|
-
"**/*.spec-d.{ts,tsx,mts,cts}",
|
|
243
|
-
"**/*.spec.*",
|
|
244
|
-
"**/__tests__/**",
|
|
245
|
-
"**/__mocks__/**",
|
|
246
|
-
"**/test-d/**",
|
|
247
|
-
],
|
|
248
|
-
entryFiles: [],
|
|
249
|
-
componentExtensions: [".vue", ".svelte", ".astro"],
|
|
250
|
-
disabledConventions: [],
|
|
251
|
-
injectionRegistrations: [],
|
|
252
|
-
lifecycleContracts: [],
|
|
253
|
-
serializers: [],
|
|
254
|
-
rootsComplete: false,
|
|
255
|
-
},
|
|
256
|
-
};
|
|
31
|
+
return SEVERITIES.indexOf(severity) >= SEVERITIES.indexOf(failOn);
|
|
257
32
|
}
|
|
258
33
|
|
|
259
34
|
/** Where a resolved setting came from, in the order the sources outrank each other. */
|
|
260
35
|
export type Source = "flag" | "repository" | "central" | "default";
|
|
261
36
|
|
|
262
|
-
/**
|
|
263
|
-
* One setting's source together with the file or flag that supplied it. `label`
|
|
264
|
-
* is empty exactly when `source` is `"default"`.
|
|
265
|
-
*/
|
|
37
|
+
/** One setting's source with the file or flag that supplied it, empty for a default. */
|
|
266
38
|
export interface Origin {
|
|
267
39
|
readonly source: Source;
|
|
268
40
|
readonly label: string;
|
|
@@ -277,63 +49,24 @@ export function renderOrigin(origin: Origin): string {
|
|
|
277
49
|
}
|
|
278
50
|
|
|
279
51
|
/**
|
|
280
|
-
* The origin of each resolved setting, keyed by the setting's dotted path. A
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
* empty.
|
|
52
|
+
* The origin of each resolved setting, keyed by the setting's dotted path. A section
|
|
53
|
+
* has no entry; a keyed section carries one entry per member it names, or one for
|
|
54
|
+
* itself when it is empty.
|
|
284
55
|
*/
|
|
285
56
|
export type Provenance = ReadonlyMap<string, Origin>;
|
|
286
57
|
|
|
287
|
-
/**
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
* supplied each setting `flags` names, keyed by dotted setting path.
|
|
292
|
-
*/
|
|
293
|
-
export interface Inputs {
|
|
294
|
-
readonly flags?: string;
|
|
295
|
-
readonly repository?: string;
|
|
296
|
-
readonly central?: string;
|
|
297
|
-
readonly repositoryLabel?: string;
|
|
298
|
-
readonly centralLabel?: string;
|
|
299
|
-
readonly flagLabels?: ReadonlyMap<string, string>;
|
|
58
|
+
/** One configuration document: its text and the path it was read from. */
|
|
59
|
+
export interface Document {
|
|
60
|
+
readonly text: string;
|
|
61
|
+
readonly label: string;
|
|
300
62
|
}
|
|
301
63
|
|
|
302
|
-
/** Which refusal a {@link ConfigError} carries. */
|
|
303
|
-
type ErrorKind =
|
|
304
|
-
/**
|
|
305
|
-
* A document that is not one JSON instance of the closed key list: a syntax
|
|
306
|
-
* error, a value of the wrong type, a value outside the closed set a key
|
|
307
|
-
* declares, or a member written twice at one level.
|
|
308
|
-
*/
|
|
309
|
-
| "malformed"
|
|
310
|
-
/**
|
|
311
|
-
* A key the closed key list does not declare, or a severity key naming no live
|
|
312
|
-
* kind or a kind whose severity the Contract fixes.
|
|
313
|
-
*/
|
|
314
|
-
| "unimplemented-key"
|
|
315
|
-
/** A resolved configuration no source supplied a target kind for. */
|
|
316
|
-
| "missing-target-kind";
|
|
317
|
-
|
|
318
64
|
/**
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
* one caller that turns it into an exit code is the command line.
|
|
65
|
+
* The configuration sources one invocation carries. `flags` holds the text each
|
|
66
|
+
* setting option was given, by the setting's dotted path.
|
|
322
67
|
*/
|
|
323
|
-
export
|
|
324
|
-
|
|
325
|
-
readonly
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* The key or field the message names, spelled as the document spells it. It is
|
|
329
|
-
* empty when the refusal names none.
|
|
330
|
-
*/
|
|
331
|
-
readonly key: string;
|
|
332
|
-
|
|
333
|
-
constructor(kind: ErrorKind, key: string, message: string) {
|
|
334
|
-
super(message);
|
|
335
|
-
this.name = "ConfigError";
|
|
336
|
-
this.kind = kind;
|
|
337
|
-
this.key = key;
|
|
338
|
-
}
|
|
68
|
+
export interface Inputs {
|
|
69
|
+
readonly flags?: ReadonlyMap<string, string>;
|
|
70
|
+
readonly repository?: Document;
|
|
71
|
+
readonly central?: Document;
|
|
339
72
|
}
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
import { SymbolFlags, type NodeHandle, type Symbol as TSSymbol } from "@typescript/native/sync";
|
|
20
20
|
import type { AliasChains } from "./alias-chain.ts";
|
|
21
21
|
import { resolvedTargets } from "./calls.ts";
|
|
22
|
-
import type {
|
|
22
|
+
import type { DeclarationEntry, ResolvedConfig } from "./config.ts";
|
|
23
23
|
import { declaredComponent, type Inventory } from "./inventory.ts";
|
|
24
24
|
import { must, Unanswerable } from "./query.ts";
|
|
25
25
|
import { nameComponent } from "./ref.ts";
|
|
@@ -252,21 +252,21 @@ export function resolveEntries<Brand>(
|
|
|
252
252
|
}
|
|
253
253
|
const modules = modulesNamed(
|
|
254
254
|
project,
|
|
255
|
-
new Set(entries.flatMap((entry) => (
|
|
255
|
+
new Set(entries.flatMap((entry) => ("module" in entry ? [entry.module] : []))),
|
|
256
256
|
);
|
|
257
257
|
return entries.map((entry) => {
|
|
258
|
-
if (
|
|
258
|
+
if ("symbol" in entry) {
|
|
259
259
|
return { entry, handles: new Set<string>(), ids: new Set(byRef.get(entry.symbol) ?? []) };
|
|
260
260
|
}
|
|
261
261
|
const { components, static: isStatic } = componentsOf(
|
|
262
|
-
|
|
262
|
+
"module" in entry ? entry.name : entry.global,
|
|
263
263
|
);
|
|
264
264
|
const [first = "", ...rest] = components;
|
|
265
265
|
let start: TSSymbol | undefined;
|
|
266
266
|
if (isStatic && rest.length === 0) {
|
|
267
267
|
// Only a member is static, and a path of one component names no member.
|
|
268
268
|
start = undefined;
|
|
269
|
-
} else if (
|
|
269
|
+
} else if ("module" in entry) {
|
|
270
270
|
const module = modules.get(entry.module);
|
|
271
271
|
start = module === undefined ? undefined : memberOf(project, module, first, false);
|
|
272
272
|
} else if (nameComponent(first) === first) {
|
|
@@ -329,14 +329,10 @@ export function entriesAt<Brand>(
|
|
|
329
329
|
* program the display name the inventory gives it.
|
|
330
330
|
*/
|
|
331
331
|
export function spelledEntry(entry: DeclarationEntry, held: Inventory): string {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
return held.symbols.find((symbol) => symbol.ref === entry.symbol)?.name ?? entry.symbol;
|
|
335
|
-
case "module":
|
|
336
|
-
return entry.name;
|
|
337
|
-
case "global":
|
|
338
|
-
return entry.global;
|
|
332
|
+
if ("symbol" in entry) {
|
|
333
|
+
return held.symbols.find((symbol) => symbol.ref === entry.symbol)?.name ?? entry.symbol;
|
|
339
334
|
}
|
|
335
|
+
return "module" in entry ? entry.name : entry.global;
|
|
340
336
|
}
|
|
341
337
|
|
|
342
338
|
/** One entry of a configuration key that names a declaration, and the key's dotted path. */
|
|
@@ -353,14 +349,17 @@ const LIFECYCLE_KEY = "ts.lifecycle_contracts";
|
|
|
353
349
|
* the injection registrations, each lifecycle contract's components and then its bases,
|
|
354
350
|
* and the serializers.
|
|
355
351
|
*/
|
|
356
|
-
export function configuredDeclarations(config:
|
|
352
|
+
export function configuredDeclarations(config: ResolvedConfig): readonly ConfiguredDeclaration[] {
|
|
357
353
|
return [
|
|
358
|
-
...config.ts.
|
|
354
|
+
...config.ts.injection_registrations.map((entry) => ({
|
|
359
355
|
key: "ts.injection_registrations",
|
|
360
356
|
entry,
|
|
361
357
|
})),
|
|
362
|
-
...config.ts.
|
|
363
|
-
[...contract.components, ...contract.bases].map((entry) => ({
|
|
358
|
+
...config.ts.lifecycle_contracts.flatMap((contract) =>
|
|
359
|
+
[...(contract.components ?? []), ...(contract.bases ?? [])].map((entry) => ({
|
|
360
|
+
key: LIFECYCLE_KEY,
|
|
361
|
+
entry,
|
|
362
|
+
})),
|
|
364
363
|
),
|
|
365
364
|
...config.ts.serializers.map((entry) => ({ key: "ts.serializers", entry })),
|
|
366
365
|
];
|
|
@@ -396,12 +395,8 @@ export function declarationsNamed<Brand>(
|
|
|
396
395
|
* sign and its name, and a global entry's path after a number sign.
|
|
397
396
|
*/
|
|
398
397
|
export function configuredRef(entry: DeclarationEntry): string {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
return entry.symbol;
|
|
402
|
-
case "module":
|
|
403
|
-
return `${entry.module}#${entry.name}`;
|
|
404
|
-
case "global":
|
|
405
|
-
return `#${entry.global}`;
|
|
398
|
+
if ("symbol" in entry) {
|
|
399
|
+
return entry.symbol;
|
|
406
400
|
}
|
|
401
|
+
return "module" in entry ? `${entry.module}#${entry.name}` : `#${entry.global}`;
|
|
407
402
|
}
|