@cplieger/deadset-ts 8.0.0-dev.7 → 8.0.0-dev.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/node-host.js +21 -3
- package/dist/src/analysis.js +13 -14
- package/dist/src/config.js +6 -83
- package/dist/src/configured-declarations.js +15 -20
- package/dist/src/consumers.js +2 -2
- package/dist/src/contract/config.generated.js +544 -0
- package/dist/src/contract/report.generated.js +3 -2
- package/dist/src/discover.js +1 -1
- package/dist/src/edges.js +36 -112
- package/dist/src/exempt-classes.js +0 -2
- package/dist/src/exempt.js +1 -1
- package/dist/src/findings/completion.js +5 -4
- package/dist/src/findings/emitters.js +1 -1
- package/dist/src/findings/intra-function.js +1 -1
- package/dist/src/findings/non-code-artifacts.js +2 -2
- package/dist/src/findings/unused-declarations.js +1 -1
- package/dist/src/findings/visibility-narrowing.js +2 -2
- package/dist/src/findings-pass.js +1 -1
- package/dist/src/framework-lifecycle.js +7 -7
- package/dist/src/fs/document.js +59 -0
- package/dist/src/injection-container.js +1 -1
- package/dist/src/json-document.js +194 -67
- package/dist/src/kinds.js +0 -7
- package/dist/src/print.js +10 -79
- package/dist/src/report-schema.js +3 -224
- package/dist/src/resolve.js +245 -715
- package/dist/src/run.js +48 -55
- package/dist/src/schema.js +320 -344
- package/dist/src/scope.js +33 -101
- package/dist/src/serialization-contract.js +1 -1
- package/dist/src/suppress-file.js +15 -17
- package/dist/src/suppress.js +3 -3
- package/dist/src/template-field.js +4 -4
- package/dist/src/usage.js +10 -0
- package/dist/src/verbs/analyze.js +12 -15
- package/dist/src/verbs/explain.js +3 -3
- package/dist/src/verbs/print-projects.js +2 -2
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/analysis.ts +30 -26
- package/src/config.ts +31 -298
- package/src/configured-declarations.ts +18 -23
- package/src/consumers.ts +4 -4
- package/src/contract/config.generated.ts +798 -0
- package/src/contract/report.generated.ts +3 -2
- package/src/discover.ts +1 -1
- package/src/edges.ts +39 -124
- package/src/exempt-classes.ts +0 -5
- package/src/exempt.ts +4 -4
- package/src/findings/completion.ts +5 -6
- package/src/findings/emitter.ts +2 -2
- package/src/findings/emitters.ts +1 -1
- package/src/findings/intra-function.ts +1 -1
- package/src/findings/non-code-artifacts.ts +4 -4
- package/src/findings/unused-declarations.ts +1 -1
- package/src/findings/visibility-narrowing.ts +2 -2
- package/src/findings-pass.ts +3 -3
- package/src/framework-lifecycle.ts +7 -7
- package/src/fs/document.ts +74 -0
- package/src/host.ts +12 -5
- package/src/injection-container.ts +1 -1
- package/src/json-document.ts +236 -76
- package/src/kinds.ts +0 -8
- package/src/print.ts +11 -92
- package/src/report-schema.ts +3 -299
- package/src/resolve.ts +281 -1105
- package/src/run.ts +50 -63
- package/src/schema.ts +391 -389
- package/src/scope.ts +39 -119
- package/src/serialization-contract.ts +1 -1
- package/src/suppress-file.ts +15 -20
- package/src/suppress.ts +3 -3
- package/src/template-field.ts +10 -7
- package/src/usage.ts +10 -0
- package/src/verbs/analyze.ts +12 -23
- package/src/verbs/explain.ts +3 -7
- package/src/verbs/print-projects.ts +2 -2
- package/src/verbs/verb.ts +2 -2
package/dist/src/scope.js
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SCOPE_SCHEMA } from "./contract/config.generated.js";
|
|
2
|
+
import { dotted, locatedFault, placeIn, readStrict, StrictJSONError } from "./json-document.js";
|
|
2
3
|
import { dirnamePath, isAbsolutePath, joinPath, resolvePath } from "./paths.js";
|
|
3
|
-
import { checkDocument } from "./schema.js";
|
|
4
|
-
import { isRecord } from "./json-document.js";
|
|
5
4
|
import { message } from "./product.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const ROLE_CONSUMER = "consumer";
|
|
5
|
+
import { firstRefusal } from "./schema.js";
|
|
6
|
+
import { UsageError } from "./usage.js";
|
|
9
7
|
/**
|
|
10
8
|
* A scope document above this length is refused rather than decoded. It names one
|
|
11
9
|
* target and its consumers, so a larger file is not one.
|
|
12
10
|
*/
|
|
13
11
|
const MAX_DOCUMENT_LENGTH = 1 << 20;
|
|
14
12
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* The bytes read of a scope document. A UTF-16 unit decodes from at most three bytes,
|
|
14
|
+
* so a document past the bound is past it within these.
|
|
15
|
+
*/
|
|
16
|
+
const MAX_DOCUMENT_BYTES = 3 * (MAX_DOCUMENT_LENGTH + 1);
|
|
17
|
+
/**
|
|
18
|
+
* A scope that cannot be read as one. It ends the run with the failure code: the run
|
|
19
|
+
* could not learn what to analyze, so it produced no answer.
|
|
17
20
|
*/
|
|
18
21
|
export class ScopeError extends Error {
|
|
19
22
|
constructor(message) {
|
|
@@ -21,119 +24,48 @@ export class ScopeError extends Error {
|
|
|
21
24
|
this.name = "ScopeError";
|
|
22
25
|
}
|
|
23
26
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
members: { id: { kind: "leaf" }, role: { kind: "leaf" }, path: { kind: "leaf" } },
|
|
27
|
-
};
|
|
28
|
-
/** The closed key list of the scope document, compared as bytes. */
|
|
29
|
-
const SCOPE_ROOT = {
|
|
30
|
-
kind: "section",
|
|
31
|
-
members: {
|
|
32
|
-
target: MODULE,
|
|
33
|
-
workspace: { kind: "leaf" },
|
|
34
|
-
consumers: { kind: "list", members: MODULE.members ?? {} },
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
/**
|
|
38
|
-
* One string member, or undefined where the document omits it. A member the
|
|
39
|
-
* document writes is a string holding at least one character, so `null` and `""`
|
|
40
|
-
* are refused rather than read as absent.
|
|
41
|
-
*/
|
|
42
|
-
function stringMember(parent, name, at) {
|
|
43
|
-
if (!Object.hasOwn(parent, name)) {
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
46
|
-
const value = parent[name];
|
|
47
|
-
if (typeof value !== "string") {
|
|
48
|
-
throw new ScopeError(`scope: ${at === "" ? name : `${at}.${name}`} is not a string`);
|
|
49
|
-
}
|
|
50
|
-
if (value === "") {
|
|
51
|
-
throw new ScopeError(`scope: ${at === "" ? name : `${at}.${name}`} names nothing`);
|
|
52
|
-
}
|
|
53
|
-
return value;
|
|
54
|
-
}
|
|
55
|
-
function readModule(value, at, role, documentDir) {
|
|
56
|
-
if (!isRecord(value)) {
|
|
57
|
-
throw new ScopeError(`scope: ${at} is not an object`);
|
|
58
|
-
}
|
|
59
|
-
const declared = stringMember(value, "role", at);
|
|
60
|
-
if (declared !== undefined && declared !== role) {
|
|
61
|
-
throw new ScopeError(`scope: ${at}.role is ${JSON.stringify(declared)}, want ${JSON.stringify(role)}`);
|
|
62
|
-
}
|
|
63
|
-
const path = stringMember(value, "path", at);
|
|
64
|
-
if (path === undefined) {
|
|
65
|
-
throw new ScopeError(`scope: ${at}.path names nothing`);
|
|
66
|
-
}
|
|
67
|
-
return {
|
|
68
|
-
id: stringMember(value, "id", at) ?? "",
|
|
69
|
-
path: isAbsolutePath(path) ? path : joinPath(documentDir, path),
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Walks the document's text against the closed key list, refusing a key the list
|
|
74
|
-
* does not declare, compared as bytes, and a key one object writes twice, which a
|
|
75
|
-
* parse cannot report because it keeps the last value.
|
|
76
|
-
*/
|
|
77
|
-
function checkKeys(text, file) {
|
|
78
|
-
try {
|
|
79
|
-
checkDocument(text, SCOPE_ROOT, `scope: ${file}`);
|
|
80
|
-
}
|
|
81
|
-
catch (error) {
|
|
82
|
-
if (error instanceof ConfigError) {
|
|
83
|
-
// The configuration's refusal names the nearest configuration key, which a
|
|
84
|
-
// scope document does not have.
|
|
85
|
-
throw new ScopeError(error.kind === "unimplemented-key"
|
|
86
|
-
? `scope: ${file}: ${error.key} is not a declared key`
|
|
87
|
-
: error.message);
|
|
88
|
-
}
|
|
89
|
-
throw error;
|
|
90
|
-
}
|
|
27
|
+
function refusal(file, place, reason) {
|
|
28
|
+
return new ScopeError(`scope: ${file}: ${place === "" ? "" : `${place}: `}${reason}`);
|
|
91
29
|
}
|
|
92
30
|
/**
|
|
93
|
-
* Decodes the scope document at `file
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* resolves
|
|
97
|
-
*
|
|
98
|
-
* The `workspace` key the format declares is checked and carries nothing here: it
|
|
99
|
-
* names the file a Go consumer resolves the target through, and a TypeScript
|
|
100
|
-
* project resolves one through its own compiler configuration.
|
|
31
|
+
* Decodes the scope document at `file`, held to the Contract's scope schema, a relative
|
|
32
|
+
* path resolved against the document's own directory. A document that cannot be read is
|
|
33
|
+
* a usage error, one that is refused a scope error. `workspace` is read by Go consumers
|
|
34
|
+
* alone: a TypeScript project resolves the target through its own compiler configuration.
|
|
101
35
|
*/
|
|
102
36
|
export function readScope(host, file) {
|
|
103
37
|
const absolute = resolvePath(host.workingDirectory(), file);
|
|
104
38
|
let text;
|
|
105
39
|
try {
|
|
106
|
-
text = host.readFile(absolute);
|
|
40
|
+
text = host.readFile(absolute, MAX_DOCUMENT_BYTES);
|
|
107
41
|
}
|
|
108
42
|
catch (error) {
|
|
109
|
-
throw new
|
|
43
|
+
throw new UsageError(`scope: ${file}: ${message(error)}`);
|
|
110
44
|
}
|
|
111
45
|
if (text.length > MAX_DOCUMENT_LENGTH) {
|
|
112
46
|
throw new ScopeError(`scope: ${file} is longer than ${String(MAX_DOCUMENT_LENGTH)} characters`);
|
|
113
47
|
}
|
|
114
48
|
let value;
|
|
115
49
|
try {
|
|
116
|
-
value =
|
|
50
|
+
value = readStrict(text).value;
|
|
117
51
|
}
|
|
118
52
|
catch (error) {
|
|
119
|
-
|
|
53
|
+
if (!(error instanceof StrictJSONError)) {
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
throw refusal(file, dotted(error.place), locatedFault(text, error));
|
|
120
57
|
}
|
|
121
|
-
|
|
122
|
-
|
|
58
|
+
const refused = firstRefusal(SCOPE_SCHEMA, value);
|
|
59
|
+
if (refused !== undefined) {
|
|
60
|
+
throw refusal(file, dotted(placeIn(value, refused.pointer, refused.member)), refused.reason);
|
|
123
61
|
}
|
|
124
|
-
|
|
125
|
-
stringMember(value, "workspace", "");
|
|
62
|
+
const declared = value;
|
|
126
63
|
const documentDir = dirnamePath(absolute);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (!Array.isArray(listed)) {
|
|
133
|
-
throw new ScopeError(`scope: ${file}: consumers is not an array`);
|
|
134
|
-
}
|
|
135
|
-
const consumers = listed.map((entry, index) => readModule(entry, `consumers[${String(index)}]`, ROLE_CONSUMER, documentDir));
|
|
136
|
-
return { target, consumers };
|
|
64
|
+
const moduleOf = (module) => ({
|
|
65
|
+
id: module.id ?? "",
|
|
66
|
+
path: isAbsolutePath(module.path) ? module.path : joinPath(documentDir, module.path),
|
|
67
|
+
});
|
|
68
|
+
return { target: moduleOf(declared.target), consumers: (declared.consumers ?? []).map(moduleOf) };
|
|
137
69
|
}
|
|
138
70
|
/** The single-package scope one directory is, when no scope document is given. */
|
|
139
71
|
export function scopeForDir(host, dir) {
|
|
@@ -19,7 +19,7 @@ import { must, Unanswerable } from "./query.js";
|
|
|
19
19
|
import { erasedStores } from "./erased-stores.js";
|
|
20
20
|
import { checkedTypes, CONVERSION_MEMBERS, valueReach } from "./value-reach.js";
|
|
21
21
|
/** The serializer every run reads, whatever the configuration names. */
|
|
22
|
-
const JSON_STRINGIFY = {
|
|
22
|
+
const JSON_STRINGIFY = { global: "JSON.stringify" };
|
|
23
23
|
/** The methods a serializer and a formatter resolve by name on a value that left the analysis. */
|
|
24
24
|
const CONVERSIONS = new Set(["toJSON", "toString"]);
|
|
25
25
|
/** The method a serializer calls on a value that carries one, wherever the value goes. */
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* ignore file by gaining a maintainer's reason.
|
|
6
6
|
*/
|
|
7
7
|
import { CODE } from "./contract/contract.generated.js";
|
|
8
|
+
import { readTargetDocument } from "./fs/document.js";
|
|
8
9
|
import { jsonChunks } from "./json-chunks.js";
|
|
9
|
-
import {
|
|
10
|
-
import { isRecord, positionAt, walkDocument } from "./json-document.js";
|
|
10
|
+
import { isRecord, positionAt, readStrict, StrictJSONError } from "./json-document.js";
|
|
11
11
|
import { declarationKey } from "./ref.js";
|
|
12
12
|
import { CODE_FORM, SuppressionError, blankReason, isEntryPath, isPartKind, isSymbolRef, siteText, } from "./suppress.js";
|
|
13
13
|
import { message } from "./product.js";
|
|
@@ -130,28 +130,27 @@ function byReferenceOf(symbols) {
|
|
|
130
130
|
}
|
|
131
131
|
/** Reads one document at the target root, binding each record against the run's declarations. */
|
|
132
132
|
function readDocument(host, targetRoot, symbols, shape) {
|
|
133
|
-
const
|
|
134
|
-
if (
|
|
133
|
+
const text = readTargetDocument(host, targetRoot, shape.file);
|
|
134
|
+
if (text === undefined) {
|
|
135
135
|
return { records: [], refusals: [] };
|
|
136
136
|
}
|
|
137
|
-
const text = host.readFile(file);
|
|
138
137
|
const malformed = (detail, site = documentSite(shape)) => {
|
|
139
138
|
throw new SuppressionError(shape.mechanism, siteText(site), detail, shape.documentWant);
|
|
140
139
|
};
|
|
141
|
-
let
|
|
140
|
+
let document;
|
|
142
141
|
try {
|
|
143
|
-
|
|
142
|
+
document = readStrict(text, shape.array);
|
|
144
143
|
}
|
|
145
144
|
catch (error) {
|
|
146
|
-
|
|
145
|
+
if (!(error instanceof StrictJSONError)) {
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
return malformed(error.message, positionAt(text, shape.file, error.offset));
|
|
147
149
|
}
|
|
150
|
+
const parsed = document.value;
|
|
148
151
|
if (!isRecord(parsed)) {
|
|
149
152
|
return malformed(shape.file);
|
|
150
153
|
}
|
|
151
|
-
const walk = walkDocument(text, shape.array);
|
|
152
|
-
if (walk.twice !== undefined) {
|
|
153
|
-
malformed(`the member ${JSON.stringify(walk.twice.name)} is written twice, so neither value is chosen`, positionAt(text, shape.file, walk.twice.offset));
|
|
154
|
-
}
|
|
155
154
|
for (const key of Object.keys(parsed)) {
|
|
156
155
|
if (key !== "description" && key !== shape.array) {
|
|
157
156
|
malformed(`the member ${JSON.stringify(key)} is not one the grammar declares`);
|
|
@@ -166,7 +165,7 @@ function readDocument(host, targetRoot, symbols, shape) {
|
|
|
166
165
|
const records = [];
|
|
167
166
|
const refusals = [];
|
|
168
167
|
held.forEach((value, index) => {
|
|
169
|
-
const offset =
|
|
168
|
+
const offset = document.starts.get(index);
|
|
170
169
|
const site = offset === undefined ? documentSite(shape) : positionAt(text, shape.file, offset);
|
|
171
170
|
const read = readRecord(value, site, byReference, shape);
|
|
172
171
|
records.push(...read.records);
|
|
@@ -278,10 +277,9 @@ function* separatorsEscaped(pieces) {
|
|
|
278
277
|
* A baseline holding one row per finding, in the order given, which is the report's, and
|
|
279
278
|
* the other language's rows `kept`, Go's rows ahead of TypeScript's and each scheme's in
|
|
280
279
|
* the order given, so two analyzers writing one document in either order write the same
|
|
281
|
-
* bytes, in the form grammar/suppression.md fixes. Every
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
* rather than written, because the grammar requires a reason on every row.
|
|
280
|
+
* bytes, in the form grammar/suppression.md fixes. Every row carries the provenance as
|
|
281
|
+
* its reason, so a finding missing a value a row needs, or an identity missing its name
|
|
282
|
+
* or version, is refused rather than written.
|
|
285
283
|
*/
|
|
286
284
|
export function writeBaseline(findings, provenance, kept = []) {
|
|
287
285
|
if (provenance.analyzer === "" || provenance.version === "") {
|
package/dist/src/suppress.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* withholds every such finding anyway, and stale otherwise.
|
|
8
8
|
*/
|
|
9
9
|
import { KIND_ROWS, SYMBOL_SUBJECTS, } from "./contract/contract.generated.js";
|
|
10
|
-
import {
|
|
10
|
+
import { UsageError } from "./usage.js";
|
|
11
11
|
import { positionKey } from "./position.js";
|
|
12
12
|
import { isRef } from "./ref.js";
|
|
13
13
|
/**
|
|
@@ -15,9 +15,9 @@ import { isRef } from "./ref.js";
|
|
|
15
15
|
* instruction the analysis cannot carry out. It is a malformed input, so the command line
|
|
16
16
|
* answers it with the usage code.
|
|
17
17
|
*/
|
|
18
|
-
export class SuppressionError extends
|
|
18
|
+
export class SuppressionError extends UsageError {
|
|
19
19
|
constructor(mechanism, at, text, want) {
|
|
20
|
-
super(
|
|
20
|
+
super(`${mechanism} ${at}: ${text}: want ${want}`);
|
|
21
21
|
this.name = "SuppressionError";
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
* type, because nothing ties the name to a type.
|
|
13
13
|
*/
|
|
14
14
|
import { readComponent } from "./component-files.js";
|
|
15
|
-
import { ConfigError } from "./config.js";
|
|
16
15
|
import { memberNames } from "./member-names.js";
|
|
17
16
|
import { extensionOf, isAbsolutePath, joinPath, normalizePath, relativePath } from "./paths.js";
|
|
18
17
|
import { isComponentFile } from "./position.js";
|
|
19
18
|
import { bytewise } from "./text/compare.js";
|
|
20
19
|
import { filesBelow } from "./fs/tree.js";
|
|
20
|
+
import { UsageError } from "./usage.js";
|
|
21
21
|
/** The kinds of declaration an action can name. */
|
|
22
22
|
const READ_BY_A_TEMPLATE = new Set([
|
|
23
23
|
"method",
|
|
@@ -51,18 +51,18 @@ export function readTemplates(host, targetRoot, analysis) {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
|
-
for (const configured of analysis.
|
|
54
|
+
for (const configured of analysis.template_dirs) {
|
|
55
55
|
const dir = normalizePath(configured);
|
|
56
56
|
if (isAbsolutePath(dir) ||
|
|
57
57
|
dir === ".." ||
|
|
58
58
|
dir.startsWith("../") ||
|
|
59
59
|
host.kindOf(joinPath(targetRoot, dir)) !== "directory") {
|
|
60
|
-
throw new
|
|
60
|
+
throw new UsageError(`analysis.template_dirs: ${JSON.stringify(configured)} is not a directory below the target root`);
|
|
61
61
|
}
|
|
62
62
|
read(dir);
|
|
63
63
|
}
|
|
64
64
|
return {
|
|
65
|
-
delimiters: analysis.
|
|
65
|
+
delimiters: analysis.template_delimiters,
|
|
66
66
|
files: [...files]
|
|
67
67
|
.map(([path, text]) => ({ path, text }))
|
|
68
68
|
.sort((a, b) => bytewise(a.path, b.path)),
|
|
@@ -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,6 +1,6 @@
|
|
|
1
1
|
import { CONTRACT_VERSION, EXIT_CLEAN, EXIT_FAILURE, } from "../contract/contract.generated.js";
|
|
2
2
|
import { runAnalysis, runBaselineWrite, unansweredCounts, } from "../analysis.js";
|
|
3
|
-
import {
|
|
3
|
+
import { kindOf } from "../config.js";
|
|
4
4
|
import { CONFORMANCE, DECLARED_GAPS } from "../conformance.js";
|
|
5
5
|
import { moduleIdentity } from "../consumers.js";
|
|
6
6
|
import { analyzerProvenance, verdictOf } from "../findings-pass.js";
|
|
@@ -14,8 +14,9 @@ import { parseTemplate, TemplateError } from "../reporters/template.js";
|
|
|
14
14
|
import { resolve } from "../resolve.js";
|
|
15
15
|
import { otherSchemeRows, rowReason, writeBaseline } from "../suppress-file.js";
|
|
16
16
|
import {} from "./verb.js";
|
|
17
|
+
import { UsageError } from "../usage.js";
|
|
17
18
|
import { bytewise } from "../text/compare.js";
|
|
18
|
-
import { diagnose,
|
|
19
|
+
import { diagnose, message } from "../product.js";
|
|
19
20
|
/** The options `analyze` takes beside the ones every verb takes. */
|
|
20
21
|
export const ANALYZE_OPTIONS = {
|
|
21
22
|
plain: ["report", "baseline-write", "exit-code", "template"],
|
|
@@ -41,10 +42,10 @@ function formatsOf(named, configured) {
|
|
|
41
42
|
const seen = new Set();
|
|
42
43
|
for (const format of formats) {
|
|
43
44
|
if (!RENDERINGS.has(format)) {
|
|
44
|
-
throw new
|
|
45
|
+
throw new UsageError(`${JSON.stringify(format)} is not a format this analyzer renders: the formats are ${formatNames()}`);
|
|
45
46
|
}
|
|
46
47
|
if (seen.has(format)) {
|
|
47
|
-
throw new
|
|
48
|
+
throw new UsageError(`the format ${JSON.stringify(format)} is named twice`);
|
|
48
49
|
}
|
|
49
50
|
seen.add(format);
|
|
50
51
|
}
|
|
@@ -130,7 +131,7 @@ function counted(n, noun) {
|
|
|
130
131
|
function templateOf(host, invoked, named, formats) {
|
|
131
132
|
if (named === undefined || named === "") {
|
|
132
133
|
if (formats.includes("template")) {
|
|
133
|
-
throw new
|
|
134
|
+
throw new UsageError("the template format renders the template --template names, and none was named");
|
|
134
135
|
}
|
|
135
136
|
return undefined;
|
|
136
137
|
}
|
|
@@ -139,7 +140,7 @@ function templateOf(host, invoked, named, formats) {
|
|
|
139
140
|
source = host.readFile(resolvePath(invoked, named));
|
|
140
141
|
}
|
|
141
142
|
catch (error) {
|
|
142
|
-
throw new
|
|
143
|
+
throw new UsageError(`--template=${named}: ${message(error)}`);
|
|
143
144
|
}
|
|
144
145
|
try {
|
|
145
146
|
return parseTemplate(source);
|
|
@@ -148,7 +149,7 @@ function templateOf(host, invoked, named, formats) {
|
|
|
148
149
|
if (!(error instanceof TemplateError)) {
|
|
149
150
|
throw error;
|
|
150
151
|
}
|
|
151
|
-
throw new
|
|
152
|
+
throw new UsageError(`--template=${named}: ${error.message}`);
|
|
152
153
|
}
|
|
153
154
|
}
|
|
154
155
|
/**
|
|
@@ -162,17 +163,13 @@ function templateOf(host, invoked, named, formats) {
|
|
|
162
163
|
export const analyzeVerb = ({ err, host, inputs, scope, openClient, option, repeated }) => {
|
|
163
164
|
const reportPath = option("report");
|
|
164
165
|
if (reportPath === undefined || reportPath === "") {
|
|
165
|
-
throw new
|
|
166
|
+
throw new UsageError("analyze writes its report to the path --report names, and no path was named");
|
|
166
167
|
}
|
|
167
168
|
const exitCode = option("exit-code") ?? EXIT_CODE_ON;
|
|
168
169
|
if (exitCode !== EXIT_CODE_ON && exitCode !== EXIT_CODE_OFF) {
|
|
169
|
-
throw new
|
|
170
|
+
throw new UsageError(`--exit-code=${JSON.stringify(exitCode)}: the values are ${EXIT_CODE_ON} and ${EXIT_CODE_OFF}`);
|
|
170
171
|
}
|
|
171
172
|
const { config, provenance } = resolve(inputs());
|
|
172
|
-
const kind = config.targetKind;
|
|
173
|
-
if (kind === "") {
|
|
174
|
-
throw new ConfigError("missing-target-kind", "target.kind", "target.kind is not set");
|
|
175
|
-
}
|
|
176
173
|
const invoked = host.workingDirectory();
|
|
177
174
|
const baseline = option("baseline-write");
|
|
178
175
|
const documents = {
|
|
@@ -227,7 +224,7 @@ export const analyzeVerb = ({ err, host, inputs, scope, openClient, option, repe
|
|
|
227
224
|
conformance: CONFORMANCE,
|
|
228
225
|
declaredGaps: DECLARED_GAPS,
|
|
229
226
|
target: {
|
|
230
|
-
kind: kindOf(kind
|
|
227
|
+
kind: kindOf(config.target.kind),
|
|
231
228
|
root,
|
|
232
229
|
identity: moduleIdentity(host, scoped.target),
|
|
233
230
|
},
|
|
@@ -249,7 +246,7 @@ export const analyzeVerb = ({ err, host, inputs, scope, openClient, option, repe
|
|
|
249
246
|
...unseenLoadNotes(run.unseenLoads),
|
|
250
247
|
],
|
|
251
248
|
});
|
|
252
|
-
report = capFindings(sortFindings(built, config.reporters.sort), config.reporters.
|
|
249
|
+
report = capFindings(sortFindings(built, config.reporters.sort), config.reporters.max_findings);
|
|
253
250
|
verdict = verdictOf(result, config);
|
|
254
251
|
}
|
|
255
252
|
catch (error) {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { EXIT_CLEAN, EXIT_USAGE } from "../contract/contract.generated.js";
|
|
2
2
|
import { runAnalysis } from "../analysis.js";
|
|
3
3
|
import { listing } from "../components.js";
|
|
4
|
-
import { ConfigError } from "../config.js";
|
|
5
4
|
import { OUTSIDE, swept as judged } from "../graph.js";
|
|
6
5
|
import { referenceKey } from "../matrix.js";
|
|
7
6
|
import { partialNotes } from "../partial.js";
|
|
@@ -9,6 +8,7 @@ import { resolvePath } from "../paths.js";
|
|
|
9
8
|
import { positionKey } from "../position.js";
|
|
10
9
|
import { resolve } from "../resolve.js";
|
|
11
10
|
import {} from "./verb.js";
|
|
11
|
+
import { UsageError } from "../usage.js";
|
|
12
12
|
import { bytewise } from "../text/compare.js";
|
|
13
13
|
import { diagnose } from "../product.js";
|
|
14
14
|
/**
|
|
@@ -36,10 +36,10 @@ function namedSymbol(option) {
|
|
|
36
36
|
});
|
|
37
37
|
const [only, ...more] = named;
|
|
38
38
|
if (only === undefined) {
|
|
39
|
-
throw new
|
|
39
|
+
throw new UsageError("explain explains the symbol --why, --why-live or --why-not names, and none was named");
|
|
40
40
|
}
|
|
41
41
|
if (more.length > 0) {
|
|
42
|
-
throw new
|
|
42
|
+
throw new UsageError(`explain explains one symbol, and ${named.map((one) => `--${one.name}`).join(", ")} each name one`);
|
|
43
43
|
}
|
|
44
44
|
return only.value;
|
|
45
45
|
}
|
|
@@ -2,7 +2,7 @@ import { EXIT_CLEAN } from "../contract/contract.generated.js";
|
|
|
2
2
|
import { runProjects } from "../analysis.js";
|
|
3
3
|
import { partialNotes } from "../partial.js";
|
|
4
4
|
import { resolvePath } from "../paths.js";
|
|
5
|
-
import {
|
|
5
|
+
import { resolvePartial } from "../resolve.js";
|
|
6
6
|
import {} from "./verb.js";
|
|
7
7
|
import { diagnose } from "../product.js";
|
|
8
8
|
/**
|
|
@@ -13,7 +13,7 @@ import { diagnose } from "../product.js";
|
|
|
13
13
|
* instead, and one that fails the analysis fails this command the same way.
|
|
14
14
|
*/
|
|
15
15
|
export const printProjectsVerb = ({ out, err, host, inputs, scope, openClient }) => {
|
|
16
|
-
const config =
|
|
16
|
+
const config = resolvePartial(inputs());
|
|
17
17
|
const scoped = scope();
|
|
18
18
|
const answer = runProjects(openClient(config), host, scoped, config);
|
|
19
19
|
for (const id of answer.configurations) {
|
package/jsr.json
CHANGED