@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/src/verbs/analyze.ts
CHANGED
|
@@ -1,25 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CONTRACT_VERSION,
|
|
3
|
-
EXIT_CLEAN,
|
|
4
|
-
EXIT_FAILURE,
|
|
5
|
-
type Format,
|
|
6
|
-
} from "../contract/contract.generated.ts";
|
|
1
|
+
import { CONTRACT_VERSION, EXIT_CLEAN, type Format } from "../contract/contract.generated.ts";
|
|
7
2
|
import {
|
|
8
3
|
runAnalysis,
|
|
9
4
|
runBaselineWrite,
|
|
10
5
|
unansweredCounts,
|
|
11
|
-
type ComponentWarning,
|
|
12
6
|
type RunAnalysis,
|
|
13
7
|
type RunProject,
|
|
14
8
|
} from "../analysis.ts";
|
|
15
|
-
import {
|
|
9
|
+
import { kindOf } from "../config.ts";
|
|
16
10
|
import { CONFORMANCE, DECLARED_GAPS } from "../conformance.ts";
|
|
17
|
-
import { moduleIdentity } from "../consumers.ts";
|
|
18
11
|
import { analyzerProvenance, verdictOf } from "../findings-pass.ts";
|
|
19
12
|
import { dirnamePath, joinPath, normalizePath, relativePath, resolvePath } from "../paths.ts";
|
|
20
13
|
import type { Host } from "../host.ts";
|
|
21
14
|
import type { ManifestEntry } from "../manifest.ts";
|
|
22
|
-
import type
|
|
15
|
+
import { moduleIdentity, type NotBuilt } from "../load/discover.ts";
|
|
23
16
|
import { partialNotes } from "../partial.ts";
|
|
24
17
|
import {
|
|
25
18
|
buildReport,
|
|
@@ -29,7 +22,6 @@ import {
|
|
|
29
22
|
type ReportNotBuilt,
|
|
30
23
|
type ReportNote,
|
|
31
24
|
} from "../report.ts";
|
|
32
|
-
import type { Report } from "../contract/report.generated.ts";
|
|
33
25
|
import { reportRefusals } from "../report-schema.ts";
|
|
34
26
|
import { json } from "../reporters/json.ts";
|
|
35
27
|
import { RENDERINGS } from "../reporters/reporters.ts";
|
|
@@ -38,8 +30,11 @@ import { resolve } from "../resolve.ts";
|
|
|
38
30
|
import { otherSchemeRows, rowReason, writeBaseline, type Recorded } from "../suppress-file.ts";
|
|
39
31
|
import type { UnseenLoad } from "../unseen-loads.ts";
|
|
40
32
|
import { type Verb, type VerbOptions } from "./verb.ts";
|
|
33
|
+
import { UsageError } from "../usage.ts";
|
|
41
34
|
import { bytewise } from "../text/compare.ts";
|
|
42
|
-
import {
|
|
35
|
+
import { productFault, failureExit } from "../failure.ts";
|
|
36
|
+
import { diagnose, message } from "../product.ts";
|
|
37
|
+
import { ownVersion } from "./version.ts";
|
|
43
38
|
|
|
44
39
|
/** The options `analyze` takes beside the ones every verb takes. */
|
|
45
40
|
export const ANALYZE_OPTIONS: VerbOptions = {
|
|
@@ -51,14 +46,6 @@ export const ANALYZE_OPTIONS: VerbOptions = {
|
|
|
51
46
|
const EXIT_CODE_ON = "on";
|
|
52
47
|
const EXIT_CODE_OFF = "off";
|
|
53
48
|
|
|
54
|
-
/** A path of the run no report can name, which ends the run with the failure code. */
|
|
55
|
-
class ReportPathError extends Error {
|
|
56
|
-
constructor(message: string) {
|
|
57
|
-
super(message);
|
|
58
|
-
this.name = "ReportPathError";
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
49
|
/** The formats this analyzer renders, for a message that names them. */
|
|
63
50
|
function formatNames(): string {
|
|
64
51
|
return [...RENDERINGS.keys()].sort(bytewise).join(", ");
|
|
@@ -70,14 +57,12 @@ function formatsOf(named: readonly string[], configured: readonly Format[]): rea
|
|
|
70
57
|
const seen = new Set<string>();
|
|
71
58
|
for (const format of formats) {
|
|
72
59
|
if (!RENDERINGS.has(format as Format)) {
|
|
73
|
-
throw new
|
|
74
|
-
"malformed",
|
|
75
|
-
"",
|
|
60
|
+
throw new UsageError(
|
|
76
61
|
`${JSON.stringify(format)} is not a format this analyzer renders: the formats are ${formatNames()}`,
|
|
77
62
|
);
|
|
78
63
|
}
|
|
79
64
|
if (seen.has(format)) {
|
|
80
|
-
throw new
|
|
65
|
+
throw new UsageError(`the format ${JSON.stringify(format)} is named twice`);
|
|
81
66
|
}
|
|
82
67
|
seen.add(format);
|
|
83
68
|
}
|
|
@@ -95,7 +80,8 @@ function runRelative(invoked: string, path: string): string {
|
|
|
95
80
|
}
|
|
96
81
|
const below = relativePath(invoked, path);
|
|
97
82
|
if (below === undefined) {
|
|
98
|
-
throw
|
|
83
|
+
throw failureExit(
|
|
84
|
+
"unreportable-path",
|
|
99
85
|
`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`,
|
|
100
86
|
);
|
|
101
87
|
}
|
|
@@ -147,8 +133,9 @@ function notBuiltOf(dropped: readonly NotBuilt[], targetRoot: string): readonly
|
|
|
147
133
|
return dropped.map((one) => {
|
|
148
134
|
const project = relativePath(targetRoot, one.configFile);
|
|
149
135
|
if (project === undefined) {
|
|
150
|
-
throw
|
|
151
|
-
|
|
136
|
+
throw failureExit(
|
|
137
|
+
"unreportable-path",
|
|
138
|
+
`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`,
|
|
152
139
|
);
|
|
153
140
|
}
|
|
154
141
|
return { id: one.id, project, error: one.error };
|
|
@@ -163,14 +150,33 @@ function configurationsOf(
|
|
|
163
150
|
return projects.map((one) => {
|
|
164
151
|
const project = relativePath(targetRoot, one.configFile);
|
|
165
152
|
if (project === undefined) {
|
|
166
|
-
throw
|
|
167
|
-
|
|
153
|
+
throw failureExit(
|
|
154
|
+
"unreportable-path",
|
|
155
|
+
`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`,
|
|
168
156
|
);
|
|
169
157
|
}
|
|
170
158
|
return { id: one.id, project };
|
|
171
159
|
});
|
|
172
160
|
}
|
|
173
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Writes one document the run was asked for. A document that cannot be produced or written
|
|
164
|
+
* ends the run with the run failure that names it and `fix`, the documents written before it
|
|
165
|
+
* kept.
|
|
166
|
+
*/
|
|
167
|
+
function writeDocument(
|
|
168
|
+
host: Host,
|
|
169
|
+
path: string,
|
|
170
|
+
fix: string,
|
|
171
|
+
produce: () => Iterable<string>,
|
|
172
|
+
): void {
|
|
173
|
+
try {
|
|
174
|
+
host.writeDocument(path, produce());
|
|
175
|
+
} catch (error: unknown) {
|
|
176
|
+
throw failureExit("unwritten-output", `${path}: ${message(error)}: ${fix}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
174
180
|
/** A count with its noun, so a message reads for one record as well as for several. */
|
|
175
181
|
function counted(n: number, noun: string): string {
|
|
176
182
|
return `${String(n)} ${noun}${n === 1 ? "" : "s"}`;
|
|
@@ -189,9 +195,7 @@ function templateOf(
|
|
|
189
195
|
): Template | undefined {
|
|
190
196
|
if (named === undefined || named === "") {
|
|
191
197
|
if (formats.includes("template")) {
|
|
192
|
-
throw new
|
|
193
|
-
"malformed",
|
|
194
|
-
"",
|
|
198
|
+
throw new UsageError(
|
|
195
199
|
"the template format renders the template --template names, and none was named",
|
|
196
200
|
);
|
|
197
201
|
}
|
|
@@ -201,7 +205,7 @@ function templateOf(
|
|
|
201
205
|
try {
|
|
202
206
|
source = host.readFile(resolvePath(invoked, named));
|
|
203
207
|
} catch (error: unknown) {
|
|
204
|
-
throw new
|
|
208
|
+
throw new UsageError(`--template=${named}: ${message(error)}`);
|
|
205
209
|
}
|
|
206
210
|
try {
|
|
207
211
|
return parseTemplate(source);
|
|
@@ -209,7 +213,7 @@ function templateOf(
|
|
|
209
213
|
if (!(error instanceof TemplateError)) {
|
|
210
214
|
throw error;
|
|
211
215
|
}
|
|
212
|
-
throw new
|
|
216
|
+
throw new UsageError(`--template=${named}: ${error.message}`);
|
|
213
217
|
}
|
|
214
218
|
}
|
|
215
219
|
|
|
@@ -231,25 +235,17 @@ interface Documents {
|
|
|
231
235
|
export const analyzeVerb: Verb = ({ err, host, inputs, scope, openClient, option, repeated }) => {
|
|
232
236
|
const reportPath = option("report");
|
|
233
237
|
if (reportPath === undefined || reportPath === "") {
|
|
234
|
-
throw new
|
|
235
|
-
"malformed",
|
|
236
|
-
"",
|
|
238
|
+
throw new UsageError(
|
|
237
239
|
"analyze writes its report to the path --report names, and no path was named",
|
|
238
240
|
);
|
|
239
241
|
}
|
|
240
242
|
const exitCode = option("exit-code") ?? EXIT_CODE_ON;
|
|
241
243
|
if (exitCode !== EXIT_CODE_ON && exitCode !== EXIT_CODE_OFF) {
|
|
242
|
-
throw new
|
|
243
|
-
"malformed",
|
|
244
|
-
"",
|
|
244
|
+
throw new UsageError(
|
|
245
245
|
`--exit-code=${JSON.stringify(exitCode)}: the values are ${EXIT_CODE_ON} and ${EXIT_CODE_OFF}`,
|
|
246
246
|
);
|
|
247
247
|
}
|
|
248
248
|
const { config, provenance } = resolve(inputs());
|
|
249
|
-
const kind = config.targetKind;
|
|
250
|
-
if (kind === "") {
|
|
251
|
-
throw new ConfigError("missing-target-kind", "target.kind", "target.kind is not set");
|
|
252
|
-
}
|
|
253
249
|
const invoked = host.workingDirectory();
|
|
254
250
|
const baseline = option("baseline-write");
|
|
255
251
|
const documents: Documents = {
|
|
@@ -261,126 +257,112 @@ export const analyzeVerb: Verb = ({ err, host, inputs, scope, openClient, option
|
|
|
261
257
|
const scoped = scope();
|
|
262
258
|
const targetRoot = scoped.target.path;
|
|
263
259
|
|
|
264
|
-
|
|
265
|
-
try {
|
|
266
|
-
version = host.analyzerVersion();
|
|
267
|
-
} catch (error: unknown) {
|
|
268
|
-
diagnose(err, message(error));
|
|
269
|
-
return EXIT_FAILURE;
|
|
270
|
-
}
|
|
260
|
+
const version = ownVersion(host);
|
|
271
261
|
|
|
272
|
-
let report: Report;
|
|
273
262
|
let recorded: readonly Recorded[];
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
let
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
host,
|
|
291
|
-
scoped,
|
|
292
|
-
config,
|
|
293
|
-
provenance,
|
|
294
|
-
mode,
|
|
295
|
-
reason,
|
|
296
|
-
);
|
|
297
|
-
analysis = write.first;
|
|
298
|
-
recorded = write.rows;
|
|
299
|
-
}
|
|
300
|
-
const { result, run } = analysis;
|
|
301
|
-
warnings = run.componentWarnings;
|
|
302
|
-
unread = run.componentsUnread.map(
|
|
303
|
-
({ configFile, reason }) =>
|
|
304
|
-
`${relativePath(resolvePath(invoked, targetRoot), configFile) ?? configFile}: its component files are not read: ${reason}`,
|
|
263
|
+
const root = runRelative(invoked, targetRoot);
|
|
264
|
+
const mode = { production: true };
|
|
265
|
+
let analysis: RunAnalysis;
|
|
266
|
+
if (documents.baseline === undefined) {
|
|
267
|
+
analysis = runAnalysis(openClient(config), host, scoped, config, provenance, mode);
|
|
268
|
+
recorded = [];
|
|
269
|
+
} else {
|
|
270
|
+
const reason = rowReason(analyzerProvenance(version));
|
|
271
|
+
const write = runBaselineWrite(
|
|
272
|
+
openClient(config),
|
|
273
|
+
host,
|
|
274
|
+
scoped,
|
|
275
|
+
config,
|
|
276
|
+
provenance,
|
|
277
|
+
mode,
|
|
278
|
+
reason,
|
|
305
279
|
);
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
`${relativePath(resolvePath(invoked, targetRoot), configFile) ?? configFile}: ${error}: the configuration is analyzed without that library`,
|
|
309
|
-
);
|
|
310
|
-
partial = partialNotes({
|
|
311
|
-
notBuilt: run.notBuilt,
|
|
312
|
-
unanswered: run.unanswered,
|
|
313
|
-
targetRoot: resolvePath(invoked, targetRoot),
|
|
314
|
-
});
|
|
315
|
-
const built = buildReport({
|
|
316
|
-
contractVersion: CONTRACT_VERSION,
|
|
317
|
-
version,
|
|
318
|
-
conformance: CONFORMANCE,
|
|
319
|
-
declaredGaps: DECLARED_GAPS,
|
|
320
|
-
target: {
|
|
321
|
-
kind: kindOf(kind, LANGUAGE),
|
|
322
|
-
root,
|
|
323
|
-
identity: moduleIdentity(host, scoped.target),
|
|
324
|
-
},
|
|
325
|
-
configurations: configurationsOf(run.projects, targetRoot),
|
|
326
|
-
notBuilt: notBuiltOf(run.notBuilt, targetRoot),
|
|
327
|
-
loaded: run.consumers.map((consumer) => ({
|
|
328
|
-
id: consumer.id,
|
|
329
|
-
path: runRelative(invoked, consumer.path),
|
|
330
|
-
})),
|
|
331
|
-
unavailable: [],
|
|
332
|
-
result,
|
|
333
|
-
testFileRules: run.testFileRules,
|
|
334
|
-
typeErrorSkips: run.typeErrorSkips,
|
|
335
|
-
unanswered: unansweredCounts(analysis),
|
|
336
|
-
conventionsApplied: run.conventionsApplied,
|
|
337
|
-
listsComponents: config.reporters.cascade === "full",
|
|
338
|
-
notes: [
|
|
339
|
-
...unmappedNotes(run.unmappedEntries, resolvePath(invoked, targetRoot)),
|
|
340
|
-
...unseenLoadNotes(run.unseenLoads),
|
|
341
|
-
],
|
|
342
|
-
});
|
|
343
|
-
report = capFindings(sortFindings(built, config.reporters.sort), config.reporters.maxFindings);
|
|
344
|
-
verdict = verdictOf(result, config);
|
|
345
|
-
} catch (error: unknown) {
|
|
346
|
-
if (!(error instanceof ReportPathError)) {
|
|
347
|
-
throw error;
|
|
348
|
-
}
|
|
349
|
-
diagnose(err, error.message);
|
|
350
|
-
return EXIT_FAILURE;
|
|
280
|
+
analysis = write.first;
|
|
281
|
+
recorded = write.rows;
|
|
351
282
|
}
|
|
283
|
+
const { result, run } = analysis;
|
|
284
|
+
const warnings = run.componentWarnings;
|
|
285
|
+
const unread = run.componentsUnread.map(
|
|
286
|
+
({ configFile, reason }) =>
|
|
287
|
+
`${relativePath(resolvePath(invoked, targetRoot), configFile) ?? configFile}: its component files are not read: ${reason}`,
|
|
288
|
+
);
|
|
289
|
+
const unfound = run.librariesUnfound.map(
|
|
290
|
+
({ configFile, error }) =>
|
|
291
|
+
`${relativePath(resolvePath(invoked, targetRoot), configFile) ?? configFile}: ${error}: the configuration is analyzed without that library`,
|
|
292
|
+
);
|
|
293
|
+
const partial = partialNotes({
|
|
294
|
+
notBuilt: run.notBuilt,
|
|
295
|
+
unanswered: run.unanswered,
|
|
296
|
+
targetRoot: resolvePath(invoked, targetRoot),
|
|
297
|
+
});
|
|
298
|
+
const built = buildReport({
|
|
299
|
+
contractVersion: CONTRACT_VERSION,
|
|
300
|
+
version,
|
|
301
|
+
conformance: CONFORMANCE,
|
|
302
|
+
declaredGaps: DECLARED_GAPS,
|
|
303
|
+
target: {
|
|
304
|
+
kind: kindOf(config.target.kind),
|
|
305
|
+
root,
|
|
306
|
+
identity: moduleIdentity(host, scoped.target),
|
|
307
|
+
},
|
|
308
|
+
configurations: configurationsOf(run.projects, targetRoot),
|
|
309
|
+
notBuilt: notBuiltOf(run.notBuilt, targetRoot),
|
|
310
|
+
loaded: run.consumers.map((consumer) => ({
|
|
311
|
+
id: consumer.id,
|
|
312
|
+
path: runRelative(invoked, consumer.path),
|
|
313
|
+
})),
|
|
314
|
+
unavailable: [],
|
|
315
|
+
result,
|
|
316
|
+
testFileRules: run.testFileRules,
|
|
317
|
+
typeErrorSkips: run.typeErrorSkips,
|
|
318
|
+
unanswered: unansweredCounts(analysis),
|
|
319
|
+
conventionsApplied: run.conventionsApplied,
|
|
320
|
+
listsComponents: config.reporters.cascade === "full",
|
|
321
|
+
notes: [
|
|
322
|
+
...unmappedNotes(run.unmappedEntries, resolvePath(invoked, targetRoot)),
|
|
323
|
+
...unseenLoadNotes(run.unseenLoads),
|
|
324
|
+
],
|
|
325
|
+
});
|
|
326
|
+
const report = capFindings(
|
|
327
|
+
sortFindings(built, config.reporters.sort),
|
|
328
|
+
config.reporters.max_findings,
|
|
329
|
+
);
|
|
330
|
+
const verdict = verdictOf(result, config);
|
|
352
331
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
host
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
const kept =
|
|
373
|
-
host.kindOf(documents.baseline) === "file"
|
|
374
|
-
? otherSchemeRows(host.readFile(documents.baseline))
|
|
375
|
-
: [];
|
|
376
|
-
host.writeDocument(
|
|
377
|
-
documents.baseline,
|
|
378
|
-
writeBaseline(recorded, analyzerProvenance(version), kept),
|
|
332
|
+
const [refused] = reportRefusals(report);
|
|
333
|
+
if (refused !== undefined) {
|
|
334
|
+
throw productFault(
|
|
335
|
+
`built a report that breaks the report schema at ${refused.pointer || "the document"}: ${refused.keyword}: ${refused.reason}`,
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
writeDocument(host, documents.report, "name a path the run can write", () => json(report));
|
|
339
|
+
const options = {
|
|
340
|
+
readSource: (path: string) => host.readFile(joinPath(targetRoot, path)),
|
|
341
|
+
template,
|
|
342
|
+
};
|
|
343
|
+
for (const format of documents.formats) {
|
|
344
|
+
const rendering = RENDERINGS.get(format);
|
|
345
|
+
if (rendering !== undefined) {
|
|
346
|
+
writeDocument(
|
|
347
|
+
host,
|
|
348
|
+
documents.report + rendering.suffix,
|
|
349
|
+
"name a path the run can write, or correct the template that renders it",
|
|
350
|
+
() => rendering.render(report, options),
|
|
379
351
|
);
|
|
380
352
|
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
|
|
353
|
+
}
|
|
354
|
+
const baselinePath = documents.baseline;
|
|
355
|
+
if (baselinePath !== undefined) {
|
|
356
|
+
writeDocument(
|
|
357
|
+
host,
|
|
358
|
+
baselinePath,
|
|
359
|
+
"name a path the run can write, or repair the rows of the baseline the run would keep",
|
|
360
|
+
() => {
|
|
361
|
+
const kept =
|
|
362
|
+
host.kindOf(baselinePath) === "file" ? otherSchemeRows(host.readFile(baselinePath)) : [];
|
|
363
|
+
return writeBaseline(recorded, analyzerProvenance(version), kept);
|
|
364
|
+
},
|
|
365
|
+
);
|
|
384
366
|
}
|
|
385
367
|
|
|
386
368
|
for (const note of partial) {
|
package/src/verbs/describe.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CONTRACT_VERSION,
|
|
3
3
|
EXIT_CLEAN,
|
|
4
|
-
EXIT_FAILURE,
|
|
5
4
|
EXIT_USAGE,
|
|
6
5
|
SCHEMA_VERSIONS,
|
|
7
6
|
} from "../contract/contract.generated.ts";
|
|
8
7
|
import { CONFORMANCE, type Conformance } from "../conformance.ts";
|
|
9
8
|
import { type Verb } from "./verb.ts";
|
|
10
|
-
import { diagnose, LANGUAGE,
|
|
9
|
+
import { diagnose, LANGUAGE, PRODUCT } from "../product.ts";
|
|
10
|
+
import { ownVersion } from "./version.ts";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* The document `describe` writes: the analyzer, the Contract version it implements, the
|
|
@@ -34,19 +34,12 @@ export function describeDocument(version: string, recorded: Conformance | undefi
|
|
|
34
34
|
return `${JSON.stringify(document, null, 2)}\n`;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/** Writes this analyzer's description to the output stream
|
|
37
|
+
/** Writes this analyzer's description to the output stream. */
|
|
38
38
|
export const describeVerb: Verb = ({ out, err, host, args }) => {
|
|
39
39
|
if (args.length > 0) {
|
|
40
40
|
diagnose(err, `describe takes no argument, got ${JSON.stringify(args[0])}`);
|
|
41
41
|
return EXIT_USAGE;
|
|
42
42
|
}
|
|
43
|
-
|
|
44
|
-
try {
|
|
45
|
-
version = host.analyzerVersion();
|
|
46
|
-
} catch (error: unknown) {
|
|
47
|
-
diagnose(err, `describe: ${message(error)}`);
|
|
48
|
-
return EXIT_FAILURE;
|
|
49
|
-
}
|
|
50
|
-
out.write(describeDocument(version, CONFORMANCE));
|
|
43
|
+
out.write(describeDocument(ownVersion(host), CONFORMANCE));
|
|
51
44
|
return EXIT_CLEAN;
|
|
52
45
|
};
|
package/src/verbs/explain.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { EXIT_CLEAN, EXIT_USAGE } from "../contract/contract.generated.ts";
|
|
|
2
2
|
import { runAnalysis, type RunAnalysis } from "../analysis.ts";
|
|
3
3
|
import { listing } from "../components.ts";
|
|
4
4
|
import type { Cascade } from "../config.ts";
|
|
5
|
-
import { ConfigError } from "../config.ts";
|
|
6
5
|
import type { CompletedFinding } from "../finding.ts";
|
|
7
6
|
import { OUTSIDE, swept as judged } from "../graph.ts";
|
|
8
7
|
import type { InventorySymbol } from "../inventory.ts";
|
|
@@ -14,6 +13,7 @@ import type { Reference } from "../references.ts";
|
|
|
14
13
|
import { resolve } from "../resolve.ts";
|
|
15
14
|
import type { Root } from "../roots.ts";
|
|
16
15
|
import { type Verb, type VerbOptions } from "./verb.ts";
|
|
16
|
+
import { UsageError } from "../usage.ts";
|
|
17
17
|
import { bytewise } from "../text/compare.ts";
|
|
18
18
|
import { diagnose } from "../product.ts";
|
|
19
19
|
|
|
@@ -47,16 +47,12 @@ function namedSymbol(option: (name: string) => string | undefined): string {
|
|
|
47
47
|
});
|
|
48
48
|
const [only, ...more] = named;
|
|
49
49
|
if (only === undefined) {
|
|
50
|
-
throw new
|
|
51
|
-
"malformed",
|
|
52
|
-
"",
|
|
50
|
+
throw new UsageError(
|
|
53
51
|
"explain explains the symbol --why, --why-live or --why-not names, and none was named",
|
|
54
52
|
);
|
|
55
53
|
}
|
|
56
54
|
if (more.length > 0) {
|
|
57
|
-
throw new
|
|
58
|
-
"malformed",
|
|
59
|
-
"",
|
|
55
|
+
throw new UsageError(
|
|
60
56
|
`explain explains one symbol, and ${named.map((one) => `--${one.name}`).join(", ")} each name one`,
|
|
61
57
|
);
|
|
62
58
|
}
|
|
@@ -2,7 +2,7 @@ import { EXIT_CLEAN } from "../contract/contract.generated.ts";
|
|
|
2
2
|
import { runProjects } from "../analysis.ts";
|
|
3
3
|
import { partialNotes } from "../partial.ts";
|
|
4
4
|
import { resolvePath } from "../paths.ts";
|
|
5
|
-
import {
|
|
5
|
+
import { resolvePartial } from "../resolve.ts";
|
|
6
6
|
import { type Verb } from "./verb.ts";
|
|
7
7
|
import { diagnose } from "../product.ts";
|
|
8
8
|
|
|
@@ -14,7 +14,7 @@ import { diagnose } from "../product.ts";
|
|
|
14
14
|
* instead, and one that fails the analysis fails this command the same way.
|
|
15
15
|
*/
|
|
16
16
|
export const printProjectsVerb: Verb = ({ out, err, host, inputs, scope, openClient }) => {
|
|
17
|
-
const config =
|
|
17
|
+
const config = resolvePartial(inputs());
|
|
18
18
|
const scoped = scope();
|
|
19
19
|
const answer = runProjects(openClient(config), host, scoped, config);
|
|
20
20
|
for (const id of answer.configurations) {
|
package/src/verbs/verb.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Inputs, PartialConfig } from "../config.ts";
|
|
2
2
|
import type { Host } from "../host.ts";
|
|
3
3
|
import type { Writer } from "../run.ts";
|
|
4
4
|
import type { Scope } from "../scope.ts";
|
|
@@ -21,7 +21,7 @@ interface Invocation {
|
|
|
21
21
|
* Opens the compiler client for a run under `config`. A client the verb opened is
|
|
22
22
|
* closed if the verb throws.
|
|
23
23
|
*/
|
|
24
|
-
readonly openClient: (config:
|
|
24
|
+
readonly openClient: (config: PartialConfig) => Engine;
|
|
25
25
|
/** The arguments after the verb's name, as written. */
|
|
26
26
|
readonly args: readonly string[];
|
|
27
27
|
/** The last value one of the verb's own options was given, undefined where none was. */
|
package/src/verbs/version.ts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { CONTRACT_VERSION, EXIT_CLEAN } from "../contract/contract.generated.ts";
|
|
2
|
+
import { productFault } from "../failure.ts";
|
|
3
|
+
import type { Host } from "../host.ts";
|
|
2
4
|
import { type Verb } from "./verb.ts";
|
|
3
|
-
import { PRODUCT } from "../product.ts";
|
|
5
|
+
import { message, PRODUCT } from "../product.ts";
|
|
6
|
+
|
|
7
|
+
/** The version the host reports for this analyzer; one it cannot read is a fault of the analyzer. */
|
|
8
|
+
export function ownVersion(host: Host): string {
|
|
9
|
+
try {
|
|
10
|
+
return host.analyzerVersion();
|
|
11
|
+
} catch (error: unknown) {
|
|
12
|
+
throw productFault(`cannot read its own version: ${message(error)}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
4
15
|
|
|
5
16
|
/** Writes the version the host reports and the Contract version this analyzer implements. */
|
|
6
17
|
export const versionVerb: Verb = ({ out, host }) => {
|
|
7
|
-
out.write(`${PRODUCT} ${host
|
|
18
|
+
out.write(`${PRODUCT} ${ownVersion(host)}\ncontract ${CONTRACT_VERSION}\n`);
|
|
8
19
|
return EXIT_CLEAN;
|
|
9
20
|
};
|
package/dist/src/base-url.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { resolvePath } from "./paths.js";
|
|
2
|
-
/**
|
|
3
|
-
* The `paths` one configuration's resolved options map under the compilers before version
|
|
4
|
-
* 7, each target absolute: a target is read against the effective `baseUrl`, and a specifier
|
|
5
|
-
* no other pattern maps is looked up below it. Undefined where no `baseUrl` is set, so the
|
|
6
|
-
* options' own `paths` stand. Version 7 reads no `baseUrl`:
|
|
7
|
-
* https://github.com/microsoft/TypeScript/issues/62207
|
|
8
|
-
*/
|
|
9
|
-
export function baseUrlPaths(options) {
|
|
10
|
-
const baseUrl = Reflect.get(options, "baseUrl");
|
|
11
|
-
if (typeof baseUrl !== "string") {
|
|
12
|
-
return undefined;
|
|
13
|
-
}
|
|
14
|
-
return Object.fromEntries(Object.entries({ "*": ["*"], ...options.paths }).map(([key, targets]) => [
|
|
15
|
-
key,
|
|
16
|
-
targets.map((target) => resolvePath(baseUrl, target)),
|
|
17
|
-
]));
|
|
18
|
-
}
|