@cplieger/deadset-ts 3.5.0 → 3.6.0

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 (43) hide show
  1. package/dist/src/analysis.js +70 -14
  2. package/dist/src/components.js +5 -1
  3. package/dist/src/dependencies.js +493 -0
  4. package/dist/src/deprecation.js +83 -0
  5. package/dist/src/discover.js +1 -1
  6. package/dist/src/enum-group.js +217 -0
  7. package/dist/src/file-facts.js +101 -0
  8. package/dist/src/findings/boundary.js +1 -0
  9. package/dist/src/findings/completion.js +99 -0
  10. package/dist/src/findings/dependencies-and-module-machinery.js +65 -1
  11. package/dist/src/findings/emitters.js +8 -0
  12. package/dist/src/findings/interfaces.js +95 -2
  13. package/dist/src/findings/non-code-artifacts.js +97 -1
  14. package/dist/src/findings/reads-and-writes.js +199 -2
  15. package/dist/src/findings/unused-declarations.js +182 -1
  16. package/dist/src/findings/visibility-narrowing.js +239 -2
  17. package/dist/src/implementations.js +210 -0
  18. package/dist/src/inventory.js +2 -2
  19. package/dist/src/resolve.js +1 -1
  20. package/dist/src/stores.js +32 -0
  21. package/jsr.json +1 -1
  22. package/package.json +1 -1
  23. package/src/analysis.ts +113 -16
  24. package/src/components.ts +6 -1
  25. package/src/dependencies.ts +617 -0
  26. package/src/deprecation.ts +101 -0
  27. package/src/discover.ts +1 -1
  28. package/src/enum-group.ts +265 -0
  29. package/src/file-facts.ts +143 -0
  30. package/src/findings/boundary.ts +39 -0
  31. package/src/findings/completion.ts +151 -0
  32. package/src/findings/dependencies-and-module-machinery.ts +79 -1
  33. package/src/findings/emitter.ts +21 -1
  34. package/src/findings/emitters.ts +13 -1
  35. package/src/findings/interfaces.ts +149 -3
  36. package/src/findings/non-code-artifacts.ts +124 -2
  37. package/src/findings/reads-and-writes.ts +310 -3
  38. package/src/findings/unused-declarations.ts +223 -2
  39. package/src/findings/visibility-narrowing.ts +324 -3
  40. package/src/implementations.ts +286 -0
  41. package/src/inventory.ts +2 -2
  42. package/src/resolve.ts +1 -1
  43. package/src/stores.ts +77 -0
package/src/analysis.ts CHANGED
@@ -11,7 +11,10 @@
11
11
  import type { Diagnostic } from "@typescript/native/unstable/sync";
12
12
  import type { Config, Provenance } from "./config.ts";
13
13
  import { decorator } from "./decorator.ts";
14
+ import { dependenciesOf, projectNeeds } from "./dependencies.ts";
15
+ import { deprecatedDeclarations } from "./deprecation.ts";
14
16
  import { diagnosticErrors, discoverProjects, DiscoveryError } from "./discover.ts";
17
+ import { enumGroup } from "./enum-group.ts";
15
18
  import {
16
19
  computeExemptions,
17
20
  disabledClasses,
@@ -21,9 +24,12 @@ import {
21
24
  type Detectors,
22
25
  } from "./exempt.ts";
23
26
  import type { TSExemptionClass } from "./exempt-classes.ts";
27
+ import { heldByInclusion, sourceTree } from "./file-facts.ts";
24
28
  import type { Finding } from "./finding.ts";
29
+ import type { EmitterInput } from "./findings/emitter.ts";
25
30
  import { frameworkLifecycle } from "./framework-lifecycle.ts";
26
31
  import type { Host } from "./host.ts";
32
+ import { implementations, mergeImplementations } from "./implementations.ts";
27
33
  import { injectionContainer } from "./injection-container.ts";
28
34
  import { interfaceSatisfaction } from "./interface-satisfaction.ts";
29
35
  import { inventory, type Inventory } from "./inventory.ts";
@@ -37,6 +43,7 @@ import { roots, unmatchedEverywhere, unmatchedRoots, type RootKind, type Roots }
37
43
  import type { Scope } from "./scope.ts";
38
44
  import { serializationContract } from "./serialization-contract.ts";
39
45
  import { diagnosticsOf, runSession, type Engine, type ProjectView } from "./session.ts";
46
+ import { accessorsOf, storesOf } from "./stores.ts";
40
47
  import type { Exemption, SweepInput } from "./sweep.ts";
41
48
  import { readTemplates, templateField } from "./template-field.ts";
42
49
 
@@ -222,6 +229,7 @@ export interface RunSweep {
222
229
  /** The exemption classes this analyzer detects, each by its detector; any other retains nothing. */
223
230
  const DETECTORS: Detectors = new Map<TSExemptionClass, Detector>([
224
231
  ["interface-satisfaction", interfaceSatisfaction],
232
+ ["enum-group", enumGroup],
225
233
  ["template-field", templateField],
226
234
  ["reflective-lookup", reflectiveLookup],
227
235
  ["decorator", decorator],
@@ -230,31 +238,40 @@ const DETECTORS: Detectors = new Map<TSExemptionClass, Detector>([
230
238
  ["serialization-contract", serializationContract],
231
239
  ]);
232
240
 
241
+ /** What the sweep of one run reads of each project, beside what a caller's stage adds. */
242
+ interface SweptProject<Extra> {
243
+ readonly configured: Configured;
244
+ readonly exempt: readonly Exemption[];
245
+ readonly extra: Extra;
246
+ }
247
+
248
+ /** The run's sweep, the exemption records it was swept with, and each project's extra answer. */
249
+ interface SweptRun<Extra> {
250
+ readonly swept: RunSweep;
251
+ readonly exempt: readonly Exemption[];
252
+ readonly projects: readonly SweptProject<Extra>[];
253
+ }
254
+
233
255
  /**
234
- * Sweeps the run the scope and the configuration describe. Each project's references
235
- * and exemptions are read against its own inventory while its view is open; the
236
- * projects then merge into one matrix in the run's order, which is swept under the
237
- * caller's marks and mode with the exemptions every project found. A record found in
238
- * one configuration holds in every configuration that holds its declaration, because
239
- * an exemption is evidence of a use the analysis cannot see and a use in one
240
- * configuration is a use. A project that carries an error ends the run before any
241
- * project is swept.
256
+ * Sweeps the run, reading each project's references and exemptions, and `extra`'s
257
+ * answer, while the project's view is open.
242
258
  */
243
- export function runSweep(
259
+ function sweepRun<Extra>(
244
260
  engine: Engine,
245
261
  host: Host,
246
262
  scope: Scope,
247
263
  config: Config,
248
264
  input: Pick<SweepInput, "marked" | "mode">,
249
- detectors: Detectors = DETECTORS,
250
- ): RunSweep {
265
+ detectors: Detectors,
266
+ extra: <Brand>(project: ProjectView<Brand>, read: ProjectRead) => Extra,
267
+ ): SweptRun<Extra> {
251
268
  const targetRoot = scope.target.path;
252
269
  const disabled = disabledClasses(config);
253
270
  const templates = disabled.has("template-field")
254
271
  ? { delimiters: config.analysis.templateDelimiters, files: [] }
255
272
  : readTemplates(host, targetRoot, config.analysis);
256
273
  const consumers = scope.consumers.map((consumer) => consumer.path);
257
- const read = readProjects(engine, host, scope, config, (project, projectRead) => {
274
+ const projects = readProjects(engine, host, scope, config, (project, projectRead) => {
258
275
  const resolved = references(project, projectRead.held, targetRoot, {
259
276
  testFiles: config.ts.testFiles,
260
277
  });
@@ -274,9 +291,89 @@ export function runSweep(
274
291
  testFiles: new Set(resolved.testFilePaths),
275
292
  },
276
293
  );
277
- return { configured, exempt };
294
+ return { configured, exempt, extra: extra(project, projectRead) };
278
295
  });
279
- const matrix = matrixOf(read.map((one) => one.configured));
280
- const swept: SweepInput = { ...input, exempt: exemptionsOf(read.flatMap((one) => one.exempt)) };
281
- return { matrix, sweep: sweepMatrix(matrix, swept), retained: retainedIn(matrix, swept) };
296
+ const matrix = matrixOf(projects.map((one) => one.configured));
297
+ const exempt = exemptionsOf(projects.flatMap((one) => one.exempt));
298
+ const swept: SweepInput = { ...input, exempt };
299
+ return {
300
+ swept: { matrix, sweep: sweepMatrix(matrix, swept), retained: retainedIn(matrix, swept) },
301
+ exempt,
302
+ projects,
303
+ };
304
+ }
305
+
306
+ /**
307
+ * Sweeps the run the scope and the configuration describe. Each project's references
308
+ * and exemptions are read against its own inventory while its view is open; the
309
+ * projects then merge into one matrix in the run's order, which is swept under the
310
+ * caller's marks and mode with the exemptions every project found. A record found in
311
+ * one configuration holds in every configuration that holds its declaration, because
312
+ * an exemption is evidence of a use the analysis cannot see and a use in one
313
+ * configuration is a use. A project that carries an error ends the run before any
314
+ * project is swept.
315
+ */
316
+ export function runSweep(
317
+ engine: Engine,
318
+ host: Host,
319
+ scope: Scope,
320
+ config: Config,
321
+ input: Pick<SweepInput, "marked" | "mode">,
322
+ detectors: Detectors = DETECTORS,
323
+ ): RunSweep {
324
+ return sweepRun(engine, host, scope, config, input, detectors, () => undefined).swept;
325
+ }
326
+
327
+ /**
328
+ * What every emitter reads of the run the scope and the configuration describe: the
329
+ * run's sweep, and the facts beside it each kind family reads, every per-project one
330
+ * read in the same session as the sweep. The run loads no consumer and reads no
331
+ * declared cross-language edge, so its boundary names neither.
332
+ */
333
+ export function runEmitterInput(
334
+ engine: Engine,
335
+ host: Host,
336
+ scope: Scope,
337
+ config: Config,
338
+ input: Pick<SweepInput, "marked" | "mode">,
339
+ detectors: Detectors = DETECTORS,
340
+ ): EmitterInput {
341
+ const targetRoot = scope.target.path;
342
+ const run = sweepRun(engine, host, scope, config, input, detectors, (project, read) => ({
343
+ deprecated: deprecatedDeclarations(project, read.held),
344
+ accessors: accessorsOf(project, read.held),
345
+ needs: projectNeeds(project, read.held, host),
346
+ implementations: implementations(project, read.held),
347
+ heldByInclusion: heldByInclusion(project, targetRoot),
348
+ }));
349
+ const { swept, projects } = run;
350
+ return {
351
+ config,
352
+ swept,
353
+ deprecated: new Set(projects.flatMap((one) => one.extra.deprecated)),
354
+ stores: storesOf(
355
+ projects.map((one) => ({
356
+ references: one.configured.references,
357
+ accessors: one.extra.accessors,
358
+ })),
359
+ run.exempt,
360
+ input.mode,
361
+ ),
362
+ dependencies: dependenciesOf(
363
+ host,
364
+ targetRoot,
365
+ projects.map((one) => one.extra.needs),
366
+ swept.sweep.candidates.map((candidate) => candidate.id),
367
+ ),
368
+ implementations: mergeImplementations(projects.map((one) => one.extra.implementations)),
369
+ files: {
370
+ tree: sourceTree(host, targetRoot),
371
+ heldByInclusion: new Set(projects.flatMap((one) => one.extra.heldByInclusion)),
372
+ },
373
+ boundary: {
374
+ consumers: { declared: scope.consumers.map((consumer) => consumer.id), loaded: [] },
375
+ encapsulated: readManifest(host, targetRoot).declaresExports,
376
+ edges: [],
377
+ },
378
+ };
282
379
  }
package/src/components.ts CHANGED
@@ -14,6 +14,11 @@ const ANALYZER = "deadset-ts";
14
14
  /** The fewest digits a component identifier's number is written with. */
15
15
  const ID_DIGITS = 4;
16
16
 
17
+ /** The identifier of the component at one place of the run's order, counted from one. */
18
+ export function componentId(place: number): string {
19
+ return `${ANALYZER}/c-${String(place).padStart(ID_DIGITS, "0")}`;
20
+ }
21
+
17
22
  /** The index of a declaration the component walk has not reached. */
18
23
  const UNVISITED = -1;
19
24
 
@@ -84,7 +89,7 @@ export function componentsOf(
84
89
  return referenced[componentOf[position] ?? 0] !== true && dead[container] !== true;
85
90
  });
86
91
  return {
87
- id: `${ANALYZER}/c-${String(place + 1).padStart(ID_DIGITS, "0")}`,
92
+ id: componentId(place + 1),
88
93
  members: group.map(idOf),
89
94
  roots: roots.map(idOf),
90
95
  deletableLines: linesSpanned(group.map((position) => graph.symbols[at[position] ?? 0])),