@cplieger/deadset-ts 7.2.0-dev.4 → 8.0.0-dev.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/bin/mapper-connection.js +2 -1
- package/dist/bin/node-host.js +24 -5
- package/dist/src/analysis.js +245 -309
- package/dist/src/augmentations.js +124 -18
- package/dist/src/automatic-types.js +170 -0
- package/dist/src/build-scripts.js +425 -0
- package/dist/src/component-layer.js +4 -2
- package/dist/src/components.js +9 -9
- package/dist/src/config.js +6 -81
- package/dist/src/configuration-files.js +78 -188
- package/dist/src/configured-declarations.js +15 -20
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/consumers.js +3 -30
- package/dist/src/contextual-uses.js +56 -9
- package/dist/src/contract/config.generated.js +558 -0
- package/dist/src/contract/contract.generated.js +1244 -0
- package/dist/src/contract/report.generated.js +862 -0
- package/dist/src/convention-rows.js +146 -1
- package/dist/src/conventions.js +136 -70
- package/dist/src/decorator.js +21 -18
- package/dist/src/dependencies.js +280 -62
- package/dist/src/edges.js +38 -122
- package/dist/src/element-access.js +4 -6
- package/dist/src/entry-point-gaps.js +9 -18
- package/dist/src/entry-points.js +106 -21
- package/dist/src/enum-group.js +3 -10
- package/dist/src/erased-stores.js +4 -6
- package/dist/src/exempt-classes.js +1 -63
- package/dist/src/exempt.js +21 -40
- package/dist/src/failure.js +36 -0
- package/dist/src/file-facts.js +2 -25
- package/dist/src/findings/completion.js +225 -63
- package/dist/src/findings/dependencies-and-module-machinery.js +72 -27
- package/dist/src/findings/emitters.js +6 -6
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +53 -19
- package/dist/src/findings/non-code-artifacts.js +71 -39
- package/dist/src/findings/reads-and-writes.js +16 -13
- package/dist/src/findings/self-check.js +10 -12
- package/dist/src/findings/unused-declarations.js +57 -28
- package/dist/src/findings/visibility-narrowing.js +40 -18
- package/dist/src/findings-pass.js +8 -15
- package/dist/src/framework-lifecycle.js +7 -7
- package/dist/src/fs/document.js +52 -0
- package/dist/src/fs/manifest.js +72 -0
- package/dist/src/fs/tree.js +88 -0
- package/dist/src/generated-file.js +18 -4
- package/dist/src/git-ignore.js +99 -0
- package/dist/src/graph.js +9 -4
- package/dist/src/html-entries.js +6 -33
- package/dist/src/import-bindings.js +97 -0
- package/dist/src/in-operands.js +2 -1
- package/dist/src/index.js +1 -1
- package/dist/src/indexed-access.js +130 -28
- package/dist/src/injection-container.js +1 -1
- package/dist/src/interface-satisfaction.js +30 -12
- package/dist/src/intra-function-parts.js +341 -69
- package/dist/src/inventory.js +54 -29
- package/dist/src/javascript-file.js +33 -0
- package/dist/src/json-document.js +198 -67
- package/dist/src/keyof-comparisons.js +4 -6
- package/dist/src/kinds.js +3 -366
- package/dist/src/load/classify.js +270 -0
- package/dist/src/load/diagnostics.js +80 -0
- package/dist/src/load/discover.js +277 -0
- package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
- package/dist/src/load/expected-module.js +136 -0
- package/dist/src/load/load.js +27 -0
- package/dist/src/load/module-lookup.js +37 -0
- package/dist/src/load/plan.js +313 -0
- package/dist/src/load/specifiers.js +115 -0
- package/dist/src/load/synthesize.js +140 -0
- package/dist/src/load/test-globals.js +207 -0
- package/dist/src/load/test-joins.js +82 -0
- package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
- package/dist/src/{workspace-programs.js → load/workspace-programs.js} +88 -61
- package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +60 -59
- package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
- package/dist/src/{workspace.js → load/workspace.js} +62 -124
- package/dist/src/manifest.js +49 -54
- package/dist/src/matrix.js +94 -13
- package/dist/src/mdx-entries.js +174 -0
- package/dist/src/module-declarations.js +9 -13
- package/dist/src/outside-tests.js +1 -1
- package/dist/src/overrides.js +41 -10
- package/dist/src/partial.js +2 -1
- package/dist/src/paths.js +26 -15
- package/dist/src/position.js +2 -1
- package/dist/src/print.js +11 -78
- package/dist/src/product.js +13 -0
- package/dist/src/query.js +1 -0
- package/dist/src/ref.js +11 -0
- package/dist/src/references.js +52 -31
- package/dist/src/report-schema.js +51 -0
- package/dist/src/report.js +59 -41
- package/dist/src/reporters/annotations.js +15 -8
- package/dist/src/reporters/sarif.js +16 -10
- package/dist/src/reporters/template.js +4 -19
- package/dist/src/resolve.js +245 -727
- package/dist/src/roots.js +374 -116
- package/dist/src/run.js +66 -77
- package/dist/src/schema.js +320 -342
- package/dist/src/scope.js +38 -115
- package/dist/src/serialization-contract.js +649 -29
- package/dist/src/session.js +41 -29
- package/dist/src/source-files.js +40 -15
- package/dist/src/suppress-file.js +34 -29
- package/dist/src/suppress-inline.js +21 -4
- package/dist/src/suppress.js +40 -56
- package/dist/src/sweep.js +32 -17
- package/dist/src/symbol-keys.js +105 -6
- package/dist/src/template-field.js +9 -19
- package/dist/src/test-support.js +119 -18
- package/dist/src/text/compare.js +26 -0
- package/dist/src/text/glob.js +194 -0
- package/dist/src/text/named-files.js +54 -0
- package/dist/src/text/shell.js +159 -0
- package/dist/src/text/yaml.js +393 -0
- package/dist/src/type-errors.js +27 -221
- package/dist/src/type-parameter-reads.js +172 -0
- package/dist/src/unseen-loads.js +100 -0
- package/dist/src/unwrap.js +23 -0
- package/dist/src/usage.js +10 -0
- package/dist/src/verbs/analyze.js +181 -125
- package/dist/src/verbs/describe.js +9 -16
- package/dist/src/verbs/explain.js +14 -9
- package/dist/src/verbs/print-config.js +2 -1
- package/dist/src/verbs/print-projects.js +6 -4
- package/dist/src/verbs/print-retained.js +4 -2
- package/dist/src/verbs/print-roots.js +4 -2
- package/dist/src/verbs/verb.js +1 -5
- package/dist/src/verbs/version.js +14 -3
- package/dist/src/workflow-steps.js +128 -33
- package/jsr.json +1 -1
- package/package.json +5 -4
- package/src/analysis.ts +333 -457
- package/src/augmentations.ts +152 -23
- package/src/automatic-types.ts +228 -0
- package/src/build-scripts.ts +543 -0
- package/src/component-layer.ts +4 -2
- package/src/components.ts +9 -10
- package/src/config.ts +31 -299
- package/src/configuration-files.ts +100 -191
- package/src/configured-declarations.ts +18 -23
- package/src/conformance-record.ts +2 -2
- package/src/consumers.ts +6 -57
- package/src/contextual-uses.ts +64 -12
- package/src/contract/config.generated.ts +818 -0
- package/src/contract/contract.generated.ts +1308 -0
- package/src/contract/report.generated.ts +1237 -0
- package/src/convention-rows.ts +186 -1
- package/src/conventions.ts +166 -90
- package/src/decorator.ts +28 -18
- package/src/dependencies.ts +356 -78
- package/src/edges.ts +41 -140
- package/src/element-access.ts +4 -7
- package/src/entry-point-gaps.ts +11 -20
- package/src/entry-points.ts +124 -28
- package/src/enum-group.ts +2 -11
- package/src/erased-stores.ts +3 -7
- package/src/exempt-classes.ts +1 -75
- package/src/exempt.ts +36 -50
- package/src/failure.ts +63 -0
- package/src/file-facts.ts +9 -24
- package/src/finding.ts +24 -9
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +297 -77
- package/src/findings/dependencies-and-module-machinery.ts +90 -31
- package/src/findings/emitter.ts +8 -9
- package/src/findings/emitters.ts +8 -8
- package/src/findings/interfaces.ts +4 -6
- package/src/findings/intra-function.ts +64 -20
- package/src/findings/non-code-artifacts.ts +88 -50
- package/src/findings/reads-and-writes.ts +20 -16
- package/src/findings/self-check.ts +11 -17
- package/src/findings/unused-declarations.ts +65 -28
- package/src/findings/visibility-narrowing.ts +51 -19
- package/src/findings-pass.ts +10 -19
- package/src/framework-lifecycle.ts +7 -7
- package/src/fs/document.ts +70 -0
- package/src/fs/manifest.ts +92 -0
- package/src/fs/tree.ts +110 -0
- package/src/generated-file.ts +23 -4
- package/src/git-ignore.ts +122 -0
- package/src/graph.ts +11 -3
- package/src/host.ts +17 -14
- package/src/html-entries.ts +6 -33
- package/src/import-bindings.ts +128 -0
- package/src/in-operands.ts +2 -1
- package/src/index.ts +1 -1
- package/src/indexed-access.ts +152 -30
- package/src/injection-container.ts +1 -1
- package/src/interface-satisfaction.ts +33 -13
- package/src/intra-function-parts.ts +449 -85
- package/src/inventory.ts +73 -27
- package/src/javascript-file.ts +37 -0
- package/src/json-document.ts +241 -76
- package/src/keyof-comparisons.ts +3 -7
- package/src/kinds.ts +5 -413
- package/src/load/classify.ts +397 -0
- package/src/load/diagnostics.ts +144 -0
- package/src/load/discover.ts +455 -0
- package/src/{emit-map.ts → load/emit-map.ts} +1 -1
- package/src/load/expected-module.ts +188 -0
- package/src/load/load.ts +73 -0
- package/src/load/module-lookup.ts +50 -0
- package/src/load/plan.ts +504 -0
- package/src/load/specifiers.ts +168 -0
- package/src/load/synthesize.ts +211 -0
- package/src/load/test-globals.ts +251 -0
- package/src/load/test-joins.ts +98 -0
- package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
- package/src/{workspace-programs.ts → load/workspace-programs.ts} +127 -80
- package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +73 -64
- package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
- package/src/{workspace.ts → load/workspace.ts} +90 -134
- package/src/manifest.ts +81 -75
- package/src/matrix.ts +154 -18
- package/src/mdx-entries.ts +218 -0
- package/src/module-declarations.ts +11 -13
- package/src/outside-tests.ts +1 -1
- package/src/overrides.ts +51 -12
- package/src/partial.ts +3 -2
- package/src/paths.ts +27 -15
- package/src/position.ts +2 -1
- package/src/print.ts +12 -91
- package/src/product.ts +18 -0
- package/src/query.ts +11 -0
- package/src/ref.ts +13 -0
- package/src/references.ts +62 -32
- package/src/report-schema.ts +68 -0
- package/src/report.ts +105 -215
- package/src/reporters/annotations.ts +22 -11
- package/src/reporters/json.ts +1 -1
- package/src/reporters/reporter.ts +2 -4
- package/src/reporters/reporters.ts +1 -1
- package/src/reporters/sarif.ts +29 -16
- package/src/reporters/template.ts +4 -20
- package/src/reporters/text.ts +3 -3
- package/src/resolve.ts +283 -1111
- package/src/roots.ts +490 -137
- package/src/run.ts +75 -91
- package/src/schema.ts +391 -387
- package/src/scope.ts +46 -138
- package/src/serialization-contract.ts +818 -26
- package/src/session.ts +77 -54
- package/src/source-files.ts +67 -17
- package/src/suppress-file.ts +36 -34
- package/src/suppress-inline.ts +20 -3
- package/src/suppress.ts +52 -63
- package/src/sweep.ts +53 -18
- package/src/symbol-keys.ts +131 -7
- package/src/template-field.ts +15 -25
- package/src/test-support.ts +155 -18
- package/src/text/compare.ts +27 -0
- package/src/text/glob.ts +210 -0
- package/src/text/named-files.inputs.json +1522 -0
- package/src/text/named-files.ts +68 -0
- package/src/text/shell.ts +161 -0
- package/src/text/yaml.ts +423 -0
- package/src/type-errors.ts +35 -273
- package/src/type-parameter-reads.ts +227 -0
- package/src/unseen-loads.ts +134 -0
- package/src/unwrap.ts +34 -0
- package/src/usage.ts +10 -0
- package/src/verbs/analyze.ts +245 -161
- package/src/verbs/describe.ts +14 -15
- package/src/verbs/explain.ts +15 -13
- package/src/verbs/print-config.ts +2 -1
- package/src/verbs/print-projects.ts +6 -4
- package/src/verbs/print-retained.ts +4 -2
- package/src/verbs/print-roots.ts +4 -2
- package/src/verbs/verb.ts +2 -8
- package/src/verbs/version.ts +15 -3
- package/src/workflow-steps.ts +149 -34
- package/dist/src/discover.js +0 -347
- package/dist/src/glob.js +0 -65
- package/dist/src/pnpm-workspace.js +0 -120
- package/dist/src/setup-failure.js +0 -23
- package/dist/src/test-joins.js +0 -101
- package/dist/src/version.js +0 -7
- package/src/discover.ts +0 -510
- package/src/glob.ts +0 -67
- package/src/pnpm-workspace.ts +0 -135
- package/src/setup-failure.ts +0 -39
- package/src/test-joins.ts +0 -120
- package/src/version.ts +0 -7
package/src/verbs/analyze.ts
CHANGED
|
@@ -1,35 +1,44 @@
|
|
|
1
|
+
import { CONTRACT_VERSION, EXIT_CLEAN, type Format } from "../contract/contract.generated.ts";
|
|
1
2
|
import {
|
|
2
3
|
runAnalysis,
|
|
3
4
|
runBaselineWrite,
|
|
4
5
|
unansweredCounts,
|
|
5
|
-
type ComponentWarning,
|
|
6
6
|
type RunAnalysis,
|
|
7
7
|
type RunProject,
|
|
8
8
|
} from "../analysis.ts";
|
|
9
|
-
import {
|
|
9
|
+
import { kindOf } from "../config.ts";
|
|
10
10
|
import { CONFORMANCE, DECLARED_GAPS } from "../conformance.ts";
|
|
11
|
-
import { moduleIdentity } from "../consumers.ts";
|
|
12
11
|
import { analyzerProvenance, verdictOf } from "../findings-pass.ts";
|
|
13
|
-
import { joinPath, normalizePath, relativePath, resolvePath } from "../paths.ts";
|
|
12
|
+
import { dirnamePath, joinPath, normalizePath, relativePath, resolvePath } from "../paths.ts";
|
|
14
13
|
import type { Host } from "../host.ts";
|
|
15
|
-
import type {
|
|
14
|
+
import type { ManifestEntry } from "../manifest.ts";
|
|
15
|
+
import type { HeldScripts } from "../roots.ts";
|
|
16
|
+
import { moduleIdentity, type NotBuilt } from "../load/discover.ts";
|
|
16
17
|
import { partialNotes } from "../partial.ts";
|
|
17
18
|
import {
|
|
18
19
|
buildReport,
|
|
19
|
-
LANGUAGE,
|
|
20
20
|
capFindings,
|
|
21
21
|
sortFindings,
|
|
22
|
-
type Report,
|
|
23
22
|
type ReportConfiguration,
|
|
24
23
|
type ReportNotBuilt,
|
|
24
|
+
type ReportNote,
|
|
25
25
|
} from "../report.ts";
|
|
26
|
+
import { reportRefusals } from "../report-schema.ts";
|
|
26
27
|
import { json } from "../reporters/json.ts";
|
|
27
28
|
import { RENDERINGS } from "../reporters/reporters.ts";
|
|
28
29
|
import { parseTemplate, TemplateError, type Template } from "../reporters/template.ts";
|
|
29
30
|
import { resolve } from "../resolve.ts";
|
|
30
31
|
import { otherSchemeRows, rowReason, writeBaseline, type Recorded } from "../suppress-file.ts";
|
|
31
|
-
import {
|
|
32
|
-
import
|
|
32
|
+
import type { UnseenLoad } from "../unseen-loads.ts";
|
|
33
|
+
import type { UnreadInput } from "../load/plan.ts";
|
|
34
|
+
import type { UnresolvedSpecifier } from "../load/specifiers.ts";
|
|
35
|
+
|
|
36
|
+
import { type Verb, type VerbOptions } from "./verb.ts";
|
|
37
|
+
import { UsageError } from "../usage.ts";
|
|
38
|
+
import { bytewise } from "../text/compare.ts";
|
|
39
|
+
import { productFault, failureExit } from "../failure.ts";
|
|
40
|
+
import { diagnose, message } from "../product.ts";
|
|
41
|
+
import { ownVersion } from "./version.ts";
|
|
33
42
|
|
|
34
43
|
/** The options `analyze` takes beside the ones every verb takes. */
|
|
35
44
|
export const ANALYZE_OPTIONS: VerbOptions = {
|
|
@@ -41,17 +50,9 @@ export const ANALYZE_OPTIONS: VerbOptions = {
|
|
|
41
50
|
const EXIT_CODE_ON = "on";
|
|
42
51
|
const EXIT_CODE_OFF = "off";
|
|
43
52
|
|
|
44
|
-
/** A path of the run no report can name, which ends the run with the failure code. */
|
|
45
|
-
class ReportPathError extends Error {
|
|
46
|
-
constructor(message: string) {
|
|
47
|
-
super(message);
|
|
48
|
-
this.name = "ReportPathError";
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
53
|
/** The formats this analyzer renders, for a message that names them. */
|
|
53
54
|
function formatNames(): string {
|
|
54
|
-
return [...RENDERINGS.keys()].sort().join(", ");
|
|
55
|
+
return [...RENDERINGS.keys()].sort(bytewise).join(", ");
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
/** The renderings one invocation writes: the ones it named, else the configuration's. */
|
|
@@ -60,14 +61,12 @@ function formatsOf(named: readonly string[], configured: readonly Format[]): rea
|
|
|
60
61
|
const seen = new Set<string>();
|
|
61
62
|
for (const format of formats) {
|
|
62
63
|
if (!RENDERINGS.has(format as Format)) {
|
|
63
|
-
throw new
|
|
64
|
-
"malformed",
|
|
65
|
-
"",
|
|
64
|
+
throw new UsageError(
|
|
66
65
|
`${JSON.stringify(format)} is not a format this analyzer renders: the formats are ${formatNames()}`,
|
|
67
66
|
);
|
|
68
67
|
}
|
|
69
68
|
if (seen.has(format)) {
|
|
70
|
-
throw new
|
|
69
|
+
throw new UsageError(`the format ${JSON.stringify(format)} is named twice`);
|
|
71
70
|
}
|
|
72
71
|
seen.add(format);
|
|
73
72
|
}
|
|
@@ -85,20 +84,96 @@ function runRelative(invoked: string, path: string): string {
|
|
|
85
84
|
}
|
|
86
85
|
const below = relativePath(invoked, path);
|
|
87
86
|
if (below === undefined) {
|
|
88
|
-
throw
|
|
87
|
+
throw failureExit(
|
|
88
|
+
"unreportable-path",
|
|
89
89
|
`a report names its target relative to the directory the run was invoked from, and ${invoked} does not contain ${path}: run from a directory that does`,
|
|
90
90
|
);
|
|
91
91
|
}
|
|
92
92
|
return below;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* One note per manifest entry the run reads back to no source file: what the entry names
|
|
97
|
+
* reaches nothing, so the findings its file would have rooted are reported.
|
|
98
|
+
*/
|
|
99
|
+
function unmappedNotes(
|
|
100
|
+
entries: readonly ManifestEntry[],
|
|
101
|
+
targetRoot: string,
|
|
102
|
+
): readonly ReportNote[] {
|
|
103
|
+
return entries.map((entry) => {
|
|
104
|
+
const named = relativePath(targetRoot, entry.path) ?? entry.path;
|
|
105
|
+
return {
|
|
106
|
+
kind: "unmapped-entry",
|
|
107
|
+
path: relativePath(targetRoot, entry.dir) ?? ".",
|
|
108
|
+
key: "ts.entry_files",
|
|
109
|
+
message: `the manifest's ${entry.member} names ${named}, which reads back to no source file of the target: name the file it is built from in ts.entry_files`,
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* One note per load of a production file the run cannot resolve, each site once: a file
|
|
116
|
+
* it loads is a root the run does not see, so `ts.roots_complete` lifts no cap there.
|
|
117
|
+
*/
|
|
118
|
+
function unseenLoadNotes(loads: readonly UnseenLoad[]): readonly ReportNote[] {
|
|
119
|
+
return [
|
|
120
|
+
...new Map(
|
|
121
|
+
loads.map((load) => {
|
|
122
|
+
const { path, line, column } = load.position;
|
|
123
|
+
const site = `${path}:${String(line)}:${String(column)}`;
|
|
124
|
+
const note: ReportNote = {
|
|
125
|
+
kind: "computed-load",
|
|
126
|
+
path: dirnamePath(path),
|
|
127
|
+
message: `${site} loads a module by a computed path the analysis cannot resolve, so the configurations compiling it report what no root reaches at probable at most, whatever ts.roots_complete says`,
|
|
128
|
+
};
|
|
129
|
+
return [site, note] as const;
|
|
130
|
+
}),
|
|
131
|
+
).values(),
|
|
132
|
+
];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** One note per package whose command strings name files a configuration holds, naming every such file. */
|
|
136
|
+
function heldScriptNotes(held: readonly HeldScripts[], targetRoot: string): readonly ReportNote[] {
|
|
137
|
+
const files = new Map<string, Set<string>>();
|
|
138
|
+
for (const one of held) {
|
|
139
|
+
files.set(one.dir, new Set([...(files.get(one.dir) ?? []), ...one.files]));
|
|
140
|
+
}
|
|
141
|
+
return [...files].map(([dir, paths]) => ({
|
|
142
|
+
kind: "unread-command",
|
|
143
|
+
path: relativePath(targetRoot, dir) ?? ".",
|
|
144
|
+
message: `commands the analysis does not read name ${[...paths].sort(bytewise).join(", ")}, so no finding is reported about ${paths.size === 1 ? "that file itself or the names it exports" : "those files themselves or the names they export"}`,
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function unresolvedNotes(specifiers: readonly UnresolvedSpecifier[]): readonly ReportNote[] {
|
|
149
|
+
return specifiers.map(({ specifier, position }) => ({
|
|
150
|
+
kind: "unresolved-specifier",
|
|
151
|
+
path: dirnamePath(position.path),
|
|
152
|
+
message: `${position.path}:${String(position.line)}:${String(position.column)} loads ${JSON.stringify(specifier)}, which resolves to no file, so it references nothing`,
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function unreadLine(input: UnreadInput, targetRoot: string): string {
|
|
157
|
+
switch (input.kind) {
|
|
158
|
+
case "manifest": {
|
|
159
|
+
const path = relativePath(targetRoot, input.manifest) ?? input.manifest;
|
|
160
|
+
return input.reason === undefined
|
|
161
|
+
? `${path} does not parse, so the manifest facts of the program formed for its package are unanswered: correct the manifest`
|
|
162
|
+
: `${path} cannot be read, so the manifest facts of the program formed for its package are unanswered: give the run read access to it: ${input.reason}`;
|
|
163
|
+
}
|
|
164
|
+
case "ignore-file":
|
|
165
|
+
return `${relativePath(targetRoot, input.path) ?? input.path} cannot be read, so the JavaScript files it would ignore are analyzed: ${input.reason}`;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
95
169
|
/** Each project the run dropped as a report names it, by its configuration file below the target. */
|
|
96
170
|
function notBuiltOf(dropped: readonly NotBuilt[], targetRoot: string): readonly ReportNotBuilt[] {
|
|
97
171
|
return dropped.map((one) => {
|
|
98
172
|
const project = relativePath(targetRoot, one.configFile);
|
|
99
173
|
if (project === undefined) {
|
|
100
|
-
throw
|
|
101
|
-
|
|
174
|
+
throw failureExit(
|
|
175
|
+
"unreportable-path",
|
|
176
|
+
`the project ${one.id} would be opened from ${one.configFile}, which is not below the target ${targetRoot}, so no report can name it: analyze a target that holds it`,
|
|
102
177
|
);
|
|
103
178
|
}
|
|
104
179
|
return { id: one.id, project, error: one.error };
|
|
@@ -113,14 +188,33 @@ function configurationsOf(
|
|
|
113
188
|
return projects.map((one) => {
|
|
114
189
|
const project = relativePath(targetRoot, one.configFile);
|
|
115
190
|
if (project === undefined) {
|
|
116
|
-
throw
|
|
117
|
-
|
|
191
|
+
throw failureExit(
|
|
192
|
+
"unreportable-path",
|
|
193
|
+
`the project ${one.id} is opened from ${one.configFile}, which is not below the target ${targetRoot}, so no report can name it: analyze a target that holds it`,
|
|
118
194
|
);
|
|
119
195
|
}
|
|
120
196
|
return { id: one.id, project };
|
|
121
197
|
});
|
|
122
198
|
}
|
|
123
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Writes one document the run was asked for. A document that cannot be produced or written
|
|
202
|
+
* ends the run with the run failure that names it and `fix`, the documents written before it
|
|
203
|
+
* kept.
|
|
204
|
+
*/
|
|
205
|
+
function writeDocument(
|
|
206
|
+
host: Host,
|
|
207
|
+
path: string,
|
|
208
|
+
fix: string,
|
|
209
|
+
produce: () => Iterable<string>,
|
|
210
|
+
): void {
|
|
211
|
+
try {
|
|
212
|
+
host.writeDocument(path, produce());
|
|
213
|
+
} catch (error: unknown) {
|
|
214
|
+
throw failureExit("unwritten-output", `${path}: ${message(error)}: ${fix}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
124
218
|
/** A count with its noun, so a message reads for one record as well as for several. */
|
|
125
219
|
function counted(n: number, noun: string): string {
|
|
126
220
|
return `${String(n)} ${noun}${n === 1 ? "" : "s"}`;
|
|
@@ -139,9 +233,7 @@ function templateOf(
|
|
|
139
233
|
): Template | undefined {
|
|
140
234
|
if (named === undefined || named === "") {
|
|
141
235
|
if (formats.includes("template")) {
|
|
142
|
-
throw new
|
|
143
|
-
"malformed",
|
|
144
|
-
"",
|
|
236
|
+
throw new UsageError(
|
|
145
237
|
"the template format renders the template --template names, and none was named",
|
|
146
238
|
);
|
|
147
239
|
}
|
|
@@ -151,11 +243,7 @@ function templateOf(
|
|
|
151
243
|
try {
|
|
152
244
|
source = host.readFile(resolvePath(invoked, named));
|
|
153
245
|
} catch (error: unknown) {
|
|
154
|
-
throw new
|
|
155
|
-
"malformed",
|
|
156
|
-
"",
|
|
157
|
-
`--template=${named}: ${error instanceof Error ? error.message : String(error)}`,
|
|
158
|
-
);
|
|
246
|
+
throw new UsageError(`--template=${named}: ${message(error)}`);
|
|
159
247
|
}
|
|
160
248
|
try {
|
|
161
249
|
return parseTemplate(source);
|
|
@@ -163,7 +251,7 @@ function templateOf(
|
|
|
163
251
|
if (!(error instanceof TemplateError)) {
|
|
164
252
|
throw error;
|
|
165
253
|
}
|
|
166
|
-
throw new
|
|
254
|
+
throw new UsageError(`--template=${named}: ${error.message}`);
|
|
167
255
|
}
|
|
168
256
|
}
|
|
169
257
|
|
|
@@ -185,25 +273,17 @@ interface Documents {
|
|
|
185
273
|
export const analyzeVerb: Verb = ({ err, host, inputs, scope, openClient, option, repeated }) => {
|
|
186
274
|
const reportPath = option("report");
|
|
187
275
|
if (reportPath === undefined || reportPath === "") {
|
|
188
|
-
throw new
|
|
189
|
-
"malformed",
|
|
190
|
-
"",
|
|
276
|
+
throw new UsageError(
|
|
191
277
|
"analyze writes its report to the path --report names, and no path was named",
|
|
192
278
|
);
|
|
193
279
|
}
|
|
194
280
|
const exitCode = option("exit-code") ?? EXIT_CODE_ON;
|
|
195
281
|
if (exitCode !== EXIT_CODE_ON && exitCode !== EXIT_CODE_OFF) {
|
|
196
|
-
throw new
|
|
197
|
-
"malformed",
|
|
198
|
-
"",
|
|
282
|
+
throw new UsageError(
|
|
199
283
|
`--exit-code=${JSON.stringify(exitCode)}: the values are ${EXIT_CODE_ON} and ${EXIT_CODE_OFF}`,
|
|
200
284
|
);
|
|
201
285
|
}
|
|
202
286
|
const { config, provenance } = resolve(inputs());
|
|
203
|
-
const kind = config.targetKind;
|
|
204
|
-
if (kind === "") {
|
|
205
|
-
throw new ConfigError("missing-target-kind", "target.kind", "target.kind is not set");
|
|
206
|
-
}
|
|
207
287
|
const invoked = host.workingDirectory();
|
|
208
288
|
const baseline = option("baseline-write");
|
|
209
289
|
const documents: Documents = {
|
|
@@ -215,146 +295,150 @@ export const analyzeVerb: Verb = ({ err, host, inputs, scope, openClient, option
|
|
|
215
295
|
const scoped = scope();
|
|
216
296
|
const targetRoot = scoped.target.path;
|
|
217
297
|
|
|
218
|
-
|
|
219
|
-
try {
|
|
220
|
-
version = host.analyzerVersion();
|
|
221
|
-
} catch (error: unknown) {
|
|
222
|
-
err.write(`deadset-ts: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
223
|
-
return EXIT_FAILURE;
|
|
224
|
-
}
|
|
298
|
+
const version = ownVersion(host);
|
|
225
299
|
|
|
226
|
-
let report: Report;
|
|
227
300
|
let recorded: readonly Recorded[];
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
let
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
host,
|
|
245
|
-
scoped,
|
|
246
|
-
config,
|
|
247
|
-
provenance,
|
|
248
|
-
mode,
|
|
249
|
-
reason,
|
|
250
|
-
);
|
|
251
|
-
analysis = write.first;
|
|
252
|
-
recorded = write.rows;
|
|
253
|
-
}
|
|
254
|
-
const { result, run } = analysis;
|
|
255
|
-
warnings = run.componentWarnings;
|
|
256
|
-
unread = run.componentsUnread.map(
|
|
257
|
-
({ configFile, reason }) =>
|
|
258
|
-
`${relativePath(resolvePath(invoked, targetRoot), configFile) ?? configFile}: its component files are not read: ${reason}`,
|
|
259
|
-
);
|
|
260
|
-
unfound = run.librariesUnfound.map(
|
|
261
|
-
({ configFile, error }) =>
|
|
262
|
-
`${relativePath(resolvePath(invoked, targetRoot), configFile) ?? configFile}: ${error}: the configuration is analyzed without that library`,
|
|
301
|
+
const root = runRelative(invoked, targetRoot);
|
|
302
|
+
const mode = { production: true };
|
|
303
|
+
let analysis: RunAnalysis;
|
|
304
|
+
if (documents.baseline === undefined) {
|
|
305
|
+
analysis = runAnalysis(openClient(config), host, scoped, config, provenance, mode);
|
|
306
|
+
recorded = [];
|
|
307
|
+
} else {
|
|
308
|
+
const reason = rowReason(analyzerProvenance(version));
|
|
309
|
+
const write = runBaselineWrite(
|
|
310
|
+
openClient(config),
|
|
311
|
+
host,
|
|
312
|
+
scoped,
|
|
313
|
+
config,
|
|
314
|
+
provenance,
|
|
315
|
+
mode,
|
|
316
|
+
reason,
|
|
263
317
|
);
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
unanswered: run.unanswered,
|
|
267
|
-
targetRoot: resolvePath(invoked, targetRoot),
|
|
268
|
-
});
|
|
269
|
-
const built = buildReport({
|
|
270
|
-
contractVersion: CONTRACT_VERSION,
|
|
271
|
-
version,
|
|
272
|
-
conformance: CONFORMANCE,
|
|
273
|
-
declaredGaps: DECLARED_GAPS,
|
|
274
|
-
target: {
|
|
275
|
-
kind: kindOf(kind, LANGUAGE),
|
|
276
|
-
root,
|
|
277
|
-
identity: moduleIdentity(host, scoped.target),
|
|
278
|
-
},
|
|
279
|
-
configurations: configurationsOf(run.projects, targetRoot),
|
|
280
|
-
notBuilt: notBuiltOf(run.notBuilt, targetRoot),
|
|
281
|
-
loaded: run.consumers.map((consumer) => ({
|
|
282
|
-
id: consumer.id,
|
|
283
|
-
path: runRelative(invoked, consumer.path),
|
|
284
|
-
})),
|
|
285
|
-
unavailable: [],
|
|
286
|
-
result,
|
|
287
|
-
testFileRules: run.testFileRules,
|
|
288
|
-
typeErrorSkips: run.typeErrorSkips,
|
|
289
|
-
unanswered: unansweredCounts(analysis),
|
|
290
|
-
conventionsApplied: run.conventionsApplied,
|
|
291
|
-
});
|
|
292
|
-
report = capFindings(sortFindings(built, config.reporters.sort), config.reporters.maxFindings);
|
|
293
|
-
verdict = verdictOf(result, config);
|
|
294
|
-
} catch (error: unknown) {
|
|
295
|
-
if (!(error instanceof ReportPathError)) {
|
|
296
|
-
throw error;
|
|
297
|
-
}
|
|
298
|
-
err.write(`deadset-ts: ${error.message}\n`);
|
|
299
|
-
return EXIT_FAILURE;
|
|
318
|
+
analysis = write.first;
|
|
319
|
+
recorded = write.rows;
|
|
300
320
|
}
|
|
321
|
+
const { result, run } = analysis;
|
|
322
|
+
const warnings = run.componentWarnings;
|
|
323
|
+
const unread = run.componentsUnread.map(
|
|
324
|
+
({ configFile, reason }) =>
|
|
325
|
+
`${relativePath(resolvePath(invoked, targetRoot), configFile) ?? configFile}: its component files are not read: ${reason}`,
|
|
326
|
+
);
|
|
327
|
+
const unfound = run.librariesUnfound.map(
|
|
328
|
+
({ configFile, error }) =>
|
|
329
|
+
`${relativePath(resolvePath(invoked, targetRoot), configFile) ?? configFile}: ${error}: the configuration is analyzed without that library`,
|
|
330
|
+
);
|
|
331
|
+
const unreadInputs = run.unread.map((one) => unreadLine(one, resolvePath(invoked, targetRoot)));
|
|
301
332
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
333
|
+
const partial = partialNotes({
|
|
334
|
+
notBuilt: run.notBuilt,
|
|
335
|
+
unanswered: run.unanswered,
|
|
336
|
+
targetRoot: resolvePath(invoked, targetRoot),
|
|
337
|
+
});
|
|
338
|
+
const built = buildReport({
|
|
339
|
+
contractVersion: CONTRACT_VERSION,
|
|
340
|
+
version,
|
|
341
|
+
conformance: CONFORMANCE,
|
|
342
|
+
declaredGaps: DECLARED_GAPS,
|
|
343
|
+
target: {
|
|
344
|
+
kind: kindOf(config.target.kind),
|
|
345
|
+
root,
|
|
346
|
+
identity: moduleIdentity(host, scoped.target),
|
|
347
|
+
},
|
|
348
|
+
configurations: configurationsOf(run.projects, targetRoot),
|
|
349
|
+
notBuilt: notBuiltOf(run.notBuilt, targetRoot),
|
|
350
|
+
loaded: run.consumers.map((consumer) => ({
|
|
351
|
+
id: consumer.id,
|
|
352
|
+
path: runRelative(invoked, consumer.path),
|
|
353
|
+
})),
|
|
354
|
+
unavailable: [],
|
|
355
|
+
result,
|
|
356
|
+
testFileRules: run.testFileRules,
|
|
357
|
+
typeErrorSkips: run.typeErrorSkips,
|
|
358
|
+
unanswered: unansweredCounts(analysis),
|
|
359
|
+
conventionsApplied: run.conventionsApplied,
|
|
360
|
+
listsComponents: config.reporters.cascade === "full",
|
|
361
|
+
notes: [
|
|
362
|
+
...unmappedNotes(run.unmappedEntries, resolvePath(invoked, targetRoot)),
|
|
363
|
+
...unseenLoadNotes(run.unseenLoads),
|
|
364
|
+
...unresolvedNotes(run.unresolvedSpecifiers),
|
|
365
|
+
...heldScriptNotes(run.heldScripts, resolvePath(invoked, targetRoot)),
|
|
366
|
+
],
|
|
367
|
+
});
|
|
368
|
+
const report = capFindings(
|
|
369
|
+
sortFindings(built, config.reporters.sort),
|
|
370
|
+
config.reporters.max_findings,
|
|
371
|
+
);
|
|
372
|
+
const verdict = verdictOf(result, config);
|
|
373
|
+
|
|
374
|
+
const [refused] = reportRefusals(report);
|
|
375
|
+
if (refused !== undefined) {
|
|
376
|
+
throw productFault(
|
|
377
|
+
`built a report that breaks the report schema at ${refused.pointer || "the document"}: ${refused.keyword}: ${refused.reason}`,
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
writeDocument(host, documents.report, "name a path the run can write", () => json(report));
|
|
381
|
+
const options = {
|
|
382
|
+
readSource: (path: string) => host.readFile(joinPath(targetRoot, path)),
|
|
383
|
+
template,
|
|
384
|
+
};
|
|
385
|
+
for (const format of documents.formats) {
|
|
386
|
+
const rendering = RENDERINGS.get(format);
|
|
387
|
+
if (rendering !== undefined) {
|
|
388
|
+
writeDocument(
|
|
389
|
+
host,
|
|
390
|
+
documents.report + rendering.suffix,
|
|
391
|
+
"name a path the run can write, or correct the template that renders it",
|
|
392
|
+
() => rendering.render(report, options),
|
|
323
393
|
);
|
|
324
394
|
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
|
|
395
|
+
}
|
|
396
|
+
const baselinePath = documents.baseline;
|
|
397
|
+
if (baselinePath !== undefined) {
|
|
398
|
+
writeDocument(
|
|
399
|
+
host,
|
|
400
|
+
baselinePath,
|
|
401
|
+
"name a path the run can write, or repair the rows of the baseline the run would keep",
|
|
402
|
+
() => {
|
|
403
|
+
const kept =
|
|
404
|
+
host.kindOf(baselinePath) === "file" ? otherSchemeRows(host.readFile(baselinePath)) : [];
|
|
405
|
+
return writeBaseline(recorded, analyzerProvenance(version), kept);
|
|
406
|
+
},
|
|
407
|
+
);
|
|
328
408
|
}
|
|
329
409
|
|
|
330
410
|
for (const note of partial) {
|
|
331
|
-
err
|
|
411
|
+
diagnose(err, note);
|
|
332
412
|
}
|
|
333
|
-
for (const line of [...unread, ...unfound]) {
|
|
334
|
-
err
|
|
413
|
+
for (const line of [...unread, ...unfound, ...unreadInputs]) {
|
|
414
|
+
diagnose(err, line);
|
|
335
415
|
}
|
|
336
416
|
for (const warning of warnings) {
|
|
337
|
-
|
|
338
|
-
|
|
417
|
+
diagnose(
|
|
418
|
+
err,
|
|
419
|
+
`${warning.path}:${String(warning.line)}: a <script> start tag that begins a line is not read as a block: ${warning.reason}`,
|
|
339
420
|
);
|
|
340
421
|
}
|
|
341
422
|
for (const skip of report.type_error_skips) {
|
|
342
|
-
|
|
343
|
-
|
|
423
|
+
diagnose(
|
|
424
|
+
err,
|
|
425
|
+
`${skip.path}:${String(skip.line)}: ${skip.message}: the function or statement holding this type error is not evaluated`,
|
|
344
426
|
);
|
|
345
427
|
}
|
|
346
428
|
if (report.totals.pending > 0) {
|
|
347
|
-
|
|
348
|
-
|
|
429
|
+
diagnose(
|
|
430
|
+
err,
|
|
431
|
+
`${counted(report.totals.pending, "pending finding")}: the report names a declared cross-language edge, and no merge has resolved it`,
|
|
349
432
|
);
|
|
350
433
|
}
|
|
351
434
|
if (report.totals.stale_suppressions > 0) {
|
|
352
|
-
err
|
|
435
|
+
diagnose(err, counted(report.totals.stale_suppressions, "stale suppression"));
|
|
353
436
|
}
|
|
354
437
|
if (exitCode === EXIT_CODE_OFF) {
|
|
355
438
|
if (verdict !== EXIT_CLEAN) {
|
|
356
|
-
|
|
357
|
-
|
|
439
|
+
diagnose(
|
|
440
|
+
err,
|
|
441
|
+
`the exit code is configured off: the verdict of this run is ${String(verdict)}`,
|
|
358
442
|
);
|
|
359
443
|
}
|
|
360
444
|
return EXIT_CLEAN;
|
package/src/verbs/describe.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CONTRACT_VERSION,
|
|
3
|
+
EXIT_CLEAN,
|
|
4
|
+
EXIT_USAGE,
|
|
5
|
+
SCHEMA_VERSIONS,
|
|
6
|
+
} from "../contract/contract.generated.ts";
|
|
1
7
|
import { CONFORMANCE, type Conformance } from "../conformance.ts";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
8
|
+
import { type Verb } from "./verb.ts";
|
|
9
|
+
import { diagnose, LANGUAGE, PRODUCT } from "../product.ts";
|
|
10
|
+
import { ownVersion } from "./version.ts";
|
|
5
11
|
|
|
6
12
|
/**
|
|
7
13
|
* The document `describe` writes: the analyzer, the Contract version it implements, the
|
|
@@ -10,10 +16,10 @@ import { EXIT_CLEAN, EXIT_FAILURE, EXIT_USAGE, type Verb } from "./verb.ts";
|
|
|
10
16
|
*/
|
|
11
17
|
export function describeDocument(version: string, recorded: Conformance | undefined): string {
|
|
12
18
|
const document = {
|
|
13
|
-
name:
|
|
19
|
+
name: PRODUCT,
|
|
14
20
|
version,
|
|
15
21
|
contract_version: CONTRACT_VERSION,
|
|
16
|
-
schema_versions_accepted:
|
|
22
|
+
schema_versions_accepted: SCHEMA_VERSIONS,
|
|
17
23
|
languages: [LANGUAGE],
|
|
18
24
|
...(recorded === undefined
|
|
19
25
|
? {}
|
|
@@ -28,19 +34,12 @@ export function describeDocument(version: string, recorded: Conformance | undefi
|
|
|
28
34
|
return `${JSON.stringify(document, null, 2)}\n`;
|
|
29
35
|
}
|
|
30
36
|
|
|
31
|
-
/** Writes this analyzer's description to the output stream
|
|
37
|
+
/** Writes this analyzer's description to the output stream. */
|
|
32
38
|
export const describeVerb: Verb = ({ out, err, host, args }) => {
|
|
33
39
|
if (args.length > 0) {
|
|
34
|
-
err
|
|
40
|
+
diagnose(err, `describe takes no argument, got ${JSON.stringify(args[0])}`);
|
|
35
41
|
return EXIT_USAGE;
|
|
36
42
|
}
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
version = host.analyzerVersion();
|
|
40
|
-
} catch (error: unknown) {
|
|
41
|
-
err.write(`deadset-ts: describe: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
42
|
-
return EXIT_FAILURE;
|
|
43
|
-
}
|
|
44
|
-
out.write(describeDocument(version, CONFORMANCE));
|
|
43
|
+
out.write(describeDocument(ownVersion(host), CONFORMANCE));
|
|
45
44
|
return EXIT_CLEAN;
|
|
46
45
|
};
|