@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
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The loads of a project's production files whose target the analysis cannot resolve: an
|
|
3
|
+
* `import()` or `require()` call whose path is computed and starts with no relative literal
|
|
4
|
+
* naming a directory below the calling file's. The files such a load runs are roots the
|
|
5
|
+
* analysis does not see, so the configuration's roots are never complete.
|
|
6
|
+
*/
|
|
7
|
+
import { isBinaryExpression, isCallExpression, isIdentifier, isNoSubstitutionTemplateLiteral, isStringLiteral, isTemplateExpression, SyntaxKind, } from "@typescript/native/ast";
|
|
8
|
+
import { dirnamePath, relativePath, resolvePath } from "./paths.js";
|
|
9
|
+
import { renderPosition } from "./position.js";
|
|
10
|
+
import { UNANSWERED } from "./query.js";
|
|
11
|
+
import { sourceFilesOf } from "./source-files.js";
|
|
12
|
+
import { unwrapped } from "./unwrap.js";
|
|
13
|
+
/** The literal text a computed path starts with: a template's head, or a concatenation's first literal. */
|
|
14
|
+
export function leadingLiteral(node) {
|
|
15
|
+
const at = unwrapped(node);
|
|
16
|
+
if (isTemplateExpression(at)) {
|
|
17
|
+
return at.head.text;
|
|
18
|
+
}
|
|
19
|
+
if (isBinaryExpression(at) && at.operatorToken.kind === SyntaxKind.PlusToken) {
|
|
20
|
+
const left = unwrapped(at.left);
|
|
21
|
+
return isStringLiteral(left) || isNoSubstitutionTemplateLiteral(left)
|
|
22
|
+
? left.text
|
|
23
|
+
: leadingLiteral(at.left);
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The directory below `dir` a computed load's leading literal names, which the entry
|
|
29
|
+
* rules root every file of, or none where it names a package, `dir` itself or a
|
|
30
|
+
* directory holding it, each of which would name files no rule roots.
|
|
31
|
+
*/
|
|
32
|
+
export function computedLoadDirectory(dir, prefix) {
|
|
33
|
+
if (!(prefix.startsWith("./") || prefix.startsWith("../")) || !prefix.endsWith("/")) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const target = resolvePath(dir, prefix);
|
|
37
|
+
return target === dir || relativePath(target, dir) !== undefined ? undefined : target;
|
|
38
|
+
}
|
|
39
|
+
/** Whether a computed path's leading literal names a package, whose files are none of the target's. */
|
|
40
|
+
function namesPackage(prefix) {
|
|
41
|
+
return (prefix !== undefined &&
|
|
42
|
+
prefix !== "" &&
|
|
43
|
+
!prefix.startsWith(".") &&
|
|
44
|
+
!prefix.startsWith("/") &&
|
|
45
|
+
!/^[A-Za-z][A-Za-z\d+.-]*:/u.test(prefix));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Every unresolved load the project's production files write, in file then source order.
|
|
49
|
+
* `testFiles` holds the test files by their path below the target root. A `require` call
|
|
50
|
+
* loads a module only where the name resolves to no declaration of the project's own
|
|
51
|
+
* files: one the project declares is its own function.
|
|
52
|
+
*/
|
|
53
|
+
export function unseenLoadsOf(project, configuration, targetRoot, testFiles) {
|
|
54
|
+
const tests = new Set(testFiles);
|
|
55
|
+
const sites = [];
|
|
56
|
+
for (const [path, file] of sourceFilesOf(project, targetRoot).byPath) {
|
|
57
|
+
if (tests.has(path) || file.isDeclarationFile) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const dir = dirnamePath(file.fileName);
|
|
61
|
+
const visit = (node) => {
|
|
62
|
+
if (isCallExpression(node)) {
|
|
63
|
+
const callee = node.expression;
|
|
64
|
+
const required = isIdentifier(callee) && callee.text === "require";
|
|
65
|
+
const [first] = node.arguments;
|
|
66
|
+
const argument = first === undefined ? undefined : unwrapped(first);
|
|
67
|
+
if ((callee.kind === SyntaxKind.ImportKeyword || required) &&
|
|
68
|
+
argument !== undefined &&
|
|
69
|
+
!isStringLiteral(argument) &&
|
|
70
|
+
!isNoSubstitutionTemplateLiteral(argument)) {
|
|
71
|
+
const prefix = leadingLiteral(argument);
|
|
72
|
+
const seen = namesPackage(prefix) ||
|
|
73
|
+
(prefix !== undefined && computedLoadDirectory(dir, prefix) !== undefined);
|
|
74
|
+
if (!seen) {
|
|
75
|
+
sites.push({
|
|
76
|
+
position: renderPosition(file, targetRoot, node.getStart()),
|
|
77
|
+
require: required ? callee : undefined,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
node.forEachChild(visit);
|
|
83
|
+
};
|
|
84
|
+
file.forEachChild(visit);
|
|
85
|
+
}
|
|
86
|
+
const requires = sites.flatMap((site) => (site.require === undefined ? [] : [site.require]));
|
|
87
|
+
const own = project.ownPaths();
|
|
88
|
+
const local = new Set();
|
|
89
|
+
project.symbolsAt(requires.map((node) => project.handle(node))).forEach((symbol, at) => {
|
|
90
|
+
const node = requires[at];
|
|
91
|
+
if (node !== undefined &&
|
|
92
|
+
symbol !== UNANSWERED &&
|
|
93
|
+
symbol?.declarations.some((handle) => own.has(handle.path)) === true) {
|
|
94
|
+
local.add(node);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
return sites
|
|
98
|
+
.filter((site) => site.require === undefined || !local.has(site.require))
|
|
99
|
+
.map((site) => ({ configuration, position: site.position }));
|
|
100
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { isAsExpression, isNonNullExpression, isParenthesizedExpression, isSatisfiesExpression, } from "@typescript/native/ast";
|
|
2
|
+
/** The expression inside every pair of parentheses around `node`. */
|
|
3
|
+
export function parenthesesRemoved(node) {
|
|
4
|
+
let at = node;
|
|
5
|
+
while (isParenthesizedExpression(at)) {
|
|
6
|
+
at = at.expression;
|
|
7
|
+
}
|
|
8
|
+
return at;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The expression inside every wrapper around `node` that changes no value it evaluates to:
|
|
12
|
+
* parentheses, `as` and `satisfies` assertions and the non-null assertion.
|
|
13
|
+
*/
|
|
14
|
+
export function unwrapped(node) {
|
|
15
|
+
let at = node;
|
|
16
|
+
while (isParenthesizedExpression(at) ||
|
|
17
|
+
isAsExpression(at) ||
|
|
18
|
+
isSatisfiesExpression(at) ||
|
|
19
|
+
isNonNullExpression(at)) {
|
|
20
|
+
at = at.expression;
|
|
21
|
+
}
|
|
22
|
+
return at;
|
|
23
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A refusal of the invocation: the fix is to the command line or to a document it
|
|
3
|
+
* names, so the run ends with the usage code before any analysis.
|
|
4
|
+
*/
|
|
5
|
+
export class UsageError extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "UsageError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -1,18 +1,24 @@
|
|
|
1
|
+
import { CONTRACT_VERSION, EXIT_CLEAN } from "../contract/contract.generated.js";
|
|
1
2
|
import { runAnalysis, runBaselineWrite, unansweredCounts, } from "../analysis.js";
|
|
2
|
-
import {
|
|
3
|
+
import { kindOf } from "../config.js";
|
|
3
4
|
import { CONFORMANCE, DECLARED_GAPS } from "../conformance.js";
|
|
4
|
-
import { moduleIdentity } from "../consumers.js";
|
|
5
5
|
import { analyzerProvenance, verdictOf } from "../findings-pass.js";
|
|
6
|
-
import { joinPath, normalizePath, relativePath, resolvePath } from "../paths.js";
|
|
6
|
+
import { dirnamePath, joinPath, normalizePath, relativePath, resolvePath } from "../paths.js";
|
|
7
|
+
import { moduleIdentity } from "../load/discover.js";
|
|
7
8
|
import { partialNotes } from "../partial.js";
|
|
8
|
-
import { buildReport,
|
|
9
|
+
import { buildReport, capFindings, sortFindings, } from "../report.js";
|
|
10
|
+
import { reportRefusals } from "../report-schema.js";
|
|
9
11
|
import { json } from "../reporters/json.js";
|
|
10
12
|
import { RENDERINGS } from "../reporters/reporters.js";
|
|
11
13
|
import { parseTemplate, TemplateError } from "../reporters/template.js";
|
|
12
14
|
import { resolve } from "../resolve.js";
|
|
13
15
|
import { otherSchemeRows, rowReason, writeBaseline } from "../suppress-file.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
+
import {} from "./verb.js";
|
|
17
|
+
import { UsageError } from "../usage.js";
|
|
18
|
+
import { bytewise } from "../text/compare.js";
|
|
19
|
+
import { productFault, failureExit } from "../failure.js";
|
|
20
|
+
import { diagnose, message } from "../product.js";
|
|
21
|
+
import { ownVersion } from "./version.js";
|
|
16
22
|
/** The options `analyze` takes beside the ones every verb takes. */
|
|
17
23
|
export const ANALYZE_OPTIONS = {
|
|
18
24
|
plain: ["report", "baseline-write", "exit-code", "template"],
|
|
@@ -21,16 +27,9 @@ export const ANALYZE_OPTIONS = {
|
|
|
21
27
|
/** The two values `--exit-code` takes; on, the default, puts the run's verdict in the exit code. */
|
|
22
28
|
const EXIT_CODE_ON = "on";
|
|
23
29
|
const EXIT_CODE_OFF = "off";
|
|
24
|
-
/** A path of the run no report can name, which ends the run with the failure code. */
|
|
25
|
-
class ReportPathError extends Error {
|
|
26
|
-
constructor(message) {
|
|
27
|
-
super(message);
|
|
28
|
-
this.name = "ReportPathError";
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
30
|
/** The formats this analyzer renders, for a message that names them. */
|
|
32
31
|
function formatNames() {
|
|
33
|
-
return [...RENDERINGS.keys()].sort().join(", ");
|
|
32
|
+
return [...RENDERINGS.keys()].sort(bytewise).join(", ");
|
|
34
33
|
}
|
|
35
34
|
/** The renderings one invocation writes: the ones it named, else the configuration's. */
|
|
36
35
|
function formatsOf(named, configured) {
|
|
@@ -38,10 +37,10 @@ function formatsOf(named, configured) {
|
|
|
38
37
|
const seen = new Set();
|
|
39
38
|
for (const format of formats) {
|
|
40
39
|
if (!RENDERINGS.has(format)) {
|
|
41
|
-
throw new
|
|
40
|
+
throw new UsageError(`${JSON.stringify(format)} is not a format this analyzer renders: the formats are ${formatNames()}`);
|
|
42
41
|
}
|
|
43
42
|
if (seen.has(format)) {
|
|
44
|
-
throw new
|
|
43
|
+
throw new UsageError(`the format ${JSON.stringify(format)} is named twice`);
|
|
45
44
|
}
|
|
46
45
|
seen.add(format);
|
|
47
46
|
}
|
|
@@ -58,7 +57,7 @@ function runRelative(invoked, path) {
|
|
|
58
57
|
}
|
|
59
58
|
const below = relativePath(invoked, path);
|
|
60
59
|
if (below === undefined) {
|
|
61
|
-
throw
|
|
60
|
+
throw failureExit("unreportable-path", `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`);
|
|
62
61
|
}
|
|
63
62
|
return below;
|
|
64
63
|
}
|
|
@@ -77,12 +76,61 @@ function unmappedNotes(entries, targetRoot) {
|
|
|
77
76
|
};
|
|
78
77
|
});
|
|
79
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* One note per load of a production file the run cannot resolve, each site once: a file
|
|
81
|
+
* it loads is a root the run does not see, so `ts.roots_complete` lifts no cap there.
|
|
82
|
+
*/
|
|
83
|
+
function unseenLoadNotes(loads) {
|
|
84
|
+
return [
|
|
85
|
+
...new Map(loads.map((load) => {
|
|
86
|
+
const { path, line, column } = load.position;
|
|
87
|
+
const site = `${path}:${String(line)}:${String(column)}`;
|
|
88
|
+
const note = {
|
|
89
|
+
kind: "computed-load",
|
|
90
|
+
path: dirnamePath(path),
|
|
91
|
+
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`,
|
|
92
|
+
};
|
|
93
|
+
return [site, note];
|
|
94
|
+
})).values(),
|
|
95
|
+
];
|
|
96
|
+
}
|
|
97
|
+
/** One note per package whose command strings name files a configuration holds, naming every such file. */
|
|
98
|
+
function heldScriptNotes(held, targetRoot) {
|
|
99
|
+
const files = new Map();
|
|
100
|
+
for (const one of held) {
|
|
101
|
+
files.set(one.dir, new Set([...(files.get(one.dir) ?? []), ...one.files]));
|
|
102
|
+
}
|
|
103
|
+
return [...files].map(([dir, paths]) => ({
|
|
104
|
+
kind: "unread-command",
|
|
105
|
+
path: relativePath(targetRoot, dir) ?? ".",
|
|
106
|
+
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"}`,
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
function unresolvedNotes(specifiers) {
|
|
110
|
+
return specifiers.map(({ specifier, position }) => ({
|
|
111
|
+
kind: "unresolved-specifier",
|
|
112
|
+
path: dirnamePath(position.path),
|
|
113
|
+
message: `${position.path}:${String(position.line)}:${String(position.column)} loads ${JSON.stringify(specifier)}, which resolves to no file, so it references nothing`,
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
function unreadLine(input, targetRoot) {
|
|
117
|
+
switch (input.kind) {
|
|
118
|
+
case "manifest": {
|
|
119
|
+
const path = relativePath(targetRoot, input.manifest) ?? input.manifest;
|
|
120
|
+
return input.reason === undefined
|
|
121
|
+
? `${path} does not parse, so the manifest facts of the program formed for its package are unanswered: correct the manifest`
|
|
122
|
+
: `${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}`;
|
|
123
|
+
}
|
|
124
|
+
case "ignore-file":
|
|
125
|
+
return `${relativePath(targetRoot, input.path) ?? input.path} cannot be read, so the JavaScript files it would ignore are analyzed: ${input.reason}`;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
80
128
|
/** Each project the run dropped as a report names it, by its configuration file below the target. */
|
|
81
129
|
function notBuiltOf(dropped, targetRoot) {
|
|
82
130
|
return dropped.map((one) => {
|
|
83
131
|
const project = relativePath(targetRoot, one.configFile);
|
|
84
132
|
if (project === undefined) {
|
|
85
|
-
throw
|
|
133
|
+
throw failureExit("unreportable-path", `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`);
|
|
86
134
|
}
|
|
87
135
|
return { id: one.id, project, error: one.error };
|
|
88
136
|
});
|
|
@@ -92,11 +140,24 @@ function configurationsOf(projects, targetRoot) {
|
|
|
92
140
|
return projects.map((one) => {
|
|
93
141
|
const project = relativePath(targetRoot, one.configFile);
|
|
94
142
|
if (project === undefined) {
|
|
95
|
-
throw
|
|
143
|
+
throw failureExit("unreportable-path", `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`);
|
|
96
144
|
}
|
|
97
145
|
return { id: one.id, project };
|
|
98
146
|
});
|
|
99
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Writes one document the run was asked for. A document that cannot be produced or written
|
|
150
|
+
* ends the run with the run failure that names it and `fix`, the documents written before it
|
|
151
|
+
* kept.
|
|
152
|
+
*/
|
|
153
|
+
function writeDocument(host, path, fix, produce) {
|
|
154
|
+
try {
|
|
155
|
+
host.writeDocument(path, produce());
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
throw failureExit("unwritten-output", `${path}: ${message(error)}: ${fix}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
100
161
|
/** A count with its noun, so a message reads for one record as well as for several. */
|
|
101
162
|
function counted(n, noun) {
|
|
102
163
|
return `${String(n)} ${noun}${n === 1 ? "" : "s"}`;
|
|
@@ -109,7 +170,7 @@ function counted(n, noun) {
|
|
|
109
170
|
function templateOf(host, invoked, named, formats) {
|
|
110
171
|
if (named === undefined || named === "") {
|
|
111
172
|
if (formats.includes("template")) {
|
|
112
|
-
throw new
|
|
173
|
+
throw new UsageError("the template format renders the template --template names, and none was named");
|
|
113
174
|
}
|
|
114
175
|
return undefined;
|
|
115
176
|
}
|
|
@@ -118,7 +179,7 @@ function templateOf(host, invoked, named, formats) {
|
|
|
118
179
|
source = host.readFile(resolvePath(invoked, named));
|
|
119
180
|
}
|
|
120
181
|
catch (error) {
|
|
121
|
-
throw new
|
|
182
|
+
throw new UsageError(`--template=${named}: ${message(error)}`);
|
|
122
183
|
}
|
|
123
184
|
try {
|
|
124
185
|
return parseTemplate(source);
|
|
@@ -127,7 +188,7 @@ function templateOf(host, invoked, named, formats) {
|
|
|
127
188
|
if (!(error instanceof TemplateError)) {
|
|
128
189
|
throw error;
|
|
129
190
|
}
|
|
130
|
-
throw new
|
|
191
|
+
throw new UsageError(`--template=${named}: ${error.message}`);
|
|
131
192
|
}
|
|
132
193
|
}
|
|
133
194
|
/**
|
|
@@ -141,17 +202,13 @@ function templateOf(host, invoked, named, formats) {
|
|
|
141
202
|
export const analyzeVerb = ({ err, host, inputs, scope, openClient, option, repeated }) => {
|
|
142
203
|
const reportPath = option("report");
|
|
143
204
|
if (reportPath === undefined || reportPath === "") {
|
|
144
|
-
throw new
|
|
205
|
+
throw new UsageError("analyze writes its report to the path --report names, and no path was named");
|
|
145
206
|
}
|
|
146
207
|
const exitCode = option("exit-code") ?? EXIT_CODE_ON;
|
|
147
208
|
if (exitCode !== EXIT_CODE_ON && exitCode !== EXIT_CODE_OFF) {
|
|
148
|
-
throw new
|
|
209
|
+
throw new UsageError(`--exit-code=${JSON.stringify(exitCode)}: the values are ${EXIT_CODE_ON} and ${EXIT_CODE_OFF}`);
|
|
149
210
|
}
|
|
150
211
|
const { config, provenance } = resolve(inputs());
|
|
151
|
-
const kind = config.targetKind;
|
|
152
|
-
if (kind === "") {
|
|
153
|
-
throw new ConfigError("missing-target-kind", "target.kind", "target.kind is not set");
|
|
154
|
-
}
|
|
155
212
|
const invoked = host.workingDirectory();
|
|
156
213
|
const baseline = option("baseline-write");
|
|
157
214
|
const documents = {
|
|
@@ -162,123 +219,106 @@ export const analyzeVerb = ({ err, host, inputs, scope, openClient, option, repe
|
|
|
162
219
|
const template = templateOf(host, invoked, option("template"), documents.formats);
|
|
163
220
|
const scoped = scope();
|
|
164
221
|
const targetRoot = scoped.target.path;
|
|
165
|
-
|
|
166
|
-
try {
|
|
167
|
-
version = host.analyzerVersion();
|
|
168
|
-
}
|
|
169
|
-
catch (error) {
|
|
170
|
-
err.write(`deadset-ts: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
171
|
-
return EXIT_FAILURE;
|
|
172
|
-
}
|
|
173
|
-
let report;
|
|
222
|
+
const version = ownVersion(host);
|
|
174
223
|
let recorded;
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
let
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const root = runRelative(invoked, targetRoot);
|
|
182
|
-
const mode = { production: true };
|
|
183
|
-
let analysis;
|
|
184
|
-
if (documents.baseline === undefined) {
|
|
185
|
-
analysis = runAnalysis(openClient(config), host, scoped, config, provenance, mode);
|
|
186
|
-
recorded = [];
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
const reason = rowReason(analyzerProvenance(version));
|
|
190
|
-
const write = runBaselineWrite(openClient(config), host, scoped, config, provenance, mode, reason);
|
|
191
|
-
analysis = write.first;
|
|
192
|
-
recorded = write.rows;
|
|
193
|
-
}
|
|
194
|
-
const { result, run } = analysis;
|
|
195
|
-
warnings = run.componentWarnings;
|
|
196
|
-
unread = run.componentsUnread.map(({ configFile, reason }) => `${relativePath(resolvePath(invoked, targetRoot), configFile) ?? configFile}: its component files are not read: ${reason}`);
|
|
197
|
-
unfound = run.librariesUnfound.map(({ configFile, error }) => `${relativePath(resolvePath(invoked, targetRoot), configFile) ?? configFile}: ${error}: the configuration is analyzed without that library`);
|
|
198
|
-
partial = partialNotes({
|
|
199
|
-
notBuilt: run.notBuilt,
|
|
200
|
-
unanswered: run.unanswered,
|
|
201
|
-
targetRoot: resolvePath(invoked, targetRoot),
|
|
202
|
-
});
|
|
203
|
-
const built = buildReport({
|
|
204
|
-
contractVersion: CONTRACT_VERSION,
|
|
205
|
-
version,
|
|
206
|
-
conformance: CONFORMANCE,
|
|
207
|
-
declaredGaps: DECLARED_GAPS,
|
|
208
|
-
target: {
|
|
209
|
-
kind: kindOf(kind, LANGUAGE),
|
|
210
|
-
root,
|
|
211
|
-
identity: moduleIdentity(host, scoped.target),
|
|
212
|
-
},
|
|
213
|
-
configurations: configurationsOf(run.projects, targetRoot),
|
|
214
|
-
notBuilt: notBuiltOf(run.notBuilt, targetRoot),
|
|
215
|
-
loaded: run.consumers.map((consumer) => ({
|
|
216
|
-
id: consumer.id,
|
|
217
|
-
path: runRelative(invoked, consumer.path),
|
|
218
|
-
})),
|
|
219
|
-
unavailable: [],
|
|
220
|
-
result,
|
|
221
|
-
testFileRules: run.testFileRules,
|
|
222
|
-
typeErrorSkips: run.typeErrorSkips,
|
|
223
|
-
unanswered: unansweredCounts(analysis),
|
|
224
|
-
conventionsApplied: run.conventionsApplied,
|
|
225
|
-
notes: unmappedNotes(run.unmappedEntries, resolvePath(invoked, targetRoot)),
|
|
226
|
-
});
|
|
227
|
-
report = capFindings(sortFindings(built, config.reporters.sort), config.reporters.maxFindings);
|
|
228
|
-
verdict = verdictOf(result, config);
|
|
224
|
+
const root = runRelative(invoked, targetRoot);
|
|
225
|
+
const mode = { production: true };
|
|
226
|
+
let analysis;
|
|
227
|
+
if (documents.baseline === undefined) {
|
|
228
|
+
analysis = runAnalysis(openClient(config), host, scoped, config, provenance, mode);
|
|
229
|
+
recorded = [];
|
|
229
230
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
return EXIT_FAILURE;
|
|
231
|
+
else {
|
|
232
|
+
const reason = rowReason(analyzerProvenance(version));
|
|
233
|
+
const write = runBaselineWrite(openClient(config), host, scoped, config, provenance, mode, reason);
|
|
234
|
+
analysis = write.first;
|
|
235
|
+
recorded = write.rows;
|
|
236
236
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
237
|
+
const { result, run } = analysis;
|
|
238
|
+
const warnings = run.componentWarnings;
|
|
239
|
+
const unread = run.componentsUnread.map(({ configFile, reason }) => `${relativePath(resolvePath(invoked, targetRoot), configFile) ?? configFile}: its component files are not read: ${reason}`);
|
|
240
|
+
const unfound = run.librariesUnfound.map(({ configFile, error }) => `${relativePath(resolvePath(invoked, targetRoot), configFile) ?? configFile}: ${error}: the configuration is analyzed without that library`);
|
|
241
|
+
const unreadInputs = run.unread.map((one) => unreadLine(one, resolvePath(invoked, targetRoot)));
|
|
242
|
+
const partial = partialNotes({
|
|
243
|
+
notBuilt: run.notBuilt,
|
|
244
|
+
unanswered: run.unanswered,
|
|
245
|
+
targetRoot: resolvePath(invoked, targetRoot),
|
|
246
|
+
});
|
|
247
|
+
const built = buildReport({
|
|
248
|
+
contractVersion: CONTRACT_VERSION,
|
|
249
|
+
version,
|
|
250
|
+
conformance: CONFORMANCE,
|
|
251
|
+
declaredGaps: DECLARED_GAPS,
|
|
252
|
+
target: {
|
|
253
|
+
kind: kindOf(config.target.kind),
|
|
254
|
+
root,
|
|
255
|
+
identity: moduleIdentity(host, scoped.target),
|
|
256
|
+
},
|
|
257
|
+
configurations: configurationsOf(run.projects, targetRoot),
|
|
258
|
+
notBuilt: notBuiltOf(run.notBuilt, targetRoot),
|
|
259
|
+
loaded: run.consumers.map((consumer) => ({
|
|
260
|
+
id: consumer.id,
|
|
261
|
+
path: runRelative(invoked, consumer.path),
|
|
262
|
+
})),
|
|
263
|
+
unavailable: [],
|
|
264
|
+
result,
|
|
265
|
+
testFileRules: run.testFileRules,
|
|
266
|
+
typeErrorSkips: run.typeErrorSkips,
|
|
267
|
+
unanswered: unansweredCounts(analysis),
|
|
268
|
+
conventionsApplied: run.conventionsApplied,
|
|
269
|
+
listsComponents: config.reporters.cascade === "full",
|
|
270
|
+
notes: [
|
|
271
|
+
...unmappedNotes(run.unmappedEntries, resolvePath(invoked, targetRoot)),
|
|
272
|
+
...unseenLoadNotes(run.unseenLoads),
|
|
273
|
+
...unresolvedNotes(run.unresolvedSpecifiers),
|
|
274
|
+
...heldScriptNotes(run.heldScripts, resolvePath(invoked, targetRoot)),
|
|
275
|
+
],
|
|
276
|
+
});
|
|
277
|
+
const report = capFindings(sortFindings(built, config.reporters.sort), config.reporters.max_findings);
|
|
278
|
+
const verdict = verdictOf(result, config);
|
|
279
|
+
const [refused] = reportRefusals(report);
|
|
280
|
+
if (refused !== undefined) {
|
|
281
|
+
throw productFault(`built a report that breaks the report schema at ${refused.pointer || "the document"}: ${refused.keyword}: ${refused.reason}`);
|
|
282
|
+
}
|
|
283
|
+
writeDocument(host, documents.report, "name a path the run can write", () => json(report));
|
|
284
|
+
const options = {
|
|
285
|
+
readSource: (path) => host.readFile(joinPath(targetRoot, path)),
|
|
286
|
+
template,
|
|
287
|
+
};
|
|
288
|
+
for (const format of documents.formats) {
|
|
289
|
+
const rendering = RENDERINGS.get(format);
|
|
290
|
+
if (rendering !== undefined) {
|
|
291
|
+
writeDocument(host, documents.report + rendering.suffix, "name a path the run can write, or correct the template that renders it", () => rendering.render(report, options));
|
|
255
292
|
}
|
|
256
293
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
294
|
+
const baselinePath = documents.baseline;
|
|
295
|
+
if (baselinePath !== undefined) {
|
|
296
|
+
writeDocument(host, baselinePath, "name a path the run can write, or repair the rows of the baseline the run would keep", () => {
|
|
297
|
+
const kept = host.kindOf(baselinePath) === "file" ? otherSchemeRows(host.readFile(baselinePath)) : [];
|
|
298
|
+
return writeBaseline(recorded, analyzerProvenance(version), kept);
|
|
299
|
+
});
|
|
260
300
|
}
|
|
261
301
|
for (const note of partial) {
|
|
262
|
-
err
|
|
302
|
+
diagnose(err, note);
|
|
263
303
|
}
|
|
264
|
-
for (const line of [...unread, ...unfound]) {
|
|
265
|
-
err
|
|
304
|
+
for (const line of [...unread, ...unfound, ...unreadInputs]) {
|
|
305
|
+
diagnose(err, line);
|
|
266
306
|
}
|
|
267
307
|
for (const warning of warnings) {
|
|
268
|
-
err
|
|
308
|
+
diagnose(err, `${warning.path}:${String(warning.line)}: a <script> start tag that begins a line is not read as a block: ${warning.reason}`);
|
|
269
309
|
}
|
|
270
310
|
for (const skip of report.type_error_skips) {
|
|
271
|
-
err
|
|
311
|
+
diagnose(err, `${skip.path}:${String(skip.line)}: ${skip.message}: the function or statement holding this type error is not evaluated`);
|
|
272
312
|
}
|
|
273
313
|
if (report.totals.pending > 0) {
|
|
274
|
-
err
|
|
314
|
+
diagnose(err, `${counted(report.totals.pending, "pending finding")}: the report names a declared cross-language edge, and no merge has resolved it`);
|
|
275
315
|
}
|
|
276
316
|
if (report.totals.stale_suppressions > 0) {
|
|
277
|
-
err
|
|
317
|
+
diagnose(err, counted(report.totals.stale_suppressions, "stale suppression"));
|
|
278
318
|
}
|
|
279
319
|
if (exitCode === EXIT_CODE_OFF) {
|
|
280
320
|
if (verdict !== EXIT_CLEAN) {
|
|
281
|
-
err
|
|
321
|
+
diagnose(err, `the exit code is configured off: the verdict of this run is ${String(verdict)}`);
|
|
282
322
|
}
|
|
283
323
|
return EXIT_CLEAN;
|
|
284
324
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { CONTRACT_VERSION, EXIT_CLEAN, EXIT_USAGE, SCHEMA_VERSIONS, } from "../contract/contract.generated.js";
|
|
1
2
|
import { CONFORMANCE } from "../conformance.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import {} from "./verb.js";
|
|
4
|
+
import { diagnose, LANGUAGE, PRODUCT } from "../product.js";
|
|
5
|
+
import { ownVersion } from "./version.js";
|
|
5
6
|
/**
|
|
6
7
|
* The document `describe` writes: the analyzer, the Contract version it implements, the
|
|
7
8
|
* report schema versions it reads, the languages it claims, and its recorded result over
|
|
@@ -9,10 +10,10 @@ import { EXIT_CLEAN, EXIT_FAILURE, EXIT_USAGE } from "./verb.js";
|
|
|
9
10
|
*/
|
|
10
11
|
export function describeDocument(version, recorded) {
|
|
11
12
|
const document = {
|
|
12
|
-
name:
|
|
13
|
+
name: PRODUCT,
|
|
13
14
|
version,
|
|
14
15
|
contract_version: CONTRACT_VERSION,
|
|
15
|
-
schema_versions_accepted:
|
|
16
|
+
schema_versions_accepted: SCHEMA_VERSIONS,
|
|
16
17
|
languages: [LANGUAGE],
|
|
17
18
|
...(recorded === undefined
|
|
18
19
|
? {}
|
|
@@ -26,20 +27,12 @@ export function describeDocument(version, recorded) {
|
|
|
26
27
|
};
|
|
27
28
|
return `${JSON.stringify(document, null, 2)}\n`;
|
|
28
29
|
}
|
|
29
|
-
/** Writes this analyzer's description to the output stream
|
|
30
|
+
/** Writes this analyzer's description to the output stream. */
|
|
30
31
|
export const describeVerb = ({ out, err, host, args }) => {
|
|
31
32
|
if (args.length > 0) {
|
|
32
|
-
err
|
|
33
|
+
diagnose(err, `describe takes no argument, got ${JSON.stringify(args[0])}`);
|
|
33
34
|
return EXIT_USAGE;
|
|
34
35
|
}
|
|
35
|
-
|
|
36
|
-
try {
|
|
37
|
-
version = host.analyzerVersion();
|
|
38
|
-
}
|
|
39
|
-
catch (error) {
|
|
40
|
-
err.write(`deadset-ts: describe: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
41
|
-
return EXIT_FAILURE;
|
|
42
|
-
}
|
|
43
|
-
out.write(describeDocument(version, CONFORMANCE));
|
|
36
|
+
out.write(describeDocument(ownVersion(host), CONFORMANCE));
|
|
44
37
|
return EXIT_CLEAN;
|
|
45
38
|
};
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
+
import { EXIT_CLEAN, EXIT_USAGE } from "../contract/contract.generated.js";
|
|
1
2
|
import { runAnalysis } from "../analysis.js";
|
|
2
3
|
import { listing } from "../components.js";
|
|
3
|
-
import { ConfigError } from "../config.js";
|
|
4
4
|
import { OUTSIDE, swept as judged } from "../graph.js";
|
|
5
5
|
import { referenceKey } from "../matrix.js";
|
|
6
6
|
import { partialNotes } from "../partial.js";
|
|
7
7
|
import { resolvePath } from "../paths.js";
|
|
8
8
|
import { positionKey } from "../position.js";
|
|
9
9
|
import { resolve } from "../resolve.js";
|
|
10
|
-
import {
|
|
10
|
+
import {} from "./verb.js";
|
|
11
|
+
import { UsageError } from "../usage.js";
|
|
12
|
+
import { bytewise } from "../text/compare.js";
|
|
13
|
+
import { diagnose } from "../product.js";
|
|
11
14
|
/**
|
|
12
15
|
* The three spellings of an explanation request, each naming the symbol to explain. They are
|
|
13
16
|
* one request, because exactly one state holds for a symbol and the answer is that state.
|
|
@@ -33,10 +36,10 @@ function namedSymbol(option) {
|
|
|
33
36
|
});
|
|
34
37
|
const [only, ...more] = named;
|
|
35
38
|
if (only === undefined) {
|
|
36
|
-
throw new
|
|
39
|
+
throw new UsageError("explain explains the symbol --why, --why-live or --why-not names, and none was named");
|
|
37
40
|
}
|
|
38
41
|
if (more.length > 0) {
|
|
39
|
-
throw new
|
|
42
|
+
throw new UsageError(`explain explains one symbol, and ${named.map((one) => `--${one.name}`).join(", ")} each name one`);
|
|
40
43
|
}
|
|
41
44
|
return only.value;
|
|
42
45
|
}
|
|
@@ -73,7 +76,7 @@ function subjectOf(symbols, named) {
|
|
|
73
76
|
const partial = symbols
|
|
74
77
|
.filter((symbol) => symbol.ref.toLowerCase().includes(folded) || symbol.name.toLowerCase().includes(folded))
|
|
75
78
|
.map(described)
|
|
76
|
-
.sort(
|
|
79
|
+
.sort(bytewise);
|
|
77
80
|
return { candidates: partial.slice(0, MAX_PARTIAL_MATCHES) };
|
|
78
81
|
}
|
|
79
82
|
/** Every reference of the run once, a reference several configurations make counted once. */
|
|
@@ -317,7 +320,7 @@ export const explainVerb = ({ out, err, host, inputs, scope, openClient, option
|
|
|
317
320
|
targetRoot: resolvePath(host.workingDirectory(), scoped.target.path),
|
|
318
321
|
});
|
|
319
322
|
if (subject === undefined) {
|
|
320
|
-
err
|
|
323
|
+
diagnose(err, `${JSON.stringify(named)} names no one symbol of the target`);
|
|
321
324
|
for (const candidate of candidates) {
|
|
322
325
|
err.write(` ${candidate}\n`);
|
|
323
326
|
}
|
|
@@ -329,7 +332,7 @@ export const explainVerb = ({ out, err, host, inputs, scope, openClient, option
|
|
|
329
332
|
out.write(explanation(analysis, subject, config.reporters.cascade));
|
|
330
333
|
}
|
|
331
334
|
for (const note of notes) {
|
|
332
|
-
err
|
|
335
|
+
diagnose(err, note);
|
|
333
336
|
}
|
|
334
337
|
return subject === undefined ? EXIT_USAGE : EXIT_CLEAN;
|
|
335
338
|
};
|