@cplieger/deadset-ts 8.0.0-dev.1 → 8.0.0-dev.11
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 +181 -341
- package/dist/src/augmentations.js +106 -13
- package/dist/src/automatic-types.js +10 -29
- package/dist/src/build-scripts.js +425 -0
- package/dist/src/component-layer.js +4 -2
- package/dist/src/components.js +2 -3
- package/dist/src/config.js +6 -82
- package/dist/src/configuration-files.js +28 -176
- package/dist/src/configured-declarations.js +15 -20
- package/dist/src/conformance-record.js +1 -1
- 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 +73 -3
- package/dist/src/conventions.js +36 -66
- package/dist/src/dependencies.js +103 -40
- package/dist/src/edges.js +38 -122
- package/dist/src/element-access.js +4 -6
- package/dist/src/entry-point-gaps.js +2 -8
- package/dist/src/entry-points.js +11 -27
- 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 +139 -72
- package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
- package/dist/src/findings/emitters.js +5 -5
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +13 -10
- package/dist/src/findings/non-code-artifacts.js +7 -8
- 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 +41 -32
- package/dist/src/findings/visibility-narrowing.js +19 -16
- 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 +7 -4
- package/dist/src/git-ignore.js +39 -26
- package/dist/src/graph.js +5 -3
- package/dist/src/html-entries.js +6 -33
- package/dist/src/in-operands.js +2 -1
- package/dist/src/index.js +1 -1
- package/dist/src/injection-container.js +1 -1
- package/dist/src/intra-function-parts.js +63 -43
- package/dist/src/inventory.js +2 -21
- 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} +89 -66
- package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +23 -65
- 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 +76 -4
- package/dist/src/mdx-entries.js +3 -26
- package/dist/src/outside-tests.js +1 -1
- 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 -79
- package/dist/src/product.js +13 -0
- package/dist/src/query.js +1 -0
- package/dist/src/ref.js +1 -0
- package/dist/src/references.js +48 -30
- package/dist/src/report-schema.js +51 -0
- package/dist/src/report.js +57 -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 -729
- package/dist/src/roots.js +273 -87
- package/dist/src/run.js +66 -77
- package/dist/src/schema.js +320 -343
- package/dist/src/scope.js +38 -115
- package/dist/src/serialization-contract.js +595 -21
- package/dist/src/session.js +34 -29
- package/dist/src/source-files.js +1 -1
- package/dist/src/suppress-file.js +25 -28
- package/dist/src/suppress-inline.js +3 -2
- package/dist/src/suppress.js +33 -51
- package/dist/src/sweep.js +7 -9
- package/dist/src/symbol-keys.js +3 -5
- package/dist/src/template-field.js +9 -19
- package/dist/src/test-support.js +8 -11
- 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 +11 -232
- 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 +166 -126
- package/dist/src/verbs/describe.js +9 -16
- package/dist/src/verbs/explain.js +10 -7
- 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 +62 -64
- package/jsr.json +1 -1
- package/package.json +5 -4
- package/src/analysis.ts +245 -502
- package/src/augmentations.ts +135 -17
- package/src/automatic-types.ts +11 -29
- package/src/build-scripts.ts +543 -0
- package/src/component-layer.ts +4 -2
- package/src/components.ts +2 -4
- package/src/config.ts +31 -302
- package/src/configuration-files.ts +34 -179
- package/src/configured-declarations.ts +18 -23
- package/src/conformance-record.ts +1 -1
- 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 +85 -3
- package/src/conventions.ts +46 -84
- package/src/dependencies.ts +118 -46
- package/src/edges.ts +41 -140
- package/src/element-access.ts +4 -7
- package/src/entry-point-gaps.ts +3 -9
- package/src/entry-points.ts +12 -38
- 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 +14 -9
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +181 -84
- package/src/findings/dependencies-and-module-machinery.ts +4 -7
- package/src/findings/emitter.ts +8 -9
- package/src/findings/emitters.ts +7 -7
- package/src/findings/interfaces.ts +4 -6
- package/src/findings/intra-function.ts +13 -11
- package/src/findings/non-code-artifacts.ts +9 -11
- package/src/findings/reads-and-writes.ts +20 -16
- package/src/findings/self-check.ts +11 -17
- package/src/findings/unused-declarations.ts +45 -32
- package/src/findings/visibility-narrowing.ts +20 -17
- 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 +7 -4
- package/src/git-ignore.ts +50 -26
- package/src/graph.ts +5 -3
- package/src/host.ts +12 -5
- package/src/html-entries.ts +6 -33
- package/src/in-operands.ts +2 -1
- package/src/index.ts +1 -1
- package/src/injection-container.ts +1 -1
- package/src/intra-function-parts.ts +73 -44
- package/src/inventory.ts +2 -20
- 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} +128 -85
- package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +33 -76
- 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 +122 -4
- package/src/mdx-entries.ts +3 -26
- package/src/outside-tests.ts +1 -1
- package/src/partial.ts +3 -2
- package/src/paths.ts +27 -15
- package/src/position.ts +2 -1
- package/src/print.ts +12 -92
- package/src/product.ts +18 -0
- package/src/query.ts +11 -0
- package/src/ref.ts +1 -0
- package/src/references.ts +52 -31
- package/src/report-schema.ts +68 -0
- package/src/report.ts +101 -224
- 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 -1114
- package/src/roots.ts +339 -104
- package/src/run.ts +75 -91
- package/src/schema.ts +391 -388
- package/src/scope.ts +46 -138
- package/src/serialization-contract.ts +763 -22
- package/src/session.ts +70 -54
- package/src/source-files.ts +1 -1
- package/src/suppress-file.ts +24 -33
- package/src/suppress-inline.ts +2 -2
- package/src/suppress.ts +43 -58
- package/src/sweep.ts +23 -9
- package/src/symbol-keys.ts +2 -5
- package/src/template-field.ts +15 -25
- package/src/test-support.ts +17 -13
- 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 +18 -288
- package/src/unseen-loads.ts +134 -0
- package/src/unwrap.ts +34 -0
- package/src/usage.ts +10 -0
- package/src/verbs/analyze.ts +224 -162
- package/src/verbs/describe.ts +14 -15
- package/src/verbs/explain.ts +10 -11
- 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 +74 -64
- package/dist/src/base-url.js +0 -18
- package/dist/src/discover.js +0 -468
- 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/base-url.ts +0 -24
- package/src/discover.ts +0 -653
- 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,37 +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
14
|
import type { ManifestEntry } from "../manifest.ts";
|
|
16
|
-
import type {
|
|
15
|
+
import type { HeldScripts } from "../roots.ts";
|
|
16
|
+
import { moduleIdentity, type NotBuilt } from "../load/discover.ts";
|
|
17
17
|
import { partialNotes } from "../partial.ts";
|
|
18
18
|
import {
|
|
19
19
|
buildReport,
|
|
20
|
-
LANGUAGE,
|
|
21
20
|
capFindings,
|
|
22
21
|
sortFindings,
|
|
23
|
-
type Report,
|
|
24
22
|
type ReportConfiguration,
|
|
25
23
|
type ReportNotBuilt,
|
|
26
24
|
type ReportNote,
|
|
27
25
|
} from "../report.ts";
|
|
26
|
+
import { reportRefusals } from "../report-schema.ts";
|
|
28
27
|
import { json } from "../reporters/json.ts";
|
|
29
28
|
import { RENDERINGS } from "../reporters/reporters.ts";
|
|
30
29
|
import { parseTemplate, TemplateError, type Template } from "../reporters/template.ts";
|
|
31
30
|
import { resolve } from "../resolve.ts";
|
|
32
31
|
import { otherSchemeRows, rowReason, writeBaseline, type Recorded } from "../suppress-file.ts";
|
|
33
|
-
import {
|
|
34
|
-
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";
|
|
35
42
|
|
|
36
43
|
/** The options `analyze` takes beside the ones every verb takes. */
|
|
37
44
|
export const ANALYZE_OPTIONS: VerbOptions = {
|
|
@@ -43,17 +50,9 @@ export const ANALYZE_OPTIONS: VerbOptions = {
|
|
|
43
50
|
const EXIT_CODE_ON = "on";
|
|
44
51
|
const EXIT_CODE_OFF = "off";
|
|
45
52
|
|
|
46
|
-
/** A path of the run no report can name, which ends the run with the failure code. */
|
|
47
|
-
class ReportPathError extends Error {
|
|
48
|
-
constructor(message: string) {
|
|
49
|
-
super(message);
|
|
50
|
-
this.name = "ReportPathError";
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
53
|
/** The formats this analyzer renders, for a message that names them. */
|
|
55
54
|
function formatNames(): string {
|
|
56
|
-
return [...RENDERINGS.keys()].sort().join(", ");
|
|
55
|
+
return [...RENDERINGS.keys()].sort(bytewise).join(", ");
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
/** The renderings one invocation writes: the ones it named, else the configuration's. */
|
|
@@ -62,14 +61,12 @@ function formatsOf(named: readonly string[], configured: readonly Format[]): rea
|
|
|
62
61
|
const seen = new Set<string>();
|
|
63
62
|
for (const format of formats) {
|
|
64
63
|
if (!RENDERINGS.has(format as Format)) {
|
|
65
|
-
throw new
|
|
66
|
-
"malformed",
|
|
67
|
-
"",
|
|
64
|
+
throw new UsageError(
|
|
68
65
|
`${JSON.stringify(format)} is not a format this analyzer renders: the formats are ${formatNames()}`,
|
|
69
66
|
);
|
|
70
67
|
}
|
|
71
68
|
if (seen.has(format)) {
|
|
72
|
-
throw new
|
|
69
|
+
throw new UsageError(`the format ${JSON.stringify(format)} is named twice`);
|
|
73
70
|
}
|
|
74
71
|
seen.add(format);
|
|
75
72
|
}
|
|
@@ -87,7 +84,8 @@ function runRelative(invoked: string, path: string): string {
|
|
|
87
84
|
}
|
|
88
85
|
const below = relativePath(invoked, path);
|
|
89
86
|
if (below === undefined) {
|
|
90
|
-
throw
|
|
87
|
+
throw failureExit(
|
|
88
|
+
"unreportable-path",
|
|
91
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`,
|
|
92
90
|
);
|
|
93
91
|
}
|
|
@@ -113,13 +111,69 @@ function unmappedNotes(
|
|
|
113
111
|
});
|
|
114
112
|
}
|
|
115
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
|
+
|
|
116
169
|
/** Each project the run dropped as a report names it, by its configuration file below the target. */
|
|
117
170
|
function notBuiltOf(dropped: readonly NotBuilt[], targetRoot: string): readonly ReportNotBuilt[] {
|
|
118
171
|
return dropped.map((one) => {
|
|
119
172
|
const project = relativePath(targetRoot, one.configFile);
|
|
120
173
|
if (project === undefined) {
|
|
121
|
-
throw
|
|
122
|
-
|
|
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`,
|
|
123
177
|
);
|
|
124
178
|
}
|
|
125
179
|
return { id: one.id, project, error: one.error };
|
|
@@ -134,14 +188,33 @@ function configurationsOf(
|
|
|
134
188
|
return projects.map((one) => {
|
|
135
189
|
const project = relativePath(targetRoot, one.configFile);
|
|
136
190
|
if (project === undefined) {
|
|
137
|
-
throw
|
|
138
|
-
|
|
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`,
|
|
139
194
|
);
|
|
140
195
|
}
|
|
141
196
|
return { id: one.id, project };
|
|
142
197
|
});
|
|
143
198
|
}
|
|
144
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
|
+
|
|
145
218
|
/** A count with its noun, so a message reads for one record as well as for several. */
|
|
146
219
|
function counted(n: number, noun: string): string {
|
|
147
220
|
return `${String(n)} ${noun}${n === 1 ? "" : "s"}`;
|
|
@@ -160,9 +233,7 @@ function templateOf(
|
|
|
160
233
|
): Template | undefined {
|
|
161
234
|
if (named === undefined || named === "") {
|
|
162
235
|
if (formats.includes("template")) {
|
|
163
|
-
throw new
|
|
164
|
-
"malformed",
|
|
165
|
-
"",
|
|
236
|
+
throw new UsageError(
|
|
166
237
|
"the template format renders the template --template names, and none was named",
|
|
167
238
|
);
|
|
168
239
|
}
|
|
@@ -172,11 +243,7 @@ function templateOf(
|
|
|
172
243
|
try {
|
|
173
244
|
source = host.readFile(resolvePath(invoked, named));
|
|
174
245
|
} catch (error: unknown) {
|
|
175
|
-
throw new
|
|
176
|
-
"malformed",
|
|
177
|
-
"",
|
|
178
|
-
`--template=${named}: ${error instanceof Error ? error.message : String(error)}`,
|
|
179
|
-
);
|
|
246
|
+
throw new UsageError(`--template=${named}: ${message(error)}`);
|
|
180
247
|
}
|
|
181
248
|
try {
|
|
182
249
|
return parseTemplate(source);
|
|
@@ -184,7 +251,7 @@ function templateOf(
|
|
|
184
251
|
if (!(error instanceof TemplateError)) {
|
|
185
252
|
throw error;
|
|
186
253
|
}
|
|
187
|
-
throw new
|
|
254
|
+
throw new UsageError(`--template=${named}: ${error.message}`);
|
|
188
255
|
}
|
|
189
256
|
}
|
|
190
257
|
|
|
@@ -206,25 +273,17 @@ interface Documents {
|
|
|
206
273
|
export const analyzeVerb: Verb = ({ err, host, inputs, scope, openClient, option, repeated }) => {
|
|
207
274
|
const reportPath = option("report");
|
|
208
275
|
if (reportPath === undefined || reportPath === "") {
|
|
209
|
-
throw new
|
|
210
|
-
"malformed",
|
|
211
|
-
"",
|
|
276
|
+
throw new UsageError(
|
|
212
277
|
"analyze writes its report to the path --report names, and no path was named",
|
|
213
278
|
);
|
|
214
279
|
}
|
|
215
280
|
const exitCode = option("exit-code") ?? EXIT_CODE_ON;
|
|
216
281
|
if (exitCode !== EXIT_CODE_ON && exitCode !== EXIT_CODE_OFF) {
|
|
217
|
-
throw new
|
|
218
|
-
"malformed",
|
|
219
|
-
"",
|
|
282
|
+
throw new UsageError(
|
|
220
283
|
`--exit-code=${JSON.stringify(exitCode)}: the values are ${EXIT_CODE_ON} and ${EXIT_CODE_OFF}`,
|
|
221
284
|
);
|
|
222
285
|
}
|
|
223
286
|
const { config, provenance } = resolve(inputs());
|
|
224
|
-
const kind = config.targetKind;
|
|
225
|
-
if (kind === "") {
|
|
226
|
-
throw new ConfigError("missing-target-kind", "target.kind", "target.kind is not set");
|
|
227
|
-
}
|
|
228
287
|
const invoked = host.workingDirectory();
|
|
229
288
|
const baseline = option("baseline-write");
|
|
230
289
|
const documents: Documents = {
|
|
@@ -236,147 +295,150 @@ export const analyzeVerb: Verb = ({ err, host, inputs, scope, openClient, option
|
|
|
236
295
|
const scoped = scope();
|
|
237
296
|
const targetRoot = scoped.target.path;
|
|
238
297
|
|
|
239
|
-
|
|
240
|
-
try {
|
|
241
|
-
version = host.analyzerVersion();
|
|
242
|
-
} catch (error: unknown) {
|
|
243
|
-
err.write(`deadset-ts: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
244
|
-
return EXIT_FAILURE;
|
|
245
|
-
}
|
|
298
|
+
const version = ownVersion(host);
|
|
246
299
|
|
|
247
|
-
let report: Report;
|
|
248
300
|
let recorded: readonly Recorded[];
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
let
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
host,
|
|
266
|
-
scoped,
|
|
267
|
-
config,
|
|
268
|
-
provenance,
|
|
269
|
-
mode,
|
|
270
|
-
reason,
|
|
271
|
-
);
|
|
272
|
-
analysis = write.first;
|
|
273
|
-
recorded = write.rows;
|
|
274
|
-
}
|
|
275
|
-
const { result, run } = analysis;
|
|
276
|
-
warnings = run.componentWarnings;
|
|
277
|
-
unread = run.componentsUnread.map(
|
|
278
|
-
({ configFile, reason }) =>
|
|
279
|
-
`${relativePath(resolvePath(invoked, targetRoot), configFile) ?? configFile}: its component files are not read: ${reason}`,
|
|
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,
|
|
280
317
|
);
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
`${relativePath(resolvePath(invoked, targetRoot), configFile) ?? configFile}: ${error}: the configuration is analyzed without that library`,
|
|
284
|
-
);
|
|
285
|
-
partial = partialNotes({
|
|
286
|
-
notBuilt: run.notBuilt,
|
|
287
|
-
unanswered: run.unanswered,
|
|
288
|
-
targetRoot: resolvePath(invoked, targetRoot),
|
|
289
|
-
});
|
|
290
|
-
const built = buildReport({
|
|
291
|
-
contractVersion: CONTRACT_VERSION,
|
|
292
|
-
version,
|
|
293
|
-
conformance: CONFORMANCE,
|
|
294
|
-
declaredGaps: DECLARED_GAPS,
|
|
295
|
-
target: {
|
|
296
|
-
kind: kindOf(kind, LANGUAGE),
|
|
297
|
-
root,
|
|
298
|
-
identity: moduleIdentity(host, scoped.target),
|
|
299
|
-
},
|
|
300
|
-
configurations: configurationsOf(run.projects, targetRoot),
|
|
301
|
-
notBuilt: notBuiltOf(run.notBuilt, targetRoot),
|
|
302
|
-
loaded: run.consumers.map((consumer) => ({
|
|
303
|
-
id: consumer.id,
|
|
304
|
-
path: runRelative(invoked, consumer.path),
|
|
305
|
-
})),
|
|
306
|
-
unavailable: [],
|
|
307
|
-
result,
|
|
308
|
-
testFileRules: run.testFileRules,
|
|
309
|
-
typeErrorSkips: run.typeErrorSkips,
|
|
310
|
-
unanswered: unansweredCounts(analysis),
|
|
311
|
-
conventionsApplied: run.conventionsApplied,
|
|
312
|
-
notes: unmappedNotes(run.unmappedEntries, resolvePath(invoked, targetRoot)),
|
|
313
|
-
});
|
|
314
|
-
report = capFindings(sortFindings(built, config.reporters.sort), config.reporters.maxFindings);
|
|
315
|
-
verdict = verdictOf(result, config);
|
|
316
|
-
} catch (error: unknown) {
|
|
317
|
-
if (!(error instanceof ReportPathError)) {
|
|
318
|
-
throw error;
|
|
319
|
-
}
|
|
320
|
-
err.write(`deadset-ts: ${error.message}\n`);
|
|
321
|
-
return EXIT_FAILURE;
|
|
318
|
+
analysis = write.first;
|
|
319
|
+
recorded = write.rows;
|
|
322
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)));
|
|
323
332
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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),
|
|
345
393
|
);
|
|
346
394
|
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
|
|
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
|
+
);
|
|
350
408
|
}
|
|
351
409
|
|
|
352
410
|
for (const note of partial) {
|
|
353
|
-
err
|
|
411
|
+
diagnose(err, note);
|
|
354
412
|
}
|
|
355
|
-
for (const line of [...unread, ...unfound]) {
|
|
356
|
-
err
|
|
413
|
+
for (const line of [...unread, ...unfound, ...unreadInputs]) {
|
|
414
|
+
diagnose(err, line);
|
|
357
415
|
}
|
|
358
416
|
for (const warning of warnings) {
|
|
359
|
-
|
|
360
|
-
|
|
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}`,
|
|
361
420
|
);
|
|
362
421
|
}
|
|
363
422
|
for (const skip of report.type_error_skips) {
|
|
364
|
-
|
|
365
|
-
|
|
423
|
+
diagnose(
|
|
424
|
+
err,
|
|
425
|
+
`${skip.path}:${String(skip.line)}: ${skip.message}: the function or statement holding this type error is not evaluated`,
|
|
366
426
|
);
|
|
367
427
|
}
|
|
368
428
|
if (report.totals.pending > 0) {
|
|
369
|
-
|
|
370
|
-
|
|
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`,
|
|
371
432
|
);
|
|
372
433
|
}
|
|
373
434
|
if (report.totals.stale_suppressions > 0) {
|
|
374
|
-
err
|
|
435
|
+
diagnose(err, counted(report.totals.stale_suppressions, "stale suppression"));
|
|
375
436
|
}
|
|
376
437
|
if (exitCode === EXIT_CODE_OFF) {
|
|
377
438
|
if (verdict !== EXIT_CLEAN) {
|
|
378
|
-
|
|
379
|
-
|
|
439
|
+
diagnose(
|
|
440
|
+
err,
|
|
441
|
+
`the exit code is configured off: the verdict of this run is ${String(verdict)}`,
|
|
380
442
|
);
|
|
381
443
|
}
|
|
382
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
|
};
|
package/src/verbs/explain.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { EXIT_CLEAN, EXIT_USAGE } from "../contract/contract.generated.ts";
|
|
1
2
|
import { runAnalysis, type RunAnalysis } from "../analysis.ts";
|
|
2
3
|
import { listing } from "../components.ts";
|
|
3
4
|
import type { Cascade } from "../config.ts";
|
|
4
|
-
import { ConfigError } from "../config.ts";
|
|
5
5
|
import type { CompletedFinding } from "../finding.ts";
|
|
6
6
|
import { OUTSIDE, swept as judged } from "../graph.ts";
|
|
7
7
|
import type { InventorySymbol } from "../inventory.ts";
|
|
@@ -12,7 +12,10 @@ import { positionKey } from "../position.ts";
|
|
|
12
12
|
import type { Reference } from "../references.ts";
|
|
13
13
|
import { resolve } from "../resolve.ts";
|
|
14
14
|
import type { Root } from "../roots.ts";
|
|
15
|
-
import {
|
|
15
|
+
import { type Verb, type VerbOptions } from "./verb.ts";
|
|
16
|
+
import { UsageError } from "../usage.ts";
|
|
17
|
+
import { bytewise } from "../text/compare.ts";
|
|
18
|
+
import { diagnose } from "../product.ts";
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
21
|
* The three spellings of an explanation request, each naming the symbol to explain. They are
|
|
@@ -44,16 +47,12 @@ function namedSymbol(option: (name: string) => string | undefined): string {
|
|
|
44
47
|
});
|
|
45
48
|
const [only, ...more] = named;
|
|
46
49
|
if (only === undefined) {
|
|
47
|
-
throw new
|
|
48
|
-
"malformed",
|
|
49
|
-
"",
|
|
50
|
+
throw new UsageError(
|
|
50
51
|
"explain explains the symbol --why, --why-live or --why-not names, and none was named",
|
|
51
52
|
);
|
|
52
53
|
}
|
|
53
54
|
if (more.length > 0) {
|
|
54
|
-
throw new
|
|
55
|
-
"malformed",
|
|
56
|
-
"",
|
|
55
|
+
throw new UsageError(
|
|
57
56
|
`explain explains one symbol, and ${named.map((one) => `--${one.name}`).join(", ")} each name one`,
|
|
58
57
|
);
|
|
59
58
|
}
|
|
@@ -101,7 +100,7 @@ function subjectOf(
|
|
|
101
100
|
symbol.ref.toLowerCase().includes(folded) || symbol.name.toLowerCase().includes(folded),
|
|
102
101
|
)
|
|
103
102
|
.map(described)
|
|
104
|
-
.sort(
|
|
103
|
+
.sort(bytewise);
|
|
105
104
|
return { candidates: partial.slice(0, MAX_PARTIAL_MATCHES) };
|
|
106
105
|
}
|
|
107
106
|
|
|
@@ -380,7 +379,7 @@ export const explainVerb: Verb = ({ out, err, host, inputs, scope, openClient, o
|
|
|
380
379
|
targetRoot: resolvePath(host.workingDirectory(), scoped.target.path),
|
|
381
380
|
});
|
|
382
381
|
if (subject === undefined) {
|
|
383
|
-
err
|
|
382
|
+
diagnose(err, `${JSON.stringify(named)} names no one symbol of the target`);
|
|
384
383
|
for (const candidate of candidates) {
|
|
385
384
|
err.write(` ${candidate}\n`);
|
|
386
385
|
}
|
|
@@ -391,7 +390,7 @@ export const explainVerb: Verb = ({ out, err, host, inputs, scope, openClient, o
|
|
|
391
390
|
out.write(explanation(analysis, subject, config.reporters.cascade));
|
|
392
391
|
}
|
|
393
392
|
for (const note of notes) {
|
|
394
|
-
err
|
|
393
|
+
diagnose(err, note);
|
|
395
394
|
}
|
|
396
395
|
return subject === undefined ? EXIT_USAGE : EXIT_CLEAN;
|
|
397
396
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { EXIT_CLEAN } from "../contract/contract.generated.ts";
|
|
1
2
|
import { printConfig } from "../print.ts";
|
|
2
3
|
import { resolve } from "../resolve.ts";
|
|
3
|
-
import {
|
|
4
|
+
import { type Verb } from "./verb.ts";
|
|
4
5
|
|
|
5
6
|
/** Writes the resolved configuration with the source of every setting. */
|
|
6
7
|
export const printConfigVerb: Verb = ({ out, inputs }) => {
|