@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.
Files changed (137) hide show
  1. package/dist/bin/node-host.js +21 -3
  2. package/dist/src/analysis.js +94 -299
  3. package/dist/src/config.js +6 -83
  4. package/dist/src/configuration-files.js +2 -9
  5. package/dist/src/configured-declarations.js +15 -20
  6. package/dist/src/consumers.js +3 -30
  7. package/dist/src/contract/config.generated.js +544 -0
  8. package/dist/src/contract/contract.generated.js +20 -10
  9. package/dist/src/contract/report.generated.js +3 -2
  10. package/dist/src/conventions.js +3 -8
  11. package/dist/src/edges.js +36 -112
  12. package/dist/src/exempt-classes.js +0 -2
  13. package/dist/src/exempt.js +1 -1
  14. package/dist/src/failure.js +36 -0
  15. package/dist/src/findings/completion.js +5 -4
  16. package/dist/src/findings/emitters.js +1 -1
  17. package/dist/src/findings/intra-function.js +1 -1
  18. package/dist/src/findings/non-code-artifacts.js +2 -2
  19. package/dist/src/findings/unused-declarations.js +1 -1
  20. package/dist/src/findings/visibility-narrowing.js +2 -2
  21. package/dist/src/findings-pass.js +1 -1
  22. package/dist/src/framework-lifecycle.js +7 -7
  23. package/dist/src/fs/document.js +52 -0
  24. package/dist/src/fs/manifest.js +7 -0
  25. package/dist/src/injection-container.js +1 -1
  26. package/dist/src/json-document.js +194 -67
  27. package/dist/src/kinds.js +0 -7
  28. package/dist/src/load/classify.js +236 -0
  29. package/dist/src/load/diagnostics.js +79 -0
  30. package/dist/src/load/discover.js +230 -0
  31. package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
  32. package/dist/src/load/expected-module.js +136 -0
  33. package/dist/src/load/load.js +22 -0
  34. package/dist/src/load/module-lookup.js +37 -0
  35. package/dist/src/load/plan.js +155 -0
  36. package/dist/src/{test-globals.js → load/test-globals.js} +51 -56
  37. package/dist/src/load/test-joins.js +82 -0
  38. package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +3 -3
  39. package/dist/src/{workspace-programs.js → load/workspace-programs.js} +37 -40
  40. package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +21 -59
  41. package/dist/src/{workspace-run.js → load/workspace-run.js} +4 -3
  42. package/dist/src/{workspace.js → load/workspace.js} +21 -17
  43. package/dist/src/outside-tests.js +1 -1
  44. package/dist/src/print.js +10 -79
  45. package/dist/src/report-schema.js +3 -224
  46. package/dist/src/resolve.js +245 -715
  47. package/dist/src/run.js +59 -72
  48. package/dist/src/schema.js +320 -344
  49. package/dist/src/scope.js +37 -113
  50. package/dist/src/serialization-contract.js +1 -1
  51. package/dist/src/session.js +22 -28
  52. package/dist/src/source-files.js +1 -1
  53. package/dist/src/suppress-file.js +15 -17
  54. package/dist/src/suppress.js +3 -3
  55. package/dist/src/template-field.js +4 -4
  56. package/dist/src/type-errors.js +11 -216
  57. package/dist/src/usage.js +10 -0
  58. package/dist/src/verbs/analyze.js +100 -122
  59. package/dist/src/verbs/describe.js +5 -12
  60. package/dist/src/verbs/explain.js +3 -3
  61. package/dist/src/verbs/print-projects.js +2 -2
  62. package/dist/src/verbs/version.js +12 -2
  63. package/jsr.json +1 -1
  64. package/package.json +1 -1
  65. package/src/analysis.ts +95 -460
  66. package/src/config.ts +31 -298
  67. package/src/configuration-files.ts +8 -10
  68. package/src/configured-declarations.ts +18 -23
  69. package/src/consumers.ts +6 -57
  70. package/src/contract/config.generated.ts +798 -0
  71. package/src/contract/contract.generated.ts +20 -10
  72. package/src/contract/report.generated.ts +3 -2
  73. package/src/conventions.ts +6 -12
  74. package/src/edges.ts +39 -124
  75. package/src/exempt-classes.ts +0 -5
  76. package/src/exempt.ts +4 -4
  77. package/src/failure.ts +63 -0
  78. package/src/findings/completion.ts +5 -6
  79. package/src/findings/emitter.ts +2 -2
  80. package/src/findings/emitters.ts +1 -1
  81. package/src/findings/intra-function.ts +1 -1
  82. package/src/findings/non-code-artifacts.ts +4 -4
  83. package/src/findings/unused-declarations.ts +1 -1
  84. package/src/findings/visibility-narrowing.ts +2 -2
  85. package/src/findings-pass.ts +3 -3
  86. package/src/framework-lifecycle.ts +7 -7
  87. package/src/fs/document.ts +70 -0
  88. package/src/fs/manifest.ts +8 -0
  89. package/src/host.ts +12 -5
  90. package/src/injection-container.ts +1 -1
  91. package/src/json-document.ts +236 -76
  92. package/src/kinds.ts +0 -8
  93. package/src/load/classify.ts +352 -0
  94. package/src/load/diagnostics.ts +141 -0
  95. package/src/load/discover.ts +378 -0
  96. package/src/{emit-map.ts → load/emit-map.ts} +1 -1
  97. package/src/load/expected-module.ts +188 -0
  98. package/src/load/load.ts +61 -0
  99. package/src/load/module-lookup.ts +50 -0
  100. package/src/load/plan.ts +258 -0
  101. package/src/{test-globals.ts → load/test-globals.ts} +59 -65
  102. package/src/load/test-joins.ts +98 -0
  103. package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +4 -4
  104. package/src/{workspace-programs.ts → load/workspace-programs.ts} +59 -53
  105. package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +31 -78
  106. package/src/{workspace-run.ts → load/workspace-run.ts} +14 -11
  107. package/src/{workspace.ts → load/workspace.ts} +28 -21
  108. package/src/outside-tests.ts +1 -1
  109. package/src/partial.ts +1 -1
  110. package/src/print.ts +11 -92
  111. package/src/report-schema.ts +3 -299
  112. package/src/resolve.ts +281 -1105
  113. package/src/run.ts +68 -80
  114. package/src/schema.ts +391 -389
  115. package/src/scope.ts +45 -131
  116. package/src/serialization-contract.ts +1 -1
  117. package/src/session.ts +52 -53
  118. package/src/source-files.ts +1 -1
  119. package/src/suppress-file.ts +15 -20
  120. package/src/suppress.ts +3 -3
  121. package/src/template-field.ts +10 -7
  122. package/src/type-errors.ts +18 -288
  123. package/src/usage.ts +10 -0
  124. package/src/verbs/analyze.ts +139 -157
  125. package/src/verbs/describe.ts +4 -11
  126. package/src/verbs/explain.ts +3 -7
  127. package/src/verbs/print-projects.ts +2 -2
  128. package/src/verbs/verb.ts +2 -2
  129. package/src/verbs/version.ts +13 -2
  130. package/dist/src/base-url.js +0 -18
  131. package/dist/src/discover.js +0 -374
  132. package/dist/src/setup-failure.js +0 -19
  133. package/dist/src/test-joins.js +0 -101
  134. package/src/base-url.ts +0 -24
  135. package/src/discover.ts +0 -548
  136. package/src/setup-failure.ts +0 -37
  137. package/src/test-joins.ts +0 -118
package/src/run.ts CHANGED
@@ -1,10 +1,13 @@
1
+ import { CONFIG_MAX_BYTES, SETTINGS } from "./contract/config.generated.ts";
1
2
  import { EXIT_FAILURE, EXIT_USAGE } from "./contract/contract.generated.ts";
2
- import { ConfigError, REPOSITORY_DOCUMENT, type Inputs } from "./config.ts";
3
- import { DiscoveryError, renderDiagnostic } from "./discover.ts";
3
+ import { REPOSITORY_DOCUMENT, type Document, type Inputs } from "./config.ts";
4
+ import { readTargetDocument } from "./fs/document.ts";
4
5
  import type { Host } from "./host.ts";
5
6
  import { joinPath, resolvePath } from "./paths.ts";
6
- import { readScope, ScopeError, scopeForDir, type Scope } from "./scope.ts";
7
- import { SetupError, setupLine } from "./setup-failure.ts";
7
+ import { renderDiagnostic } from "./load/diagnostics.ts";
8
+ import { FailureExit, failureLines, productFault } from "./failure.ts";
9
+ import { readScope, scopeForDir, type Scope } from "./scope.ts";
10
+ import { UsageError } from "./usage.ts";
8
11
  import { openEngine, type Engine, type EngineOptions } from "./session.ts";
9
12
  import { printConfigVerb } from "./verbs/print-config.ts";
10
13
  import { printProjectsVerb } from "./verbs/print-projects.ts";
@@ -48,33 +51,16 @@ const USAGE = `usage: ${PRODUCT} <verb> [options]\nverbs: ${VERBS.map((verb) =>
48
51
  const SOURCE_EDIT_TOKENS = ["fix", "edit", "delete", "rewrite"] as const;
49
52
 
50
53
  /**
51
- * Maps an option name to the setting of the Contract's configuration schema it supplies,
52
- * so one entry registers the option, supplies the document resolution reads, and gives
53
- * the spelling the setting's provenance names. The name is this command's vocabulary and
54
- * is never derived from the path: a setting reaches the command line only by an entry,
55
- * and a path names a key the schema declares.
54
+ * Maps an option name to the setting of the Contract's configuration schema it supplies:
55
+ * the options contract/settings.json names for a setting this analyzer reads.
56
56
  */
57
- export const SETTING_OPTIONS: ReadonlyMap<string, string> = new Map([
58
- ["min-confidence", "analysis.min_confidence"],
59
- ["sort", "reporters.sort"],
60
- ["cascade", "reporters.cascade"],
61
- ["max-findings", "reporters.max_findings"],
62
- ["fail-on", "reporters.fail_on"],
63
- ]);
57
+ export const SETTING_OPTIONS: ReadonlyMap<string, string> = new Map(
58
+ SETTINGS.flatMap((row) => (row.option === undefined ? [] : [[row.option, row.key] as const])),
59
+ );
64
60
 
65
61
  /** The options that take a value and are not settings. */
66
62
  const PLAIN_OPTIONS: ReadonlySet<string> = new Set(["target", "config", "central", "scope"]);
67
63
 
68
- /**
69
- * The setting options whose value is an integer rather than a string. A value
70
- * written other than as digits is passed on as the string it is, so resolution
71
- * refuses it by its type rather than reading `1e2` as 100.
72
- */
73
- const INTEGER_OPTIONS: ReadonlySet<string> = new Set(["max-findings"]);
74
-
75
- /** An integer as an option writes it: an optional minus sign and digits alone. */
76
- const INTEGER_TEXT = /^-?[0-9]+$/u;
77
-
78
64
  /** One invocation's options, by name: the last value of each, and every value of a repeatable one. */
79
65
  interface Options {
80
66
  readonly named: ReadonlyMap<string, string>;
@@ -109,7 +95,7 @@ function readOptions(args: readonly string[], own: VerbOptions): Options {
109
95
  const repeated = new Map<string, string[]>();
110
96
  for (const arg of args) {
111
97
  if (!arg.startsWith("-")) {
112
- throw new ConfigError("malformed", "", `unexpected argument ${JSON.stringify(arg)}`);
98
+ throw new UsageError(`unexpected argument ${JSON.stringify(arg)}`);
113
99
  }
114
100
  const [spelled, ...rest] = arg.split("=");
115
101
  const name = (spelled ?? "").replace(/^-+/u, "");
@@ -119,14 +105,10 @@ function readOptions(args: readonly string[], own: VerbOptions): Options {
119
105
  !own.plain.includes(name) &&
120
106
  !own.repeatable.includes(name)
121
107
  ) {
122
- throw new ConfigError("malformed", "", `unknown option ${JSON.stringify(spelled ?? arg)}`);
108
+ throw new UsageError(`unknown option ${JSON.stringify(spelled ?? arg)}`);
123
109
  }
124
110
  if (rest.length === 0) {
125
- throw new ConfigError(
126
- "malformed",
127
- "",
128
- `option ${JSON.stringify(spelled ?? arg)} takes a value`,
129
- );
111
+ throw new UsageError(`option ${JSON.stringify(spelled ?? arg)} takes a value`);
130
112
  }
131
113
  const value = rest.join("=");
132
114
  named.set(name, value);
@@ -135,47 +117,56 @@ function readOptions(args: readonly string[], own: VerbOptions): Options {
135
117
  return { named, repeated };
136
118
  }
137
119
 
138
- function readFileOr(host: Host, path: string, orUndefined: boolean): string | undefined {
120
+ /** The document an option names. */
121
+ function documentAt(host: Host, label: string): Document {
139
122
  try {
140
- return host.readFile(resolvePath(host.workingDirectory(), path));
123
+ const path = resolvePath(host.workingDirectory(), label);
124
+ // A file past the bound reads as one byte more than it, which resolution refuses.
125
+ return { text: host.readFile(path, CONFIG_MAX_BYTES + 1), label };
141
126
  } catch (error: unknown) {
142
- if (orUndefined) {
143
- return undefined;
144
- }
145
- throw new ConfigError("malformed", "", `${path}: ${message(error)}`);
127
+ throw new UsageError(`${label}: ${message(error)}`);
146
128
  }
147
129
  }
148
130
 
149
131
  /**
150
- * The configuration documents one invocation reads: the repository configuration
151
- * at the target root or the path an option named, the central configuration, and
152
- * the settings the options supply as one flag document.
132
+ * The repository configuration at the target root, none where it is absent. One that
133
+ * cannot be read is a configuration document that cannot be, refused with the usage code.
134
+ */
135
+ function repositoryAt(host: Host, target: string): Document | undefined {
136
+ const label = joinPath(target, REPOSITORY_DOCUMENT);
137
+ const root = resolvePath(host.workingDirectory(), target);
138
+ const text = readTargetDocument(
139
+ host,
140
+ root,
141
+ REPOSITORY_DOCUMENT,
142
+ label,
143
+ (named, reason) => new UsageError(`${named}: ${reason}`),
144
+ );
145
+ return text === undefined ? undefined : { text, label };
146
+ }
147
+
148
+ /**
149
+ * The configuration sources one invocation carries: the repository configuration at
150
+ * the target root or the path an option named, the central configuration, and the
151
+ * text each setting option was given.
153
152
  */
154
153
  function inputsOf(host: Host, options: Options): Inputs {
155
- const target = options.named.get("target") ?? ".";
156
154
  const named = options.named.get("config");
157
- const repositoryLabel = named ?? joinPath(target, REPOSITORY_DOCUMENT);
158
- const repository = readFileOr(host, repositoryLabel, named === undefined);
155
+ const target = options.named.get("target") ?? ".";
156
+ const repository = named === undefined ? repositoryAt(host, target) : documentAt(host, named);
159
157
  const centralLabel = options.named.get("central");
160
- const central = centralLabel === undefined ? undefined : readFileOr(host, centralLabel, false);
161
-
162
- const flat: Record<string, unknown> = {};
163
- const flagLabels = new Map<string, string>();
164
- for (const [name, path] of SETTING_OPTIONS) {
158
+ const central = centralLabel === undefined ? undefined : documentAt(host, centralLabel);
159
+ const flags = new Map<string, string>();
160
+ for (const [name, key] of SETTING_OPTIONS) {
165
161
  const value = options.named.get(name);
166
- if (value === undefined) {
167
- continue;
162
+ if (value !== undefined) {
163
+ flags.set(key, value);
168
164
  }
169
- flat[path] = INTEGER_OPTIONS.has(name) && INTEGER_TEXT.test(value) ? Number(value) : value;
170
- flagLabels.set(path, `--${name}`);
171
165
  }
172
- const flags = Object.keys(flat).length === 0 ? undefined : JSON.stringify(flat);
173
-
174
166
  return {
175
- ...(flags === undefined ? {} : { flags }),
176
- ...(repository === undefined ? {} : { repository, repositoryLabel }),
177
- ...(central === undefined || centralLabel === undefined ? {} : { central, centralLabel }),
178
- flagLabels,
167
+ flags,
168
+ ...(repository === undefined ? {} : { repository }),
169
+ ...(central === undefined ? {} : { central }),
179
170
  };
180
171
  }
181
172
 
@@ -193,9 +184,7 @@ function scopeOf(host: Host, options: Options): Scope {
193
184
  * the exit code: 0 for a completed verb, 1 for a finding that fails the run, 2 for a
194
185
  * usage error, a requested source edit or a refused configuration, 3 for a failure that
195
186
  * produced no answer. It writes nothing outside `out` and `err` and never exits the
196
- * process. `host` is the platform the run reads, the version it reports included, and
197
- * `openClient` opens the compiler client; a caller that watches what a run reads
198
- * supplies its own of either.
187
+ * process. A caller that watches what a run reads supplies its own `host` or `openClient`.
199
188
  */
200
189
  export function run(
201
190
  args: readonly string[],
@@ -255,7 +244,7 @@ function invoke(
255
244
  openClient: (config) => {
256
245
  opened = openClient({
257
246
  collectTiming: false,
258
- components: { extensions: config.ts.componentExtensions, host: streams.host },
247
+ components: { extensions: config.ts.component_extensions, host: streams.host },
259
248
  });
260
249
  return opened;
261
250
  },
@@ -266,29 +255,28 @@ function invoke(
266
255
  }
267
256
  }
268
257
 
269
- /** Turns one refusal into the exit code the Contract's table gives it. */
258
+ /**
259
+ * Turns one refusal into the exit code the Contract's table gives it. An error no class names
260
+ * is a fault of this analyzer, printed with its stack for the maintainers it asks the user to
261
+ * report it to.
262
+ */
270
263
  function refuse(error: unknown, err: Writer): number {
271
- if (error instanceof ConfigError) {
264
+ if (error instanceof UsageError) {
272
265
  diagnose(err, error.message);
273
266
  err.write(USAGE);
274
267
  return EXIT_USAGE;
275
268
  }
276
- if (error instanceof DiscoveryError) {
277
- for (const diagnostic of error.diagnostics) {
278
- err.write(`${renderDiagnostic(diagnostic)}\n`);
269
+ if (!(error instanceof FailureExit)) {
270
+ if (error instanceof Error && error.stack !== undefined) {
271
+ err.write(`${error.stack}\n`);
279
272
  }
280
- diagnose(err, error.message);
281
- return EXIT_FAILURE;
273
+ return refuse(productFault(`meets an error none of its classes names: ${message(error)}`), err);
282
274
  }
283
- if (error instanceof SetupError) {
284
- for (const failure of error.failures) {
285
- err.write(`${setupLine(failure)}\n`);
286
- }
287
- return EXIT_FAILURE;
275
+ for (const diagnostic of error.diagnostics) {
276
+ err.write(`${renderDiagnostic(diagnostic)}\n`);
288
277
  }
289
- if (error instanceof ScopeError) {
290
- diagnose(err, error.message);
291
- return EXIT_FAILURE;
278
+ for (const line of failureLines(error.failures)) {
279
+ err.write(`${line}\n`);
292
280
  }
293
- throw error;
281
+ return EXIT_FAILURE;
294
282
  }