@cplieger/deadset-ts 7.0.0-dev.1 → 7.0.0-dev.2

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 (77) hide show
  1. package/dist/src/analysis.js +6 -16
  2. package/dist/src/component-layer.js +1 -1
  3. package/dist/src/configuration-files.js +227 -27
  4. package/dist/src/contextual-uses.js +41 -7
  5. package/dist/src/convention-rows.js +6 -0
  6. package/dist/src/conventions.js +32 -0
  7. package/dist/src/dependencies.js +12 -8
  8. package/dist/src/discover.js +1 -1
  9. package/dist/src/edges.js +1 -1
  10. package/dist/src/exempt-classes.js +9 -22
  11. package/dist/src/exempt.js +3 -15
  12. package/dist/src/findings/completion.js +35 -4
  13. package/dist/src/findings/emitters.js +0 -4
  14. package/dist/src/findings/self-check.js +14 -10
  15. package/dist/src/indexed-access.js +8 -2
  16. package/dist/src/inventory.js +3 -12
  17. package/dist/src/references.js +2 -2
  18. package/dist/src/report.js +3 -0
  19. package/dist/src/roots.js +0 -1
  20. package/dist/src/run.js +0 -2
  21. package/dist/src/schema.js +1 -1
  22. package/dist/src/scope.js +2 -2
  23. package/dist/src/session.js +0 -1
  24. package/dist/src/suppress-inline.js +1 -1
  25. package/dist/src/suppress.js +15 -16
  26. package/dist/src/workspace-programs.js +0 -3
  27. package/dist/src/workspace-resolution.js +0 -2
  28. package/dist/src/workspace.js +0 -3
  29. package/jsr.json +1 -1
  30. package/package.json +2 -1
  31. package/src/analysis.ts +10 -30
  32. package/src/calls.ts +3 -3
  33. package/src/component-files.ts +7 -7
  34. package/src/component-layer.ts +2 -2
  35. package/src/components.ts +1 -1
  36. package/src/config.ts +4 -4
  37. package/src/configuration-files.ts +246 -28
  38. package/src/contextual-uses.ts +63 -9
  39. package/src/convention-rows.ts +19 -0
  40. package/src/conventions.ts +43 -0
  41. package/src/dependencies.ts +14 -10
  42. package/src/discover.ts +3 -3
  43. package/src/edges.ts +3 -3
  44. package/src/entry-points.ts +1 -1
  45. package/src/exempt-classes.ts +10 -24
  46. package/src/exempt.ts +5 -29
  47. package/src/file-facts.ts +1 -1
  48. package/src/finding.ts +3 -1
  49. package/src/findings/completion.ts +46 -4
  50. package/src/findings/emitters.ts +3 -8
  51. package/src/findings/self-check.ts +17 -14
  52. package/src/findings-pass.ts +2 -2
  53. package/src/graph.ts +2 -2
  54. package/src/html-entries.ts +1 -1
  55. package/src/indexed-access.ts +10 -2
  56. package/src/inventory.ts +5 -33
  57. package/src/json-document.ts +1 -1
  58. package/src/manifest.ts +1 -1
  59. package/src/namespace-objects.ts +1 -1
  60. package/src/pnpm-workspace.ts +1 -1
  61. package/src/references.ts +7 -6
  62. package/src/report.ts +8 -4
  63. package/src/roots.ts +0 -3
  64. package/src/run.ts +0 -2
  65. package/src/schema.ts +2 -2
  66. package/src/scope.ts +2 -2
  67. package/src/session.ts +0 -2
  68. package/src/stores.ts +1 -1
  69. package/src/suppress-inline.ts +3 -3
  70. package/src/suppress.ts +21 -24
  71. package/src/template-field.ts +1 -1
  72. package/src/value-reach.ts +1 -1
  73. package/src/verbs/verb.ts +1 -4
  74. package/src/workspace-programs.ts +2 -9
  75. package/src/workspace-resolution.ts +0 -3
  76. package/src/workspace-run.ts +1 -1
  77. package/src/workspace.ts +1 -9
@@ -3,8 +3,8 @@
3
3
  * An object-literal property writes the member of its name on each constituent of the
4
4
  * literal's contextual type that declares one. A value reaching a position typed by another
5
5
  * object type reads each of its members that type declares, through array elements and
6
- * function parameters and results too; a class instance there is the
7
- * interface-satisfaction class's evidence instead.
6
+ * the parameters and results of functions and constructors too; a class instance there is
7
+ * the interface-satisfaction class's evidence instead.
8
8
  */
9
9
 
10
10
  import {
@@ -18,12 +18,13 @@ import {
18
18
  isPropertyAssignment,
19
19
  isSetAccessorDeclaration,
20
20
  isShorthandPropertyAssignment,
21
+ isSpreadAssignment,
21
22
  isStringLiteral,
22
23
  type Expression,
23
24
  type Node,
24
25
  type ObjectLiteralExpression,
25
26
  } from "@typescript/native/unstable/ast";
26
- import { SignatureKind, type Type } from "@typescript/native/unstable/sync";
27
+ import { SignatureKind, SymbolFlags, type Type } from "@typescript/native/unstable/sync";
27
28
  import type { AliasChains } from "./alias-chain.ts";
28
29
  import type { Inventory, SymbolKind } from "./inventory.ts";
29
30
  import { isAnswered, type PropertyTables } from "./query.ts";
@@ -45,14 +46,14 @@ export interface FlowAt {
45
46
  }
46
47
 
47
48
  /** One use the contextual types make: the node it is written at and the members it names. */
48
- export interface ContextualUse {
49
+ interface ContextualUse {
49
50
  readonly at: Node;
50
51
  readonly from: string;
51
52
  readonly ids: readonly string[];
52
53
  }
53
54
 
54
55
  /** What the contextual types of one project's literals and values use, and what asking cost. */
55
- export interface ContextualUses {
56
+ interface ContextualUses {
56
57
  /** Each literal property that writes a member, by the property's node. */
57
58
  readonly writes: readonly ContextualUse[];
58
59
  /** Each value whose members a position typed by another object type reads. */
@@ -93,8 +94,9 @@ function propertyName(
93
94
  return undefined;
94
95
  }
95
96
 
96
- /** The parameter and result types of one call signature, each where the checker answered. */
97
+ /** The parameter and result types of one call or construct signature, each where the checker answered. */
97
98
  interface Called {
99
+ readonly kind: SignatureKind;
98
100
  readonly parameters: readonly (Type | undefined)[];
99
101
  readonly result: Type | undefined;
100
102
  }
@@ -116,6 +118,8 @@ interface Member {
116
118
  readonly handles: ReadonlySet<string>;
117
119
  /** Whether a declaration of it is in one of the target's own files. */
118
120
  readonly own: boolean;
121
+ /** Whether the checker reads it as an accessor. */
122
+ readonly accessor: boolean;
119
123
  }
120
124
 
121
125
  /**
@@ -135,6 +139,11 @@ export function contextualUses<Brand>(
135
139
  const queries = project.queries;
136
140
  const ownPaths = project.ownPaths();
137
141
  const kinds = new Map(held.symbols.map((symbol) => [symbol.id, symbol.kind]));
142
+ const privateNames = new Set(
143
+ held.symbols
144
+ .filter((symbol) => symbol.visibility === "private-name")
145
+ .map((symbol) => symbol.id),
146
+ );
138
147
  let batches = 0;
139
148
  let lookups = 0;
140
149
  const batchesOf = (count: number): number => Math.ceil(count / cap);
@@ -177,6 +186,7 @@ export function contextualUses<Brand>(
177
186
  property.declarations.map((handle) => `${handle.path}#${String(handle.index)}`),
178
187
  ),
179
188
  own: property.declarations.some((handle) => ownPaths.has(handle.path)),
189
+ accessor: (property.flags & SymbolFlags.Accessor) !== 0,
180
190
  });
181
191
  }
182
192
  tables.set(type.id, table);
@@ -269,13 +279,22 @@ export function contextualUses<Brand>(
269
279
  };
270
280
 
271
281
  const calls = new Map<number, Called | undefined>();
272
- /** The parameter and return types of a type's one call signature, read once per type. */
282
+ /**
283
+ * The parameter and return types of a type's one call signature, or of its one construct
284
+ * signature where it has no call signature, read once per type.
285
+ */
273
286
  const callOf = (type: Type): Called | undefined => {
274
287
  if (calls.has(type.id)) {
275
288
  return calls.get(type.id);
276
289
  }
277
290
  lookups += 1;
278
- const signatures = queries.signaturesOf(type, SignatureKind.Call);
291
+ let kind = SignatureKind.Call;
292
+ let signatures = queries.signaturesOf(type, kind);
293
+ if (isAnswered(signatures) && signatures.length === 0) {
294
+ lookups += 1;
295
+ kind = SignatureKind.Construct;
296
+ signatures = queries.signaturesOf(type, kind);
297
+ }
279
298
  let called: Called | undefined;
280
299
  const [signature, ...more] = isAnswered(signatures) ? signatures : [];
281
300
  if (signature !== undefined && more.length === 0) {
@@ -285,6 +304,7 @@ export function contextualUses<Brand>(
285
304
  const result = queries.returnTypeOf(signature);
286
305
  lookups += parameters.length + 1;
287
306
  called = {
307
+ kind,
288
308
  parameters: parameters.map((one) => (isAnswered(one) ? one : undefined)),
289
309
  result: isAnswered(result) ? result : undefined,
290
310
  };
@@ -330,7 +350,7 @@ export function contextualUses<Brand>(
330
350
  continue;
331
351
  }
332
352
  const other = called === undefined ? undefined : callOf(part);
333
- if (called === undefined || other === undefined) {
353
+ if (called === undefined || other?.kind !== called.kind) {
334
354
  continue;
335
355
  }
336
356
  other.parameters.forEach((parameter, index) => {
@@ -413,5 +433,39 @@ export function contextualUses<Brand>(
413
433
  reads.push({ at: flow.value, from: flow.from, ids: [...ids] });
414
434
  }
415
435
  });
436
+ const spreads = literals.flatMap(({ literal, from }) =>
437
+ literal.properties.flatMap((property) =>
438
+ isSpreadAssignment(property) ? [{ value: property.expression, from }] : [],
439
+ ),
440
+ );
441
+ batches += batchesOf(spreads.length);
442
+ const spreadTypes = queries.typesAt(
443
+ spreads.map((one) => one.value),
444
+ cap,
445
+ );
446
+ spreads.forEach(({ value, from }, index) => {
447
+ const type = spreadTypes[index];
448
+ if (type === undefined || !isAnswered(type)) {
449
+ return;
450
+ }
451
+ // A spread gets every own enumerable property, an overwritten key's getter included.
452
+ // So it reads each data member but a class's accessor, which sits on the prototype with
453
+ // the class's methods (never data members), and a private name, which is no property
454
+ // (https://tc39.es/ecma262/#sec-copydataproperties).
455
+ const own = (member: Member, id: string): boolean =>
456
+ !privateNames.has(id) && !(member.accessor && kinds.get(id) === "class-member");
457
+ const ids = [
458
+ ...new Set(
459
+ partsOf(type).flatMap((part) =>
460
+ [...membersOf(part).values()].flatMap((member) =>
461
+ member.ids.filter((id) => own(member, id)),
462
+ ),
463
+ ),
464
+ ),
465
+ ];
466
+ if (ids.length > 0) {
467
+ reads.push({ at: value, from, ids });
468
+ }
469
+ });
416
470
  return { writes, reads, batches, lookups };
417
471
  }
@@ -53,6 +53,16 @@ export interface PackageKey {
53
53
  readonly files: readonly string[];
54
54
  }
55
55
 
56
+ /** One configuration property whose strings name files the test runner loads beside the tests. */
57
+ export interface TestFileReading extends MoveReading {
58
+ /**
59
+ * The properties that move the directory the strings are read against, the first one the
60
+ * file writes deciding, read against the manifest's directory, which stands where none is
61
+ * written. A value that is not one string literal leaves the file's strings unread.
62
+ */
63
+ readonly roots: readonly string[];
64
+ }
65
+
56
66
  /** One directory a row's globs name that a configuration property moves. */
57
67
  export interface DirectoryMove {
58
68
  /** The placeholder the row's globs and later moves write as `<id>`. */
@@ -91,6 +101,8 @@ export interface ConventionRow {
91
101
  * string there names no file the tool runs or loads.
92
102
  */
93
103
  readonly selections?: readonly MoveReading[];
104
+ /** The properties whose strings name a file the test runner loads beside the tests, a test file. */
105
+ readonly testFiles?: readonly TestFileReading[];
94
106
  /**
95
107
  * The members whose strings name a package the tool loads: the package itself, a
96
108
  * subpath of it, or the package followed by `:` and a name.
@@ -144,6 +156,12 @@ const STRYKER_CONFIG = "{stryker,.stryker}.{conf,config}.{json,js,mjs,cjs}";
144
156
  const STRYKER_MODULE = "{stryker,.stryker}.{conf,config}.{js,mjs,cjs}";
145
157
  const VITEST_CONFIG = `{vitest,vite}.config.${ANY_MODULE}`;
146
158
 
159
+ /**
160
+ * The properties Vitest takes its project root from, `test.root` before Vite's `root`; it
161
+ * resolves `setupFiles` against that root (https://vitest.dev/config/setupfiles).
162
+ */
163
+ const VITEST_ROOTS = ["test.root", "root"] as const;
164
+
147
165
  /** The entry files of an Angular CLI project, at the workspace root or below `projects`. */
148
166
  const ANGULAR_ENTRIES = [
149
167
  "src/{main,main.server,server,polyfills,test}.ts",
@@ -642,5 +660,6 @@ export const CONVENTION_ROWS: readonly ConventionRow[] = [
642
660
  { property: "test.coverage.include", files: [VITEST_CONFIG] },
643
661
  { property: "test.coverage.exclude", files: [VITEST_CONFIG] },
644
662
  ],
663
+ testFiles: [{ property: "test.setupFiles", files: [VITEST_CONFIG], roots: VITEST_ROOTS }],
645
664
  },
646
665
  ];
@@ -55,6 +55,7 @@ import {
55
55
  type OptionsCall,
56
56
  type PackageKey,
57
57
  type ShortNameReading,
58
+ type TestFileReading,
58
59
  } from "./convention-rows.ts";
59
60
  import { documentStrings } from "./configuration-files.ts";
60
61
  import { globExpression } from "./glob.ts";
@@ -119,6 +120,8 @@ export interface Conventions {
119
120
  readonly uses: readonly string[];
120
121
  /** Per configuration file, absolute, the strings its applied rows' selection keys hold. */
121
122
  readonly selected: ReadonlyMap<string, Selected>;
123
+ /** The files, absolute, an applied row's test-file properties name. */
124
+ readonly testFiles: ReadonlySet<string>;
122
125
  }
123
126
 
124
127
  /** The strings one configuration file holds at selection keys, which root no file. */
@@ -608,6 +611,43 @@ function holdsValue(held: Node, value: true | string): boolean {
608
611
  );
609
612
  }
610
613
 
614
+ /** The directory one file's test-file strings are read against, as {@link TestFileReading} states it. */
615
+ function testFileRoot(file: SourceFile, dir: string, reading: TestFileReading): string | undefined {
616
+ for (const property of reading.roots) {
617
+ const values = valuesAt(file, property, reading.call);
618
+ if (values.length > 0) {
619
+ const texts = new Set(values.map(literalText));
620
+ const [text] = texts;
621
+ return texts.size === 1 && text !== undefined ? resolvePath(dir, text) : undefined;
622
+ }
623
+ }
624
+ return dir;
625
+ }
626
+
627
+ /** The files an applied row's test-file properties name. */
628
+ function testFilesNamed(
629
+ host: Host,
630
+ parse: ParseSource,
631
+ dir: string,
632
+ readings: readonly TestFileReading[],
633
+ into: Set<string>,
634
+ ): void {
635
+ for (const reading of readings) {
636
+ for (const { file } of readingFiles(host, parse, dir, reading.files)) {
637
+ const at = testFileRoot(file, dir, reading);
638
+ if (at === undefined) {
639
+ continue;
640
+ }
641
+ for (const node of stringsAt(file, reading.property, reading.call)) {
642
+ const text = literalText(node);
643
+ if (text !== undefined && text !== "") {
644
+ into.add(resolvePath(at, text));
645
+ }
646
+ }
647
+ }
648
+ }
649
+ }
650
+
611
651
  /** The strings an applied row's selection keys hold, added per configuration file to `into`. */
612
652
  function selectedStrings(
613
653
  host: Host,
@@ -953,6 +993,7 @@ export function readConventions(
953
993
  const failures: ConventionFailure[] = [];
954
994
  const uses = new Set<string>();
955
995
  const selected = new Map<string, { starts: Set<number>; texts: string[] }>();
996
+ const testFiles = new Set<string>();
956
997
  for (const manifest of manifests) {
957
998
  const declared = readJSON(host, joinPath(manifest.dir, "package.json"));
958
999
  const versions = new Map<string, string | undefined>();
@@ -1025,6 +1066,7 @@ export function readConventions(
1025
1066
  uses.add(name);
1026
1067
  }
1027
1068
  selectedStrings(host, parse, manifest.dir, row.selections ?? [], selected);
1069
+ testFilesNamed(host, parse, manifest.dir, row.testFiles ?? [], testFiles);
1028
1070
  const excludes = [
1029
1071
  ...new Set((row.excludes ?? []).flatMap((one) => expand(one, resolved))),
1030
1072
  ].map(globExpression);
@@ -1055,6 +1097,7 @@ export function readConventions(
1055
1097
  failures,
1056
1098
  uses: [...uses].sort(compare),
1057
1099
  selected,
1100
+ testFiles,
1058
1101
  };
1059
1102
  }
1060
1103
 
@@ -59,7 +59,7 @@ const SECTIONS: readonly (readonly [string, DependencySection])[] = [
59
59
  const PROJECT_OWNER = "";
60
60
 
61
61
  /** One dependency a manifest the run reads declares. */
62
- export interface DeclaredDependency {
62
+ interface DeclaredDependency {
63
63
  readonly name: string;
64
64
  readonly section: DependencySection;
65
65
  /** Where the manifest writes the dependency's key, below the target root. */
@@ -78,7 +78,7 @@ export function configurationNeeds(packages: readonly string[]): ProjectNeeds {
78
78
  }
79
79
 
80
80
  /** What the installed copy of one declared dependency states about itself. */
81
- export interface InstalledDependency {
81
+ interface InstalledDependency {
82
82
  /** The commands its manifest's `bin` declares, by name. */
83
83
  readonly commands: readonly string[];
84
84
  }
@@ -366,16 +366,20 @@ export function projectNeeds<Brand>(
366
366
  use(ownersOf(file, held, node), dependenciesNamed(text, names));
367
367
  }
368
368
  }
369
- for (const document of configuration.documents) {
370
- const names = declaredAt(document.path);
371
- for (const text of document.strings) {
372
- use([PROJECT_OWNER], dependenciesNamed(text, names));
369
+ for (const { strings, manifestDirs } of configuration.documents) {
370
+ for (const manifestDir of manifestDirs) {
371
+ const names = declaredAt(joinPath(manifestDir, "package.json"));
372
+ for (const text of strings) {
373
+ use([PROJECT_OWNER], dependenciesNamed(text, names));
374
+ }
373
375
  }
374
376
  }
375
- for (const { file, manifestDir } of configuration.outside) {
376
- const names = declaredAt(joinPath(manifestDir, "package.json"));
377
- for (const { text } of moduleStrings(file)) {
378
- use([PROJECT_OWNER], dependenciesNamed(text, names));
377
+ for (const { file, manifestDirs } of configuration.outside) {
378
+ for (const manifestDir of manifestDirs) {
379
+ const names = declaredAt(joinPath(manifestDir, "package.json"));
380
+ for (const { text } of moduleStrings(file)) {
381
+ use([PROJECT_OWNER], dependenciesNamed(text, names));
382
+ }
379
383
  }
380
384
  }
381
385
  return { packages, uses, unanswered };
package/src/discover.ts CHANGED
@@ -34,7 +34,7 @@ export class DiscoveryError extends Error {
34
34
  const ERROR_CATEGORY: number = DiagnosticCategory.Error;
35
35
 
36
36
  /** Whether one diagnostic is an error, which is what fails a run closed. */
37
- export function isError(diagnostic: Diagnostic): boolean {
37
+ function isError(diagnostic: Diagnostic): boolean {
38
38
  return diagnostic.category === ERROR_CATEGORY;
39
39
  }
40
40
 
@@ -113,7 +113,7 @@ export function projectErrors(sets: ProjectDiagnostics): ProjectErrors {
113
113
  }
114
114
 
115
115
  /** One project a run analyzes. */
116
- export interface DiscoveredProject {
116
+ interface DiscoveredProject {
117
117
  /**
118
118
  * The name a run gives the project: the identifier the build matrix declares for
119
119
  * it, or, for a project discovery derived, its configuration file's path below the
@@ -162,7 +162,7 @@ export function isGuess(discovery: Discovery, configFile: string): boolean {
162
162
  }
163
163
 
164
164
  /** Every project discovery found, and where each came from. */
165
- export interface Discovery {
165
+ interface Discovery {
166
166
  /** The projects, in discovery order, each once. */
167
167
  readonly projects: readonly DiscoveredProject[];
168
168
  /**
package/src/edges.ts CHANGED
@@ -39,7 +39,7 @@ const REFERENCE_WANT =
39
39
  "a stable symbol reference naming one symbol exactly, <language>://<scope>#<fragment>";
40
40
 
41
41
  /** An edges document the Contract refuses: a malformed input, answered with the usage code. */
42
- export class EdgesError extends ConfigError {
42
+ class EdgesError extends ConfigError {
43
43
  constructor(at: string, text: string, want: string) {
44
44
  super("malformed", EDGES_FILE, `${at}: ${text}: want ${want}`);
45
45
  this.name = "EdgesError";
@@ -174,7 +174,7 @@ export function readEdgeSides(host: Host, targetRoot: string): readonly EdgeSide
174
174
  }
175
175
 
176
176
  /** One side's verdict, as an evaluation names it. */
177
- export type EdgeState = "live" | "dead" | "absent";
177
+ type EdgeState = "live" | "dead" | "absent";
178
178
 
179
179
  /** One edge evaluation: one side of this language, its state, and the finding it holds. */
180
180
  export interface EdgeEvaluation<F extends Finding = Finding> extends EdgeSide {
@@ -184,7 +184,7 @@ export interface EdgeEvaluation<F extends Finding = Finding> extends EdgeSide {
184
184
  }
185
185
 
186
186
  /** The findings a report publishes, and one evaluation per declared side of this language. */
187
- export interface Evaluated<F extends Finding> {
187
+ interface Evaluated<F extends Finding> {
188
188
  readonly findings: readonly F[];
189
189
  /** Ordered by edge, then by side, each compared bytewise. */
190
190
  readonly evaluations: readonly EdgeEvaluation<F>[];
@@ -64,7 +64,7 @@ export type EntryRule =
64
64
  | "workflow-step";
65
65
 
66
66
  /** One file a rule enters. */
67
- export interface EntryPoint {
67
+ interface EntryPoint {
68
68
  readonly file: SourceFile;
69
69
  readonly rule: EntryRule;
70
70
  /**
@@ -1,16 +1,12 @@
1
1
  /**
2
- * The exemption classes of the Contract's vocabulary, in its order: each class's name,
3
- * the languages it runs on, the confidence it is recorded at and, for a class that runs
4
- * on TypeScript, which private members it may retain.
5
- *
6
- * The rows are written out rather than read from the Contract at run time, for the
7
- * reason the fixed severity codes are, and a test holds them equal to the Contract
8
- * release this analyzer implements. A class name is a type, so a detector table or a
9
- * record naming a class the vocabulary does not hold, or one that runs on another
10
- * language only, does not compile.
2
+ * The exemption classes of the Contract's vocabulary, in its order: each class's name, its
3
+ * languages and, for a TypeScript class, which private members it may retain. The rows are
4
+ * written out, as the fixed severity codes are, and a test holds them equal to the Contract.
5
+ * A class name is a type, so a table or a record naming a class outside the vocabulary, or
6
+ * one of another language only, does not compile.
11
7
  */
12
8
 
13
- import type { Confidence, Language } from "./config.ts";
9
+ import type { Language } from "./config.ts";
14
10
 
15
11
  /**
16
12
  * Whether a class may retain a member declared with the `private` modifier, and one
@@ -27,7 +23,6 @@ export interface TypeScriptVisibility {
27
23
  interface ExemptionClassRow {
28
24
  readonly class: string;
29
25
  readonly languages: readonly Language[];
30
- readonly confidence: Confidence;
31
26
  /** Present exactly on a class that runs on TypeScript. */
32
27
  readonly typescriptVisibility?: TypeScriptVisibility;
33
28
  }
@@ -37,59 +32,50 @@ export const EXEMPTION_CLASSES = [
37
32
  {
38
33
  class: "interface-satisfaction",
39
34
  languages: ["go", "ts"],
40
- confidence: "certain",
41
35
  typescriptVisibility: { private: false, privateName: false },
42
36
  },
43
- { class: "encoding-reflection", languages: ["go"], confidence: "certain" },
44
- { class: "format-verb-contract", languages: ["go"], confidence: "certain" },
45
- { class: "errors-duck-typing", languages: ["go"], confidence: "certain" },
37
+ { class: "encoding-reflection", languages: ["go"] },
38
+ { class: "format-verb-contract", languages: ["go"] },
39
+ { class: "errors-duck-typing", languages: ["go"] },
46
40
  {
47
41
  class: "enum-group",
48
42
  languages: ["go", "ts"],
49
- confidence: "certain",
50
43
  typescriptVisibility: { private: false, privateName: false },
51
44
  },
52
45
  {
53
46
  class: "generated-file",
54
47
  languages: ["go", "ts"],
55
- confidence: "certain",
56
48
  typescriptVisibility: { private: true, privateName: false },
57
49
  },
58
- { class: "linkname-cgo-asm-plugin", languages: ["go"], confidence: "certain" },
50
+ { class: "linkname-cgo-asm-plugin", languages: ["go"] },
59
51
  {
60
52
  class: "template-field",
61
53
  languages: ["go", "ts"],
62
- confidence: "possible",
63
54
  typescriptVisibility: { private: true, privateName: false },
64
55
  },
65
56
  {
66
57
  class: "reflective-lookup",
67
58
  languages: ["go", "ts"],
68
- confidence: "possible",
69
59
  typescriptVisibility: { private: true, privateName: false },
70
60
  },
71
61
  {
72
62
  class: "decorator",
73
63
  languages: ["ts"],
74
- confidence: "certain",
75
64
  typescriptVisibility: { private: true, privateName: false },
76
65
  },
77
66
  {
78
67
  class: "injection-container",
79
68
  languages: ["ts"],
80
- confidence: "certain",
81
69
  typescriptVisibility: { private: true, privateName: false },
82
70
  },
83
71
  {
84
72
  class: "framework-lifecycle",
85
73
  languages: ["ts"],
86
- confidence: "certain",
87
74
  typescriptVisibility: { private: true, privateName: false },
88
75
  },
89
76
  {
90
77
  class: "serialization-contract",
91
78
  languages: ["ts"],
92
- confidence: "certain",
93
79
  typescriptVisibility: { private: true, privateName: false },
94
80
  },
95
81
  ] as const satisfies readonly ExemptionClassRow[];
package/src/exempt.ts CHANGED
@@ -64,9 +64,7 @@ export type Detector = <Brand>(input: DetectorInput<Brand>) => readonly Evidence
64
64
  export type Detectors = ReadonlyMap<TSExemptionClass, Detector>;
65
65
 
66
66
  /** What decides which records of one project stand. */
67
- export interface Holding {
68
- /** The classes the configuration switches off, whose detectors do not run. */
69
- readonly disabled: ReadonlySet<ExemptionClass>;
67
+ interface Holding {
70
68
  readonly mode: Mode;
71
69
  /** The paths the reference pass classified as test files. */
72
70
  readonly testFiles: ReadonlySet<string>;
@@ -154,23 +152,8 @@ function detected<Brand>(
154
152
  }
155
153
  }
156
154
 
157
- /**
158
- * The records one project's detectors found, each class in the vocabulary's order and
159
- * none of a switched-off class. Evidence on a private member the class's row says it
160
- * cannot retain is dropped first, whatever the detector found. Under a production mode
161
- * a record whose evidence is written in a test file holds nothing, as a reference a
162
- * test file makes is none there.
163
- */
164
- export function computeExemptions<Brand>(
165
- input: DetectorInput<Brand>,
166
- detectors: Detectors,
167
- holding: Holding,
168
- ): readonly Exemption[] {
169
- return detectExemptions(input, detectors, holding).records;
170
- }
171
-
172
155
  /** One project's records, and the evidence a production mode found in a test file. */
173
- export interface Detected {
156
+ interface Detected {
174
157
  readonly records: readonly Exemption[];
175
158
  /**
176
159
  * Under a production mode, each record whose evidence is written in a test file. It
@@ -180,18 +163,11 @@ export interface Detected {
180
163
  readonly inTestFiles: readonly Exemption[];
181
164
  }
182
165
 
183
- /** The records of {@link computeExemptions}, beside the test-file evidence it set aside. */
184
- export function detectExemptions<Brand>(
185
- input: DetectorInput<Brand>,
186
- detectors: Detectors,
187
- holding: Holding,
188
- ): Detected {
189
- return heldRecords(evidenceRecords(input, detectors, holding.disabled), holding);
190
- }
191
-
192
166
  /**
193
167
  * Every record one project's detectors found, one per piece of evidence, before the
194
- * mode decides which of them hold: what {@link heldRecords} reads.
168
+ * mode decides which of them hold: what {@link heldRecords} reads. Classes run in the
169
+ * vocabulary's order and a switched-off class runs not at all; evidence on a private
170
+ * member the class's row says it cannot retain is dropped, whatever the detector found.
195
171
  */
196
172
  export function evidenceRecords<Brand>(
197
173
  input: DetectorInput<Brand>,
package/src/file-facts.ts CHANGED
@@ -22,7 +22,7 @@ const DECLARATION_FILE = /\.d\.(?:[^./]+\.)?[mc]?ts$/u;
22
22
  const LINE_BREAK = /\r\n|[\n\r\u2028\u2029]/gu;
23
23
 
24
24
  /** One source file below the target root. */
25
- export interface TreeFile {
25
+ interface TreeFile {
26
26
  /** The path below the target root, with the solidus as separator. */
27
27
  readonly path: string;
28
28
  /** The stable reference of the file's module. */
package/src/finding.ts CHANGED
@@ -25,7 +25,7 @@ export interface FindingPosition {
25
25
  }
26
26
 
27
27
  /** What a finding is about. */
28
- export interface FindingSubject {
28
+ interface FindingSubject {
29
29
  /** The stable symbol reference, or for a configured root the root as written. */
30
30
  readonly ref: string;
31
31
  /** The subject kind, from the closed vocabulary of the finding schema. */
@@ -65,6 +65,8 @@ export interface FindingDetails {
65
65
  readonly entry?: SuppressionEntry;
66
66
  /** On the intra-function kinds: the external rules that report the same kind, as the vocabulary lists them. */
67
67
  readonly overlap?: readonly string[];
68
+ /** On a deletable finding: the dependencies whose last use deleting the subject removes. */
69
+ readonly removesLastUseOf?: readonly string[];
68
70
  }
69
71
 
70
72
  /** One suppression in the four-member form the ignore file and the baseline share. */