@cplieger/deadset-ts 8.0.0-dev.7 → 8.0.0-dev.9
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 +94 -299
- package/dist/src/config.js +6 -83
- package/dist/src/configuration-files.js +2 -9
- package/dist/src/configured-declarations.js +15 -20
- package/dist/src/consumers.js +3 -30
- package/dist/src/contract/config.generated.js +544 -0
- package/dist/src/contract/contract.generated.js +20 -10
- package/dist/src/contract/report.generated.js +3 -2
- package/dist/src/conventions.js +3 -8
- 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/failure.js +36 -0
- 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 +52 -0
- package/dist/src/fs/manifest.js +7 -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/load/classify.js +236 -0
- package/dist/src/load/diagnostics.js +79 -0
- package/dist/src/load/discover.js +230 -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 +22 -0
- package/dist/src/load/module-lookup.js +37 -0
- package/dist/src/load/plan.js +155 -0
- package/dist/src/{test-globals.js → load/test-globals.js} +51 -56
- package/dist/src/load/test-joins.js +82 -0
- package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +3 -3
- package/dist/src/{workspace-programs.js → load/workspace-programs.js} +37 -40
- package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +21 -59
- package/dist/src/{workspace-run.js → load/workspace-run.js} +4 -3
- package/dist/src/{workspace.js → load/workspace.js} +21 -17
- package/dist/src/outside-tests.js +1 -1
- 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 +59 -72
- package/dist/src/schema.js +320 -344
- package/dist/src/scope.js +37 -113
- package/dist/src/serialization-contract.js +1 -1
- package/dist/src/session.js +22 -28
- package/dist/src/source-files.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/type-errors.js +11 -216
- package/dist/src/usage.js +10 -0
- package/dist/src/verbs/analyze.js +100 -122
- package/dist/src/verbs/describe.js +5 -12
- package/dist/src/verbs/explain.js +3 -3
- package/dist/src/verbs/print-projects.js +2 -2
- package/dist/src/verbs/version.js +12 -2
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/analysis.ts +95 -460
- package/src/config.ts +31 -298
- package/src/configuration-files.ts +8 -10
- package/src/configured-declarations.ts +18 -23
- package/src/consumers.ts +6 -57
- package/src/contract/config.generated.ts +798 -0
- package/src/contract/contract.generated.ts +20 -10
- package/src/contract/report.generated.ts +3 -2
- package/src/conventions.ts +6 -12
- package/src/edges.ts +39 -124
- package/src/exempt-classes.ts +0 -5
- package/src/exempt.ts +4 -4
- package/src/failure.ts +63 -0
- 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 +70 -0
- package/src/fs/manifest.ts +8 -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/load/classify.ts +352 -0
- package/src/load/diagnostics.ts +141 -0
- package/src/load/discover.ts +378 -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 +61 -0
- package/src/load/module-lookup.ts +50 -0
- package/src/load/plan.ts +258 -0
- package/src/{test-globals.ts → load/test-globals.ts} +59 -65
- package/src/load/test-joins.ts +98 -0
- package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +4 -4
- package/src/{workspace-programs.ts → load/workspace-programs.ts} +59 -53
- package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +31 -78
- package/src/{workspace-run.ts → load/workspace-run.ts} +14 -11
- package/src/{workspace.ts → load/workspace.ts} +28 -21
- package/src/outside-tests.ts +1 -1
- package/src/partial.ts +1 -1
- package/src/print.ts +11 -92
- package/src/report-schema.ts +3 -299
- package/src/resolve.ts +281 -1105
- package/src/run.ts +68 -80
- package/src/schema.ts +391 -389
- package/src/scope.ts +45 -131
- package/src/serialization-contract.ts +1 -1
- package/src/session.ts +52 -53
- package/src/source-files.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/type-errors.ts +18 -288
- package/src/usage.ts +10 -0
- package/src/verbs/analyze.ts +139 -157
- package/src/verbs/describe.ts +4 -11
- package/src/verbs/explain.ts +3 -7
- package/src/verbs/print-projects.ts +2 -2
- package/src/verbs/verb.ts +2 -2
- package/src/verbs/version.ts +13 -2
- package/dist/src/base-url.js +0 -18
- package/dist/src/discover.js +0 -374
- package/dist/src/setup-failure.js +0 -19
- package/dist/src/test-joins.js +0 -101
- package/src/base-url.ts +0 -24
- package/src/discover.ts +0 -548
- package/src/setup-failure.ts +0 -37
- package/src/test-joins.ts +0 -118
package/dist/src/scope.js
CHANGED
|
@@ -1,149 +1,73 @@
|
|
|
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 {
|
|
4
|
-
import {
|
|
4
|
+
import { MARKERS } from "./load/discover.js";
|
|
5
|
+
import { failureExit } from "./failure.js";
|
|
5
6
|
import { message } from "./product.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const ROLE_CONSUMER = "consumer";
|
|
7
|
+
import { firstRefusal } from "./schema.js";
|
|
8
|
+
import { UsageError } from "./usage.js";
|
|
9
9
|
/**
|
|
10
10
|
* A scope document above this length is refused rather than decoded. It names one
|
|
11
11
|
* target and its consumers, so a larger file is not one.
|
|
12
12
|
*/
|
|
13
13
|
const MAX_DOCUMENT_LENGTH = 1 << 20;
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* The bytes read of a scope document. A UTF-16 unit decodes from at most three bytes,
|
|
16
|
+
* so a document past the bound is past it within these.
|
|
17
17
|
*/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.name = "ScopeError";
|
|
22
|
-
}
|
|
18
|
+
const MAX_DOCUMENT_BYTES = 3 * (MAX_DOCUMENT_LENGTH + 1);
|
|
19
|
+
function refusal(file, place, reason) {
|
|
20
|
+
return new UsageError(`scope: ${file}: ${place === "" ? "" : `${place}: `}${reason}`);
|
|
23
21
|
}
|
|
24
|
-
const MODULE = {
|
|
25
|
-
kind: "section",
|
|
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
22
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
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
|
-
}
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Decodes the scope document at `file`. Decoding is strict: an undeclared key, a
|
|
94
|
-
* key written twice in one object, and a value of a type the document does not
|
|
95
|
-
* admit, `null` included, are errors, and a relative path inside the document
|
|
96
|
-
* resolves against the document's own directory.
|
|
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.
|
|
23
|
+
* Decodes the scope document at `file`, held to the Contract's scope schema, a relative
|
|
24
|
+
* path resolved against the document's own directory. A document that cannot be read or is
|
|
25
|
+
* refused is a usage error, as any document the invocation names. `workspace` is read by Go consumers
|
|
26
|
+
* alone: a TypeScript project resolves the target through its own compiler configuration.
|
|
101
27
|
*/
|
|
102
28
|
export function readScope(host, file) {
|
|
103
29
|
const absolute = resolvePath(host.workingDirectory(), file);
|
|
104
30
|
let text;
|
|
105
31
|
try {
|
|
106
|
-
text = host.readFile(absolute);
|
|
32
|
+
text = host.readFile(absolute, MAX_DOCUMENT_BYTES);
|
|
107
33
|
}
|
|
108
34
|
catch (error) {
|
|
109
|
-
throw new
|
|
35
|
+
throw new UsageError(`scope: ${file}: ${message(error)}`);
|
|
110
36
|
}
|
|
111
37
|
if (text.length > MAX_DOCUMENT_LENGTH) {
|
|
112
|
-
throw new
|
|
38
|
+
throw new UsageError(`scope: ${file} is longer than ${String(MAX_DOCUMENT_LENGTH)} characters`);
|
|
113
39
|
}
|
|
114
40
|
let value;
|
|
115
41
|
try {
|
|
116
|
-
value =
|
|
42
|
+
value = readStrict(text).value;
|
|
117
43
|
}
|
|
118
44
|
catch (error) {
|
|
119
|
-
|
|
45
|
+
if (!(error instanceof StrictJSONError)) {
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
throw refusal(file, dotted(error.place), locatedFault(text, error));
|
|
120
49
|
}
|
|
121
|
-
|
|
122
|
-
|
|
50
|
+
const refused = firstRefusal(SCOPE_SCHEMA, value);
|
|
51
|
+
if (refused !== undefined) {
|
|
52
|
+
throw refusal(file, dotted(placeIn(value, refused.pointer, refused.member)), refused.reason);
|
|
123
53
|
}
|
|
124
|
-
|
|
125
|
-
stringMember(value, "workspace", "");
|
|
54
|
+
const declared = value;
|
|
126
55
|
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 };
|
|
56
|
+
const moduleOf = (module) => ({
|
|
57
|
+
id: module.id ?? "",
|
|
58
|
+
path: isAbsolutePath(module.path) ? module.path : joinPath(documentDir, module.path),
|
|
59
|
+
});
|
|
60
|
+
return { target: moduleOf(declared.target), consumers: (declared.consumers ?? []).map(moduleOf) };
|
|
137
61
|
}
|
|
138
|
-
/**
|
|
62
|
+
/**
|
|
63
|
+
* The single-package scope one directory is, when no scope document is given. A target that is
|
|
64
|
+
* no directory holds no program.
|
|
65
|
+
*/
|
|
139
66
|
export function scopeForDir(host, dir) {
|
|
140
67
|
const path = resolvePath(host.workingDirectory(), dir);
|
|
141
68
|
const kind = host.kindOf(path);
|
|
142
|
-
if (kind === "absent") {
|
|
143
|
-
throw new ScopeError(`scope: ${path} does not exist`);
|
|
144
|
-
}
|
|
145
69
|
if (kind !== "directory") {
|
|
146
|
-
throw
|
|
70
|
+
throw failureExit("no-program", `the target ${path} ${kind === "absent" ? "does not exist" : "is not a directory"}, so it holds no compiler configuration, no file named ${MARKERS.join(", ")}: name the directory that holds one`);
|
|
147
71
|
}
|
|
148
72
|
return { target: { id: "", path }, consumers: [] };
|
|
149
73
|
}
|
|
@@ -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. */
|
package/dist/src/session.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { API, SymbolFlags } from "@typescript/native/sync";
|
|
2
2
|
import { componentLayer, mapperManifest } from "./component-layer.js";
|
|
3
|
-
import { DiscoveryError } from "./discover.js";
|
|
4
3
|
import { joinPath } from "./paths.js";
|
|
5
4
|
import { queriesOf, UnansweredLog, } from "./query.js";
|
|
6
5
|
import { MANIFEST } from "./fs/manifest.js";
|
|
@@ -253,14 +252,13 @@ function projectsOf(snapshot, built, written) {
|
|
|
253
252
|
return opened;
|
|
254
253
|
}
|
|
255
254
|
/**
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
* `first` runs over every opened project before the first visit and `last` after the last,
|
|
261
|
-
* both while the client answers; the snapshot and the client are then released.
|
|
255
|
+
* Opens every project the compiler configurations name in one never updated snapshot of one
|
|
256
|
+
* client, and hands them to `use` while the client answers; the snapshot and the client are
|
|
257
|
+
* then released. A configuration `options.programs` describes is built as that program, or
|
|
258
|
+
* opened from the configuration it writes. One the snapshot holds nothing for has no view.
|
|
262
259
|
*/
|
|
263
|
-
export function
|
|
260
|
+
export function openSession(engine, configFiles, options, use) {
|
|
261
|
+
const programs = options.programs ?? new Map();
|
|
264
262
|
try {
|
|
265
263
|
const built = [];
|
|
266
264
|
const params = [];
|
|
@@ -282,30 +280,13 @@ export function runSession(engine, configFiles, visit, unopened = "refuse", prog
|
|
|
282
280
|
const opened = projectsOf(snapshot, built, written);
|
|
283
281
|
const log = new UnansweredLog();
|
|
284
282
|
const views = [];
|
|
285
|
-
const missing = [];
|
|
286
283
|
for (const configFile of configFiles) {
|
|
287
284
|
const project = opened.get(configFile);
|
|
288
|
-
if (project
|
|
289
|
-
|
|
290
|
-
throw new DiscoveryError(`${configFile} opened no project`, []);
|
|
291
|
-
}
|
|
292
|
-
missing.push(configFile);
|
|
293
|
-
continue;
|
|
285
|
+
if (project !== undefined) {
|
|
286
|
+
views.push(viewOf(project, configFile, engine, log));
|
|
294
287
|
}
|
|
295
|
-
views.push(viewOf(project, configFile, engine, log));
|
|
296
288
|
}
|
|
297
|
-
|
|
298
|
-
configFile: view.configFile,
|
|
299
|
-
fileNames: () => view.ownSourceFiles().map((file) => file.fileName),
|
|
300
|
-
})));
|
|
301
|
-
const projects = views.map((view) => visit(view));
|
|
302
|
-
last();
|
|
303
|
-
return {
|
|
304
|
-
projects,
|
|
305
|
-
unopened: missing,
|
|
306
|
-
timing: engine.getTimingInfo(),
|
|
307
|
-
unanswered: log.questions,
|
|
308
|
-
};
|
|
289
|
+
return use({ views }, () => log.questions);
|
|
309
290
|
}
|
|
310
291
|
finally {
|
|
311
292
|
snapshot.dispose();
|
|
@@ -315,3 +296,16 @@ export function runSession(engine, configFiles, visit, unopened = "refuse", prog
|
|
|
315
296
|
engine.close();
|
|
316
297
|
}
|
|
317
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* Runs `visit` over the project {@link openSession} opens for each configuration, and answers
|
|
301
|
+
* in their order; a configuration the snapshot opens nothing for throws before any visit.
|
|
302
|
+
*/
|
|
303
|
+
export function runSession(engine, configFiles, visit) {
|
|
304
|
+
return openSession(engine, configFiles, {}, (opened) => {
|
|
305
|
+
const unopened = configFiles.find((configFile) => !opened.views.some((view) => view.configFile === configFile));
|
|
306
|
+
if (unopened !== undefined) {
|
|
307
|
+
throw new Error(`${unopened} opened no project`);
|
|
308
|
+
}
|
|
309
|
+
return { projects: opened.views.map((view) => visit(view)), timing: engine.getTimingInfo() };
|
|
310
|
+
});
|
|
311
|
+
}
|
package/dist/src/source-files.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* in a document or a string names one: as the file itself, or as a file the
|
|
4
4
|
* compiler emits from it.
|
|
5
5
|
*/
|
|
6
|
-
import { commonDirectory, emittedFrom } from "./emit-map.js";
|
|
6
|
+
import { commonDirectory, emittedFrom } from "./load/emit-map.js";
|
|
7
7
|
import { dirnamePath, joinPath, relativePath, resolvePath } from "./paths.js";
|
|
8
8
|
/** The one character a subpath pattern of a manifest's `exports` holds. */
|
|
9
9
|
const SUBPATH_WILDCARD = "*";
|
|
@@ -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)),
|