@cplieger/deadset-ts 6.1.1-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 (98) hide show
  1. package/dist/src/analysis.js +45 -32
  2. package/dist/src/component-layer.js +1 -1
  3. package/dist/src/configuration-files.js +227 -27
  4. package/dist/src/conformance-record.js +2 -2
  5. package/dist/src/contextual-uses.js +68 -10
  6. package/dist/src/convention-rows.js +48 -0
  7. package/dist/src/conventions.js +80 -0
  8. package/dist/src/dependencies.js +121 -46
  9. package/dist/src/discover.js +1 -1
  10. package/dist/src/edges.js +1 -1
  11. package/dist/src/exempt-classes.js +9 -22
  12. package/dist/src/exempt.js +3 -15
  13. package/dist/src/findings/completion.js +41 -11
  14. package/dist/src/findings/dependencies-and-module-machinery.js +6 -26
  15. package/dist/src/findings/emitters.js +0 -4
  16. package/dist/src/findings/self-check.js +14 -10
  17. package/dist/src/html-entries.js +29 -14
  18. package/dist/src/indexed-access.js +377 -81
  19. package/dist/src/intra-function-parts.js +57 -11
  20. package/dist/src/inventory.js +3 -12
  21. package/dist/src/kinds.js +6 -6
  22. package/dist/src/references.js +62 -23
  23. package/dist/src/report.js +3 -0
  24. package/dist/src/roots.js +96 -7
  25. package/dist/src/run.js +0 -2
  26. package/dist/src/schema.js +1 -1
  27. package/dist/src/scope.js +2 -2
  28. package/dist/src/session.js +6 -2
  29. package/dist/src/source-files.js +1 -0
  30. package/dist/src/suppress-inline.js +1 -1
  31. package/dist/src/suppress.js +15 -16
  32. package/dist/src/sweep.js +29 -17
  33. package/dist/src/symbol-keys.js +114 -0
  34. package/dist/src/test-joins.js +101 -0
  35. package/dist/src/version.js +1 -1
  36. package/dist/src/workflow-steps.js +21 -15
  37. package/dist/src/workspace-programs.js +1 -4
  38. package/dist/src/workspace-resolution.js +0 -2
  39. package/dist/src/workspace.js +0 -3
  40. package/jsr.json +1 -1
  41. package/package.json +2 -1
  42. package/src/analysis.ts +55 -47
  43. package/src/calls.ts +3 -3
  44. package/src/component-files.ts +7 -7
  45. package/src/component-layer.ts +2 -2
  46. package/src/components.ts +1 -1
  47. package/src/config.ts +4 -4
  48. package/src/configuration-files.ts +246 -28
  49. package/src/conformance-record.ts +2 -2
  50. package/src/contextual-uses.ts +99 -13
  51. package/src/convention-rows.ts +76 -0
  52. package/src/conventions.ts +102 -0
  53. package/src/dependencies.ts +148 -56
  54. package/src/discover.ts +3 -3
  55. package/src/edges.ts +3 -3
  56. package/src/entry-points.ts +1 -1
  57. package/src/exempt-classes.ts +10 -24
  58. package/src/exempt.ts +5 -29
  59. package/src/file-facts.ts +1 -1
  60. package/src/finding.ts +5 -1
  61. package/src/findings/completion.ts +53 -11
  62. package/src/findings/dependencies-and-module-machinery.ts +9 -28
  63. package/src/findings/emitters.ts +3 -8
  64. package/src/findings/self-check.ts +17 -14
  65. package/src/findings-pass.ts +2 -2
  66. package/src/graph.ts +2 -2
  67. package/src/html-entries.ts +34 -16
  68. package/src/indexed-access.ts +476 -102
  69. package/src/intra-function-parts.ts +84 -12
  70. package/src/inventory.ts +5 -33
  71. package/src/json-document.ts +1 -1
  72. package/src/kinds.ts +7 -6
  73. package/src/manifest.ts +1 -1
  74. package/src/namespace-objects.ts +1 -1
  75. package/src/pnpm-workspace.ts +1 -1
  76. package/src/references.ts +93 -33
  77. package/src/report.ts +8 -4
  78. package/src/roots.ts +129 -9
  79. package/src/run.ts +0 -2
  80. package/src/schema.ts +2 -2
  81. package/src/scope.ts +2 -2
  82. package/src/session.ts +7 -3
  83. package/src/source-files.ts +3 -0
  84. package/src/stores.ts +1 -1
  85. package/src/suppress-inline.ts +3 -3
  86. package/src/suppress.ts +21 -24
  87. package/src/sweep.ts +29 -17
  88. package/src/symbol-keys.ts +172 -0
  89. package/src/template-field.ts +1 -1
  90. package/src/test-joins.ts +120 -0
  91. package/src/value-reach.ts +1 -1
  92. package/src/verbs/verb.ts +1 -4
  93. package/src/version.ts +1 -1
  94. package/src/workflow-steps.ts +23 -16
  95. package/src/workspace-programs.ts +3 -10
  96. package/src/workspace-resolution.ts +0 -3
  97. package/src/workspace-run.ts +1 -1
  98. package/src/workspace.ts +1 -9
package/src/analysis.ts CHANGED
@@ -62,11 +62,7 @@ import { globExpression } from "./glob.ts";
62
62
  import type { Finding } from "./finding.ts";
63
63
  import { findingsPass, recordedFindings, rowKey, type PassResult } from "./findings-pass.ts";
64
64
  import type { EmitterInput } from "./findings/emitter.ts";
65
- import {
66
- NO_SELF_CHECK,
67
- unmatchedRootFindings,
68
- type SelfCheckFacts,
69
- } from "./findings/self-check.ts";
65
+ import { unmatchedRootFindings, type SelfCheckFacts } from "./findings/self-check.ts";
70
66
  import { frameworkLifecycle } from "./framework-lifecycle.ts";
71
67
  import type { Host } from "./host.ts";
72
68
  import { implementations, mergeImplementations } from "./implementations.ts";
@@ -84,7 +80,7 @@ import {
84
80
  type Matrix,
85
81
  type SweepResult,
86
82
  } from "./matrix.ts";
87
- import { joinPath, normalizePath, relativePath, resolvePath } from "./paths.ts";
83
+ import { dirnamePath, joinPath, normalizePath, relativePath, resolvePath } from "./paths.ts";
88
84
  import type { UnansweredQuestion } from "./query.ts";
89
85
  import { byPosition, isComponentFile, type Position } from "./position.ts";
90
86
  import type { TypeErrorSkip, UnansweredCount } from "./report.ts";
@@ -102,6 +98,7 @@ import { roots, unmatchedEverywhere, type RootKind, type Roots } from "./roots.t
102
98
  import type { Scope } from "./scope.ts";
103
99
  import { serializationContract } from "./serialization-contract.ts";
104
100
  import { outsideTestReferences, testFilesAt, type OutsideTest } from "./outside-tests.ts";
101
+ import { joinedTestFiles, withJoinedTests } from "./test-joins.ts";
105
102
  import { supportReferences, testSupportFiles } from "./test-support.ts";
106
103
  import { diagnosticsOf, ownedAs, runSession, type Engine, type ProjectView } from "./session.ts";
107
104
  import { accessorsOf, storesOf } from "./stores.ts";
@@ -193,6 +190,8 @@ interface ProjectRead {
193
190
  readonly skipped: readonly SkippedUnit[];
194
191
  /** The project's generated files, by path below the target root, each with the detail its retentions name. */
195
192
  readonly generated: ReadonlyMap<string, string>;
193
+ /** The files, absolute, an applied convention row names as test files. */
194
+ readonly testFileNames: ReadonlySet<string>;
196
195
  }
197
196
 
198
197
  /** One target project the run analyzes, as its stage is handed it. */
@@ -308,6 +307,9 @@ function readProjects<Answer>(
308
307
  testFiles: config.ts.testFiles,
309
308
  parse: (fileName: string, text: string) => engine.parseSourceFile(fileName, text),
310
309
  publishedAPI: config.targetKind === "library",
310
+ publishedMembers: new Set(
311
+ (workspace?.entries ?? []).filter((entry) => entry.published).map((entry) => entry.dir),
312
+ ),
311
313
  };
312
314
  const notBuilt: NotBuilt[] = [...discovered.notBuilt];
313
315
  const dropped = (configFile: string, error: string): void => {
@@ -332,6 +334,10 @@ function readProjects<Answer>(
332
334
  unbuildable.push(...met);
333
335
  }
334
336
  };
337
+ const testPatterns = config.ts.testFiles.map(globExpression);
338
+ const joins = discovered.derived
339
+ ? joinedTestFiles(engine, host, absoluteRoot, discovered.configFiles, testPatterns)
340
+ : new Map<string, readonly string[]>();
335
341
  const session = runSession(
336
342
  engine,
337
343
  [...discovered.configFiles, ...consumerOf.keys()],
@@ -452,6 +458,7 @@ function readProjects<Answer>(
452
458
  .filter((file) => referenceOnly(file.fileName))
453
459
  .flatMap((file) => relativePath(absoluteRoot, file.fileName) ?? []),
454
460
  generated: generatedFilesOf(own, generatedFiles(conventions, own)),
461
+ testFileNames: conventions.testFiles,
455
462
  };
456
463
  };
457
464
  for (const file of project.ownSourceFiles()) {
@@ -460,14 +467,15 @@ function readProjects<Answer>(
460
467
  return stage(project, { configuration, read });
461
468
  },
462
469
  discovered.derived ? "record" : "refuse",
463
- workspace?.programs,
470
+ joins.size === 0
471
+ ? workspace?.programs
472
+ : withJoinedTests(engine, host, workspace?.programs ?? new Map(), joins),
464
473
  () => {
465
- const patterns = config.ts.testFiles.map(globExpression);
466
474
  outsideTests = testFilesAt(
467
475
  host,
468
476
  targetRoot,
469
477
  sourcePaths(host, targetRoot).filter(
470
- (path) => !heldFiles.has(path) && patterns.some((pattern) => pattern.test(path)),
478
+ (path) => !heldFiles.has(path) && testPatterns.some((pattern) => pattern.test(path)),
471
479
  ),
472
480
  options.parse,
473
481
  );
@@ -640,7 +648,7 @@ export interface ComponentWarning {
640
648
  }
641
649
 
642
650
  /** The exemption classes this analyzer detects, each by its detector; any other retains nothing. */
643
- const DETECTORS: Detectors = new Map<TSExemptionClass, Detector>([
651
+ export const DETECTORS: Detectors = new Map<TSExemptionClass, Detector>([
644
652
  ["interface-satisfaction", interfaceSatisfaction],
645
653
  ["enum-group", enumGroup],
646
654
  ["generated-file", generatedFile],
@@ -730,6 +738,7 @@ function readRun<Extra>(
730
738
  const projectRead = opened.read();
731
739
  const resolved = references(project, projectRead.held, targetRoot, {
732
740
  testFiles: config.ts.testFiles,
741
+ testFileNames: projectRead.testFileNames,
733
742
  });
734
743
  const evidence = evidenceRecords(
735
744
  {
@@ -790,7 +799,6 @@ function readRun<Extra>(
790
799
  const { projectRead } = one;
791
800
  const satisfied = settled[index];
792
801
  const detected = heldRecords(satisfied?.evidence ?? one.evidence, {
793
- disabled,
794
802
  mode,
795
803
  testFiles: new Set([...one.testFiles, ...support]),
796
804
  });
@@ -1040,7 +1048,7 @@ interface EmitterExtra {
1040
1048
  }
1041
1049
 
1042
1050
  /** Reads a run's projects with every per-project fact an emitter reads. */
1043
- function readEmitterRun(
1051
+ export function readEmitterRun(
1044
1052
  engine: Engine,
1045
1053
  host: Host,
1046
1054
  scope: Scope,
@@ -1082,34 +1090,51 @@ function readEmitterRun(
1082
1090
  }
1083
1091
 
1084
1092
  /**
1085
- * The workspace members whose manifest the run reads whole enough to judge its
1086
- * dependencies: a built project holds a file below the member's directory, and no derived
1087
- * configuration at or below it was dropped. A file no built project holds may import any
1088
- * dependency the member declares.
1093
+ * The workspace members whose dependencies the run judges: `read` holds those a built
1094
+ * project holds a file below, `unbuilt` the others whose own directory, the nearest
1095
+ * manifest's above the configuration, holds a configuration the run could not build. A
1096
+ * member no configuration holds a file of is in neither.
1089
1097
  */
1090
- function membersWhollyRead(
1098
+ function judgedMembers(
1091
1099
  host: Host,
1092
1100
  targetRoot: string,
1093
1101
  read: ReadRun<EmitterExtra>,
1094
- ): readonly string[] {
1102
+ ): { readonly read: readonly string[]; readonly unbuilt: readonly string[] } {
1095
1103
  const root = resolvePath(host.workingDirectory(), targetRoot);
1096
1104
  const held = new Set(read.matrix.union.symbols.map((symbol) => symbol.position.path));
1097
- const dropped = read.notBuilt.map((one) => relativePath(root, one.configFile) ?? "");
1098
- return read.memberDirs.filter((dir) => {
1105
+ const owners = new Set(
1106
+ read.notBuilt.map((one) => nearestManifestDir(host, root, one.configFile)),
1107
+ );
1108
+ const judged = { read: [] as string[], unbuilt: [] as string[] };
1109
+ for (const dir of read.memberDirs) {
1099
1110
  const below = relativePath(root, dir);
1100
1111
  if (below === undefined) {
1101
- return false;
1112
+ continue;
1102
1113
  }
1103
- const inside = (path: string): boolean => path.startsWith(`${below}/`);
1104
- return [...held].some(inside) && !dropped.some(inside);
1105
- });
1114
+ if ([...held].some((path) => path.startsWith(`${below}/`))) {
1115
+ judged.read.push(dir);
1116
+ } else if (owners.has(dir)) {
1117
+ judged.unbuilt.push(dir);
1118
+ }
1119
+ }
1120
+ return judged;
1121
+ }
1122
+
1123
+ /** The directory of the nearest `package.json` at or above a file, inside `root`, else `root`. */
1124
+ function nearestManifestDir(host: Host, root: string, file: string): string {
1125
+ for (let dir = dirnamePath(file); relativePath(root, dir) !== undefined; dir = dirnamePath(dir)) {
1126
+ if (host.kindOf(joinPath(dir, "package.json")) === "file") {
1127
+ return dir;
1128
+ }
1129
+ }
1130
+ return root;
1106
1131
  }
1107
1132
 
1108
1133
  /**
1109
1134
  * The input every emitter reads over one sweep of a read run: the facts beside the sweep
1110
1135
  * each kind family reads, every per-project one read in the same session as the sweep.
1111
1136
  */
1112
- function emitterInputOver(
1137
+ export function emitterInputOver(
1113
1138
  host: Host,
1114
1139
  scope: Scope,
1115
1140
  config: Config,
@@ -1120,6 +1145,7 @@ function emitterInputOver(
1120
1145
  const targetRoot = scope.target.path;
1121
1146
  const swept = sweptOf(read, input);
1122
1147
  const { projects } = read;
1148
+ const members = judgedMembers(host, targetRoot, read);
1123
1149
  return {
1124
1150
  config,
1125
1151
  swept,
@@ -1137,7 +1163,8 @@ function emitterInputOver(
1137
1163
  targetRoot,
1138
1164
  [...projects.map((one) => one.extra.needs), configurationNeeds(read.conventionUses)],
1139
1165
  swept.sweep.candidates.map((candidate) => candidate.id),
1140
- membersWhollyRead(host, targetRoot, read),
1166
+ members.read,
1167
+ members.unbuilt,
1141
1168
  ),
1142
1169
  implementations: mergeImplementations(projects.map((one) => one.extra.implementations)),
1143
1170
  files: {
@@ -1162,25 +1189,6 @@ function emitterInputOver(
1162
1189
  };
1163
1190
  }
1164
1191
 
1165
- /**
1166
- * What every emitter reads of the run the scope and the configuration describe, swept
1167
- * under the caller's marks: the run's sweep, the facts beside it each kind family reads,
1168
- * and the sides of the target's declared cross-language edges that name a symbol of this
1169
- * language. The run reads no suppression document, so the self-check family has nothing
1170
- * to report.
1171
- */
1172
- export function runEmitterInput(
1173
- engine: Engine,
1174
- host: Host,
1175
- scope: Scope,
1176
- config: Config,
1177
- input: Pick<SweepInput, "marked" | "mode">,
1178
- detectors: Detectors = DETECTORS,
1179
- ): EmitterInput {
1180
- const read = readEmitterRun(engine, host, scope, config, input.mode, detectors);
1181
- return emitterInputOver(host, scope, config, read, input, NO_SELF_CHECK);
1182
- }
1183
-
1184
1192
  /** One run read for its findings: the input swept with and without its suppressions' marks. */
1185
1193
  export interface AnalysisInputs {
1186
1194
  /** The run swept under the marks of every bound suppression record. */
@@ -1310,7 +1318,7 @@ export interface RunProject {
1310
1318
  }
1311
1319
 
1312
1320
  /** What a report states about the run beside its findings. */
1313
- export interface RunFacts {
1321
+ interface RunFacts {
1314
1322
  /** The projects the run analyzed, in the run's order. */
1315
1323
  readonly projects: readonly RunProject[];
1316
1324
  /** The rules that classified files as test files, each counted over every project at once. */
@@ -1332,7 +1340,7 @@ export interface RunFacts {
1332
1340
  }
1333
1341
 
1334
1342
  /** One configuration whose component files a run cannot read. */
1335
- export interface ComponentsUnread {
1343
+ interface ComponentsUnread {
1336
1344
  readonly configFile: string;
1337
1345
  readonly reason: string;
1338
1346
  }
package/src/calls.ts CHANGED
@@ -30,7 +30,7 @@ import { must } from "./query.ts";
30
30
  import type { ProjectView } from "./session.ts";
31
31
 
32
32
  /** One value written where a call passes it, and the node its declaration is resolved from. */
33
- export interface PassedValue {
33
+ interface PassedValue {
34
34
  /** Where the value is written, which is where a record of it sits. */
35
35
  readonly site: Node;
36
36
  /** The name the value is resolved through, or the shorthand assignment that holds it. */
@@ -39,7 +39,7 @@ export interface PassedValue {
39
39
  }
40
40
 
41
41
  /** One class of the target a call passes or a decorator is attached to, and where. */
42
- export interface ClassUse {
42
+ interface ClassUse {
43
43
  /** The inventory's identifier of the class. */
44
44
  readonly id: string;
45
45
  readonly site: Node;
@@ -182,7 +182,7 @@ export function classesPassed<Brand>(
182
182
  }
183
183
 
184
184
  /** One class declaration of the target and the decorators written on it. */
185
- export interface DecoratedClass {
185
+ interface DecoratedClass {
186
186
  readonly id: string;
187
187
  readonly decorators: readonly Decorator[];
188
188
  }
@@ -7,10 +7,10 @@
7
7
  */
8
8
 
9
9
  /** The language one block is written in, as the module's virtual extension spells it. */
10
- export type BlockLanguage = "ts" | "tsx" | "js" | "jsx";
10
+ type BlockLanguage = "ts" | "tsx" | "js" | "jsx";
11
11
 
12
12
  /** One block the module holds, the frontmatter included. */
13
- export interface Block {
13
+ interface Block {
14
14
  /** The offset the block's markup starts at: the `<` of its start tag, or 0 for frontmatter. */
15
15
  readonly tagStart: number;
16
16
  readonly contentStart: number;
@@ -20,7 +20,7 @@ export interface Block {
20
20
  }
21
21
 
22
22
  /** One file a `<script src>` element names, which the module imports whole. */
23
- export interface SourceImport {
23
+ interface SourceImport {
24
24
  /** The specifier the module imports, relative to the component file. */
25
25
  readonly specifier: string;
26
26
  /** The offsets of the `src` attribute, name and value. */
@@ -29,14 +29,14 @@ export interface SourceImport {
29
29
  }
30
30
 
31
31
  /** A `<script` start tag beginning a line that the module holds no block for. */
32
- export interface ComponentWarning {
32
+ interface ComponentWarning {
33
33
  /** The offset of the tag's `<`. */
34
34
  readonly offset: number;
35
35
  readonly reason: string;
36
36
  }
37
37
 
38
38
  /** One run of markup an expression or an attribute value is written in. */
39
- export interface MarkupAction {
39
+ interface MarkupAction {
40
40
  /** The offset of the run's first code unit. */
41
41
  readonly start: number;
42
42
  readonly text: string;
@@ -45,7 +45,7 @@ export interface MarkupAction {
45
45
  }
46
46
 
47
47
  /** What one component file's text is read as. */
48
- export interface ComponentFile {
48
+ interface ComponentFile {
49
49
  readonly blocks: readonly Block[];
50
50
  readonly imports: readonly SourceImport[];
51
51
  readonly warnings: readonly ComponentWarning[];
@@ -54,7 +54,7 @@ export interface ComponentFile {
54
54
  }
55
55
 
56
56
  /** The module the compiler reads in place of one component file. */
57
- export interface ComponentModule {
57
+ interface ComponentModule {
58
58
  /** The module's text: the file's length, then what the module appends. */
59
59
  readonly text: string;
60
60
  /** The virtual extension: the strongest language any block names, `.ts` for none. */
@@ -11,10 +11,10 @@ import type { Host } from "./host.ts";
11
11
  import { dirnamePath, joinPath } from "./paths.ts";
12
12
 
13
13
  /** The name the mapper's manifest declares and every configuration's entry names. */
14
- export const MAPPER_PACKAGE = "deadset-ts-component-files";
14
+ const MAPPER_PACKAGE = "deadset-ts-component-files";
15
15
 
16
16
  /** The file system one snapshot is created with. */
17
- export type SnapshotFileSystem = NonNullable<CreateSnapshotParams["fileSystem"]>;
17
+ type SnapshotFileSystem = NonNullable<CreateSnapshotParams["fileSystem"]>;
18
18
 
19
19
  /** The manifest of the mapper's package, which runs `command`. */
20
20
  export function mapperManifest(command: readonly string[]): string {
package/src/components.ts CHANGED
@@ -42,7 +42,7 @@ export interface Component {
42
42
  }
43
43
 
44
44
  /** What a report carries for one dead component under one cascade mode. */
45
- export interface Listing {
45
+ interface Listing {
46
46
  readonly roots: readonly string[];
47
47
  /** Every member under the full mode, and nothing under the default one. */
48
48
  readonly members: readonly string[];
package/src/config.ts CHANGED
@@ -50,7 +50,7 @@ export type Cascade = "roots" | "full";
50
50
  * tags in effect. It is the shape a language built under build constraints reads,
51
51
  * and this analyzer leaves it to that analysis.
52
52
  */
53
- export interface PlatformConfiguration {
53
+ interface PlatformConfiguration {
54
54
  readonly shape: "platform";
55
55
  readonly id: string;
56
56
  readonly os: string;
@@ -97,7 +97,7 @@ export interface Analysis {
97
97
  }
98
98
 
99
99
  /** How findings are rendered. */
100
- export interface Reporters {
100
+ interface Reporters {
101
101
  readonly formats: readonly Format[];
102
102
  readonly sort: Sort;
103
103
  readonly cascade: Cascade;
@@ -154,7 +154,7 @@ export type Provider =
154
154
  };
155
155
 
156
156
  /** The analyzers the orchestrator runs. */
157
- export interface Providers {
157
+ interface Providers {
158
158
  readonly analyzers: readonly Provider[];
159
159
  }
160
160
 
@@ -293,7 +293,7 @@ export interface Inputs {
293
293
  }
294
294
 
295
295
  /** Which refusal a {@link ConfigError} carries. */
296
- export type ErrorKind =
296
+ type ErrorKind =
297
297
  /**
298
298
  * A document that is not one JSON instance of the closed key list: a syntax
299
299
  * error, a value of the wrong type, a value outside the closed set a key