@cplieger/deadset-ts 8.0.0-dev.8 → 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.
Files changed (75) hide show
  1. package/dist/src/analysis.js +84 -288
  2. package/dist/src/configuration-files.js +2 -9
  3. package/dist/src/consumers.js +1 -28
  4. package/dist/src/contract/contract.generated.js +20 -10
  5. package/dist/src/conventions.js +3 -8
  6. package/dist/src/failure.js +36 -0
  7. package/dist/src/fs/document.js +21 -28
  8. package/dist/src/fs/manifest.js +7 -0
  9. package/dist/src/load/classify.js +236 -0
  10. package/dist/src/load/diagnostics.js +79 -0
  11. package/dist/src/load/discover.js +230 -0
  12. package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
  13. package/dist/src/load/expected-module.js +136 -0
  14. package/dist/src/load/load.js +22 -0
  15. package/dist/src/load/module-lookup.js +37 -0
  16. package/dist/src/load/plan.js +155 -0
  17. package/dist/src/{test-globals.js → load/test-globals.js} +51 -56
  18. package/dist/src/load/test-joins.js +82 -0
  19. package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +3 -3
  20. package/dist/src/{workspace-programs.js → load/workspace-programs.js} +37 -40
  21. package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +21 -59
  22. package/dist/src/{workspace-run.js → load/workspace-run.js} +4 -3
  23. package/dist/src/{workspace.js → load/workspace.js} +21 -17
  24. package/dist/src/outside-tests.js +1 -1
  25. package/dist/src/run.js +20 -26
  26. package/dist/src/scope.js +11 -19
  27. package/dist/src/session.js +22 -28
  28. package/dist/src/source-files.js +1 -1
  29. package/dist/src/type-errors.js +11 -216
  30. package/dist/src/verbs/analyze.js +90 -109
  31. package/dist/src/verbs/describe.js +5 -12
  32. package/dist/src/verbs/version.js +12 -2
  33. package/jsr.json +1 -1
  34. package/package.json +1 -1
  35. package/src/analysis.ts +68 -437
  36. package/src/configuration-files.ts +8 -10
  37. package/src/consumers.ts +2 -53
  38. package/src/contract/contract.generated.ts +20 -10
  39. package/src/conventions.ts +6 -12
  40. package/src/failure.ts +63 -0
  41. package/src/fs/document.ts +23 -27
  42. package/src/fs/manifest.ts +8 -0
  43. package/src/load/classify.ts +352 -0
  44. package/src/load/diagnostics.ts +141 -0
  45. package/src/load/discover.ts +378 -0
  46. package/src/{emit-map.ts → load/emit-map.ts} +1 -1
  47. package/src/load/expected-module.ts +188 -0
  48. package/src/load/load.ts +61 -0
  49. package/src/load/module-lookup.ts +50 -0
  50. package/src/load/plan.ts +258 -0
  51. package/src/{test-globals.ts → load/test-globals.ts} +59 -65
  52. package/src/load/test-joins.ts +98 -0
  53. package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +4 -4
  54. package/src/{workspace-programs.ts → load/workspace-programs.ts} +59 -53
  55. package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +31 -78
  56. package/src/{workspace-run.ts → load/workspace-run.ts} +14 -11
  57. package/src/{workspace.ts → load/workspace.ts} +28 -21
  58. package/src/outside-tests.ts +1 -1
  59. package/src/partial.ts +1 -1
  60. package/src/run.ts +26 -25
  61. package/src/scope.ts +15 -21
  62. package/src/session.ts +52 -53
  63. package/src/source-files.ts +1 -1
  64. package/src/type-errors.ts +18 -288
  65. package/src/verbs/analyze.ts +129 -136
  66. package/src/verbs/describe.ts +4 -11
  67. package/src/verbs/version.ts +13 -2
  68. package/dist/src/base-url.js +0 -18
  69. package/dist/src/discover.js +0 -374
  70. package/dist/src/setup-failure.js +0 -19
  71. package/dist/src/test-joins.js +0 -101
  72. package/src/base-url.ts +0 -24
  73. package/src/discover.ts +0 -548
  74. package/src/setup-failure.ts +0 -37
  75. package/src/test-joins.ts +0 -118
@@ -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
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 { NotBuilt } from "../discover.ts";
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";
@@ -40,7 +32,9 @@ import type { UnseenLoad } from "../unseen-loads.ts";
40
32
  import { type Verb, type VerbOptions } from "./verb.ts";
41
33
  import { UsageError } from "../usage.ts";
42
34
  import { bytewise } from "../text/compare.ts";
35
+ import { productFault, failureExit } from "../failure.ts";
43
36
  import { diagnose, message } from "../product.ts";
37
+ import { ownVersion } from "./version.ts";
44
38
 
45
39
  /** The options `analyze` takes beside the ones every verb takes. */
46
40
  export const ANALYZE_OPTIONS: VerbOptions = {
@@ -52,14 +46,6 @@ export const ANALYZE_OPTIONS: VerbOptions = {
52
46
  const EXIT_CODE_ON = "on";
53
47
  const EXIT_CODE_OFF = "off";
54
48
 
55
- /** A path of the run no report can name, which ends the run with the failure code. */
56
- class ReportPathError extends Error {
57
- constructor(message: string) {
58
- super(message);
59
- this.name = "ReportPathError";
60
- }
61
- }
62
-
63
49
  /** The formats this analyzer renders, for a message that names them. */
64
50
  function formatNames(): string {
65
51
  return [...RENDERINGS.keys()].sort(bytewise).join(", ");
@@ -94,7 +80,8 @@ function runRelative(invoked: string, path: string): string {
94
80
  }
95
81
  const below = relativePath(invoked, path);
96
82
  if (below === undefined) {
97
- throw new ReportPathError(
83
+ throw failureExit(
84
+ "unreportable-path",
98
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`,
99
86
  );
100
87
  }
@@ -146,8 +133,9 @@ function notBuiltOf(dropped: readonly NotBuilt[], targetRoot: string): readonly
146
133
  return dropped.map((one) => {
147
134
  const project = relativePath(targetRoot, one.configFile);
148
135
  if (project === undefined) {
149
- throw new ReportPathError(
150
- `the project ${one.id} would be opened from ${one.configFile}, which is not below the target ${targetRoot}, so no report can name it`,
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`,
151
139
  );
152
140
  }
153
141
  return { id: one.id, project, error: one.error };
@@ -162,14 +150,33 @@ function configurationsOf(
162
150
  return projects.map((one) => {
163
151
  const project = relativePath(targetRoot, one.configFile);
164
152
  if (project === undefined) {
165
- throw new ReportPathError(
166
- `the project ${one.id} is opened from ${one.configFile}, which is not below the target ${targetRoot}, so no report can name it`,
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`,
167
156
  );
168
157
  }
169
158
  return { id: one.id, project };
170
159
  });
171
160
  }
172
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
+
173
180
  /** A count with its noun, so a message reads for one record as well as for several. */
174
181
  function counted(n: number, noun: string): string {
175
182
  return `${String(n)} ${noun}${n === 1 ? "" : "s"}`;
@@ -250,126 +257,112 @@ export const analyzeVerb: Verb = ({ err, host, inputs, scope, openClient, option
250
257
  const scoped = scope();
251
258
  const targetRoot = scoped.target.path;
252
259
 
253
- let version: string;
254
- try {
255
- version = host.analyzerVersion();
256
- } catch (error: unknown) {
257
- diagnose(err, message(error));
258
- return EXIT_FAILURE;
259
- }
260
+ const version = ownVersion(host);
260
261
 
261
- let report: Report;
262
262
  let recorded: readonly Recorded[];
263
- let verdict: number;
264
- let partial: readonly string[];
265
- let warnings: readonly ComponentWarning[];
266
- let unread: readonly string[];
267
- let unfound: readonly string[];
268
- try {
269
- const root = runRelative(invoked, targetRoot);
270
- const mode = { production: true };
271
- let analysis: RunAnalysis;
272
- if (documents.baseline === undefined) {
273
- analysis = runAnalysis(openClient(config), host, scoped, config, provenance, mode);
274
- recorded = [];
275
- } else {
276
- const reason = rowReason(analyzerProvenance(version));
277
- const write = runBaselineWrite(
278
- openClient(config),
279
- host,
280
- scoped,
281
- config,
282
- provenance,
283
- mode,
284
- reason,
285
- );
286
- analysis = write.first;
287
- recorded = write.rows;
288
- }
289
- const { result, run } = analysis;
290
- warnings = run.componentWarnings;
291
- unread = run.componentsUnread.map(
292
- ({ configFile, reason }) =>
293
- `${relativePath(resolvePath(invoked, targetRoot), configFile) ?? configFile}: its component files are not read: ${reason}`,
294
- );
295
- unfound = run.librariesUnfound.map(
296
- ({ configFile, error }) =>
297
- `${relativePath(resolvePath(invoked, targetRoot), configFile) ?? configFile}: ${error}: the configuration is analyzed without that library`,
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,
298
279
  );
299
- partial = partialNotes({
300
- notBuilt: run.notBuilt,
301
- unanswered: run.unanswered,
302
- targetRoot: resolvePath(invoked, targetRoot),
303
- });
304
- const built = buildReport({
305
- contractVersion: CONTRACT_VERSION,
306
- version,
307
- conformance: CONFORMANCE,
308
- declaredGaps: DECLARED_GAPS,
309
- target: {
310
- kind: kindOf(config.target.kind),
311
- root,
312
- identity: moduleIdentity(host, scoped.target),
313
- },
314
- configurations: configurationsOf(run.projects, targetRoot),
315
- notBuilt: notBuiltOf(run.notBuilt, targetRoot),
316
- loaded: run.consumers.map((consumer) => ({
317
- id: consumer.id,
318
- path: runRelative(invoked, consumer.path),
319
- })),
320
- unavailable: [],
321
- result,
322
- testFileRules: run.testFileRules,
323
- typeErrorSkips: run.typeErrorSkips,
324
- unanswered: unansweredCounts(analysis),
325
- conventionsApplied: run.conventionsApplied,
326
- listsComponents: config.reporters.cascade === "full",
327
- notes: [
328
- ...unmappedNotes(run.unmappedEntries, resolvePath(invoked, targetRoot)),
329
- ...unseenLoadNotes(run.unseenLoads),
330
- ],
331
- });
332
- report = capFindings(sortFindings(built, config.reporters.sort), config.reporters.max_findings);
333
- verdict = verdictOf(result, config);
334
- } catch (error: unknown) {
335
- if (!(error instanceof ReportPathError)) {
336
- throw error;
337
- }
338
- diagnose(err, error.message);
339
- return EXIT_FAILURE;
280
+ analysis = write.first;
281
+ recorded = write.rows;
340
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);
341
331
 
342
- try {
343
- const [refused] = reportRefusals(report);
344
- if (refused !== undefined) {
345
- throw new Error(
346
- `the report breaks the report schema at ${refused.pointer || "the document"}: ${refused.keyword}: ${refused.reason}`,
347
- );
348
- }
349
- host.writeDocument(documents.report, json(report));
350
- const options = {
351
- readSource: (path: string) => host.readFile(joinPath(targetRoot, path)),
352
- template,
353
- };
354
- for (const format of documents.formats) {
355
- const rendering = RENDERINGS.get(format);
356
- if (rendering !== undefined) {
357
- host.writeDocument(documents.report + rendering.suffix, rendering.render(report, options));
358
- }
359
- }
360
- if (documents.baseline !== undefined) {
361
- const kept =
362
- host.kindOf(documents.baseline) === "file"
363
- ? otherSchemeRows(host.readFile(documents.baseline))
364
- : [];
365
- host.writeDocument(
366
- documents.baseline,
367
- 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),
368
351
  );
369
352
  }
370
- } catch (error: unknown) {
371
- diagnose(err, message(error));
372
- return EXIT_FAILURE;
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
+ );
373
366
  }
374
367
 
375
368
  for (const note of partial) {
@@ -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, message, PRODUCT } from "../product.ts";
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, and nothing to the error 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
- let version: string;
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
  };
@@ -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.analyzerVersion()}\ncontract ${CONTRACT_VERSION}\n`);
18
+ out.write(`${PRODUCT} ${ownVersion(host)}\ncontract ${CONTRACT_VERSION}\n`);
8
19
  return EXIT_CLEAN;
9
20
  };
@@ -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
- }