@cplieger/deadset-ts 8.0.0-dev.15 → 8.0.0-dev.16

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 (134) hide show
  1. package/dist/src/analysis.js +41 -37
  2. package/dist/src/commands/read.js +6 -7
  3. package/dist/src/conformance-record.js +1 -1
  4. package/dist/src/consumers.js +3 -1
  5. package/dist/src/contract/config.generated.js +1 -1
  6. package/dist/src/contract/contract.generated.js +119 -2
  7. package/dist/src/dependencies.js +79 -48
  8. package/dist/src/entries/build-scripts.js +222 -0
  9. package/dist/src/{configuration-files.js → entries/configuration-files.js} +20 -58
  10. package/dist/src/{convention-rows.js → entries/convention-rows.js} +120 -27
  11. package/dist/src/entries/conventions.js +739 -0
  12. package/dist/src/{html-entries.js → entries/html-entries.js} +3 -3
  13. package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +2 -2
  14. package/dist/src/entries/run-directories.js +77 -0
  15. package/dist/src/entries/sources.js +144 -0
  16. package/dist/src/entries/static-module.js +669 -0
  17. package/dist/src/entries/test-runners.js +684 -0
  18. package/dist/src/{workflow-steps.js → entries/workflow-steps.js} +18 -8
  19. package/dist/src/extensions.js +37 -0
  20. package/dist/src/facts.js +38 -0
  21. package/dist/src/findings/emitters.js +5 -8
  22. package/dist/src/findings/self-check.js +36 -20
  23. package/dist/src/findings-pass.js +5 -6
  24. package/dist/src/framework-lifecycle.js +1 -1
  25. package/dist/src/fs/manifest.js +15 -20
  26. package/dist/src/generated-file.js +172 -29
  27. package/dist/src/graph.js +2 -4
  28. package/dist/src/index.js +1 -1
  29. package/dist/src/injection-container.js +1 -1
  30. package/dist/src/inventory/commonjs.js +62 -17
  31. package/dist/src/inventory/enumerate.js +2 -1
  32. package/dist/src/javascript-file.js +26 -11
  33. package/dist/src/load/classify.js +28 -9
  34. package/dist/src/load/diagnostics.js +8 -1
  35. package/dist/src/load/discover.js +15 -7
  36. package/dist/src/load/plan.js +2 -2
  37. package/dist/src/load/synthesize.js +5 -5
  38. package/dist/src/load/workspace-manifests.js +1 -1
  39. package/dist/src/matrix.js +6 -13
  40. package/dist/src/outside-tests.js +1 -1
  41. package/dist/src/paths.js +16 -0
  42. package/dist/src/platform-calls.js +78 -0
  43. package/dist/src/platform-symbols.js +140 -0
  44. package/dist/src/references.js +202 -32
  45. package/dist/src/resolve.js +1 -1
  46. package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +4 -4
  47. package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +5 -8
  48. package/dist/src/{manifest.js → roots/manifest.js} +4 -4
  49. package/dist/src/roots/published-reach.js +412 -0
  50. package/dist/src/roots/root-set.js +489 -0
  51. package/dist/src/roots/unseen-loads.js +208 -0
  52. package/dist/src/serialization-contract.js +1 -1
  53. package/dist/src/source-files.js +5 -9
  54. package/dist/src/suppress.js +30 -32
  55. package/dist/src/test-support.js +4 -2
  56. package/dist/src/verbs/analyze.js +13 -1
  57. package/dist/src/verbs/describe.js +5 -3
  58. package/jsr.json +1 -1
  59. package/package.json +2 -2
  60. package/src/analysis.ts +75 -52
  61. package/src/automatic-types.ts +1 -1
  62. package/src/commands/read.ts +6 -8
  63. package/src/conformance-record.ts +1 -1
  64. package/src/consumers.ts +4 -0
  65. package/src/contract/config.generated.ts +1 -1
  66. package/src/contract/contract.generated.ts +138 -2
  67. package/src/dependencies.ts +96 -49
  68. package/src/entries/build-scripts.ts +292 -0
  69. package/src/{configuration-files.ts → entries/configuration-files.ts} +23 -75
  70. package/src/{convention-rows.ts → entries/convention-rows.ts} +167 -28
  71. package/src/{conventions.ts → entries/conventions.ts} +347 -522
  72. package/src/{html-entries.ts → entries/html-entries.ts} +5 -5
  73. package/src/{mdx-entries.ts → entries/mdx-entries.ts} +4 -4
  74. package/src/entries/run-directories.ts +108 -0
  75. package/src/entries/sources.ts +266 -0
  76. package/src/entries/static-module.ts +857 -0
  77. package/src/entries/test-runners.ts +868 -0
  78. package/src/{workflow-steps.ts → entries/workflow-steps.ts} +29 -11
  79. package/src/exempt.ts +3 -2
  80. package/src/extensions.ts +45 -0
  81. package/src/facts.ts +44 -0
  82. package/src/file-facts.ts +1 -1
  83. package/src/findings/emitters.ts +14 -10
  84. package/src/findings/self-check.ts +41 -22
  85. package/src/findings/visibility-narrowing.ts +1 -1
  86. package/src/findings-pass.ts +5 -12
  87. package/src/framework-lifecycle.ts +1 -1
  88. package/src/fs/manifest.ts +25 -21
  89. package/src/generated-file.ts +240 -36
  90. package/src/graph.ts +3 -5
  91. package/src/index.ts +1 -1
  92. package/src/injection-container.ts +1 -1
  93. package/src/inventory/commonjs.ts +102 -17
  94. package/src/inventory/enumerate.ts +2 -1
  95. package/src/javascript-file.ts +29 -12
  96. package/src/load/classify.ts +49 -13
  97. package/src/load/diagnostics.ts +11 -1
  98. package/src/load/discover.ts +27 -11
  99. package/src/load/plan.ts +11 -2
  100. package/src/load/synthesize.ts +6 -6
  101. package/src/load/test-globals.ts +1 -1
  102. package/src/load/workspace-manifests.ts +1 -1
  103. package/src/load/workspace-run.ts +1 -1
  104. package/src/matrix.ts +7 -14
  105. package/src/outside-tests.ts +1 -1
  106. package/src/paths.ts +18 -0
  107. package/src/platform-calls.ts +119 -0
  108. package/src/platform-symbols.ts +205 -0
  109. package/src/references.ts +281 -34
  110. package/src/report.ts +1 -1
  111. package/src/resolve.ts +1 -1
  112. package/src/{configured-declarations.ts → roots/configured-declarations.ts} +7 -7
  113. package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +5 -8
  114. package/src/{manifest.ts → roots/manifest.ts} +5 -5
  115. package/src/roots/published-reach.ts +481 -0
  116. package/src/{roots.ts → roots/root-set.ts} +203 -630
  117. package/src/roots/unseen-loads.ts +307 -0
  118. package/src/serialization-contract.ts +1 -1
  119. package/src/source-files.ts +5 -10
  120. package/src/suppress.ts +48 -33
  121. package/src/test-support.ts +8 -3
  122. package/src/verbs/analyze.ts +17 -4
  123. package/src/verbs/describe.ts +5 -2
  124. package/src/verbs/explain.ts +1 -1
  125. package/dist/src/build-scripts.js +0 -418
  126. package/dist/src/conventions.js +0 -907
  127. package/dist/src/entry-points.js +0 -444
  128. package/dist/src/own-body.js +0 -28
  129. package/dist/src/roots.js +0 -865
  130. package/dist/src/unseen-loads.js +0 -189
  131. package/src/build-scripts.ts +0 -536
  132. package/src/entry-points.ts +0 -584
  133. package/src/own-body.ts +0 -42
  134. package/src/unseen-loads.ts +0 -282
@@ -12,7 +12,7 @@ import { SignatureKind, SymbolFlags, TypeFlags, } from "@typescript/native/sync"
12
12
  import { aliasChains } from "./alias-chain.js";
13
13
  import { calleeName, resolvedTargets, valuesPassed } from "./calls.js";
14
14
  import { classMembers, memberComponent, typesAt } from "./class-members.js";
15
- import { names, namesADeclaration, resolveEntries, spelledEntry, } from "./configured-declarations.js";
15
+ import { names, namesADeclaration, resolveEntries, spelledEntry, } from "./roots/configured-declarations.js";
16
16
  import { nodeKey } from "./inventory.js";
17
17
  import { renderPosition } from "./position.js";
18
18
  import { must, Unanswerable } from "./query.js";
@@ -5,12 +5,11 @@
5
5
  */
6
6
  import { commonDirectory, emittedFrom } from "./load/emit-map.js";
7
7
  import { dirnamePath, joinPath, relativePath, resolvePath } from "./paths.js";
8
+ import { OUTPUT_EXTENSIONS } from "./extensions.js";
8
9
  /** The one character a subpath pattern of a manifest's `exports` holds. */
9
10
  const SUBPATH_WILDCARD = "*";
10
11
  /** The extension a source file's name drops when the project writes it out. */
11
12
  const SOURCE_EXTENSION = /\.[cm]?[jt]sx?$/u;
12
- /** The extensions the project may write a source file's output under. */
13
- const OUTPUT_EXTENSIONS = [".js", ".mjs", ".cjs", ".jsx", ".d.ts", ".d.mts", ".d.cts"];
14
13
  /**
15
14
  * Whether one path is what a subpath pattern stands for: the text before the
16
15
  * wildcard, any run of characters including the solidus, then the text after it.
@@ -43,13 +42,10 @@ function layoutOf(project, fileNames) {
43
42
  };
44
43
  }
45
44
  /**
46
- * The lookups over one project's own files.
47
- *
48
- * A path that names an emitted file is read back to the source the project would
49
- * emit it from through the project's own emit mapping and nothing else: the
50
- * extension is one the compiler emits from a source extension, and a path below the
51
- * output directory is read below the directory the compiler emits from, which the
52
- * configuration declares or which is the directory every input file shares.
45
+ * The lookups over one project's own files. A path naming an emitted file is read back to its
46
+ * source through the project's own emit mapping alone: the extension is one the compiler emits
47
+ * from a source extension, and a path below the output directory is read below the directory
48
+ * the compiler emits from, which the configuration declares or every input file shares.
53
49
  */
54
50
  export function sourceFilesOf(project, targetRoot) {
55
51
  const byName = new Map();
@@ -4,9 +4,9 @@
4
4
  * ignore entry or a baseline row. A bound record marks its declaration live before the
5
5
  * sweep, unless its code reports a part or a dependency row, and is in effect when it
6
6
  * withholds a finding its code would have produced there, dormant when the configuration
7
- * withholds every such finding anyway, and stale otherwise.
7
+ * or a hold withholds every such finding anyway, and stale otherwise.
8
8
  */
9
- import { KIND_ROWS, SYMBOL_SUBJECTS, } from "./contract/contract.generated.js";
9
+ import { KIND_ROWS, REMEDIES, SYMBOL_SUBJECTS, } from "./contract/contract.generated.js";
10
10
  import { UsageError } from "./usage.js";
11
11
  import { positionKey } from "./position.js";
12
12
  import { isRef } from "./ref.js";
@@ -66,6 +66,8 @@ export function isSymbolRef(value) {
66
66
  export function blankReason(reason) {
67
67
  return reason === undefined || /^[ \t\r\n]*$/u.test(reason);
68
68
  }
69
+ /** No hold. */
70
+ const NO_HOLDS = { withholds: () => false, holdsRecord: () => false };
69
71
  /** The subject of the kind each live code names. */
70
72
  const SUBJECT_OF_CODE = new Map(KIND_ROWS.map((row) => [row.code, row.subject]));
71
73
  /** The subject a finding about one symbol kind is about. */
@@ -78,32 +80,25 @@ function subjectOf(kind) {
78
80
  function rowOf(subject) {
79
81
  return subject === "file" || subject === "dependency" || subject === "record";
80
82
  }
81
- /**
82
- * Whether a finding's subject is a row of a document rather than a declaration. Apart
83
- * from a dependency row, nothing withholds a finding about one.
84
- */
83
+ /** Whether a finding's subject is a row of a document rather than a declaration. */
85
84
  export function isRowSubject(kind) {
86
85
  return rowOf(subjectOf(kind));
87
86
  }
88
- /** Whether a code reports a row of a document. */
89
- export function isRowCode(code) {
90
- return rowOf(SUBJECT_OF_CODE.get(code));
87
+ /** The subject a code reports, which kinds.json's remedies table answers for. */
88
+ export function subjectOfCode(code) {
89
+ return SUBJECT_OF_CODE.get(code);
91
90
  }
92
91
  /**
93
- * Whether a finding's subject is a dependency row. An ignore entry or a baseline row
94
- * naming the row's reference and file withholds its finding and marks nothing, and an
95
- * inline directive never binds one.
92
+ * Whether a record of `mechanism` can name a finding about `subject`, as kinds.json's
93
+ * remedies table lists the suppressions of each subject. A subject the table does not
94
+ * know is a declaration's.
96
95
  */
97
- export function isDependencySubject(kind) {
98
- return subjectOf(kind) === "dependency";
99
- }
100
- /** Whether a code reports a dependency row, which only an ignore entry or a baseline row names. */
101
- export function isDependencyCode(code) {
102
- return SUBJECT_OF_CODE.get(code) === "dependency";
96
+ export function names(subject, mechanism) {
97
+ return REMEDIES[subject ?? "declaration"].suppressions.includes(mechanism);
103
98
  }
104
- /** Whether a code reports a row of a document that no record of any mechanism can name. */
105
- export function isUnsuppressibleCode(code) {
106
- return isRowCode(code) && !isDependencyCode(code);
99
+ /** Whether a finding about a symbol of `kind` is one a record of `mechanism` can name. */
100
+ export function namesKind(kind, mechanism) {
101
+ return names(subjectOf(kind), mechanism);
107
102
  }
108
103
  /**
109
104
  * Whether a finding's subject is a part of the declaration its reference names: a part
@@ -136,14 +131,13 @@ export function pairs(record, finding) {
136
131
  if (record.code !== finding.code) {
137
132
  return false;
138
133
  }
139
- if (isDependencySubject(finding.symbol.kind)) {
140
- return (record.mechanism !== "inline" &&
141
- record.symbol === finding.symbol.ref &&
142
- record.path === finding.position.path);
143
- }
144
- if (isRowSubject(finding.symbol.kind)) {
134
+ const subject = subjectOf(finding.symbol.kind);
135
+ if (!names(subject, record.mechanism)) {
145
136
  return false;
146
137
  }
138
+ if (subject === "dependency") {
139
+ return record.symbol === finding.symbol.ref && record.path === finding.position.path;
140
+ }
147
141
  if (record.bound !== "" && record.bound === positionKey(finding.position)) {
148
142
  return true;
149
143
  }
@@ -155,10 +149,10 @@ export function pairs(record, finding) {
155
149
  * there: swept with no mark for a liveness kind, and as swept otherwise. The first record
156
150
  * in reading order claims a finding, so a second naming it is stale; a part-kind record
157
151
  * claims every finding of its code about a part of its declaration. A record is dormant
158
- * when the dials withhold every finding it claims, read from those findings alone, or when
159
- * it claims none and its code's severity is `allow`.
152
+ * when the dials or `holds` withhold every finding it claims, or when it claims none and
153
+ * its code is at `allow` or a hold keeps its declaration live.
160
154
  */
161
- export function ledgerOf(records, would, dials) {
155
+ export function ledgerOf(records, would, dials, holds = NO_HOLDS) {
162
156
  const taken = new Set();
163
157
  const verdicts = [];
164
158
  for (const record of records) {
@@ -166,13 +160,17 @@ export function ledgerOf(records, would, dials) {
166
160
  const first = would.find(open);
167
161
  const claimed = first === undefined ? [] : isPartKind(record.code) ? would.filter(open) : [first];
168
162
  if (claimed.length === 0) {
169
- verdicts.push(record.bound !== "" && dials.allows(record.code) ? "dormant" : "stale");
163
+ verdicts.push(record.bound !== "" && (dials.allows(record.code) || holds.holdsRecord(record))
164
+ ? "dormant"
165
+ : "stale");
170
166
  continue;
171
167
  }
172
168
  for (const finding of claimed) {
173
169
  taken.add(claimKey(finding));
174
170
  }
175
- verdicts.push(claimed.every((finding) => dials.withholds(finding)) ? "dormant" : "in-effect");
171
+ verdicts.push(claimed.every((finding) => dials.withholds(finding) || holds.withholds(finding))
172
+ ? "dormant"
173
+ : "in-effect");
176
174
  }
177
175
  return {
178
176
  verdicts,
@@ -5,7 +5,7 @@
5
5
  * another such file, so it is judged with the tests: what it references is referenced by
6
6
  * test code.
7
7
  */
8
- import { CONVENTION_ROWS, TEST_LIBRARIES, TEST_LIBRARY_SCOPES } from "./convention-rows.js";
8
+ import { CONVENTION_ROWS, TEST_LIBRARIES, TEST_LIBRARY_SCOPES } from "./entries/convention-rows.js";
9
9
  import { packageOfSpecifier } from "./dependencies.js";
10
10
  /** The root kind that never takes a file out of test-support code: the skip only withholds. */
11
11
  const SKIP_ROOT = "type-error";
@@ -14,7 +14,9 @@ const SKIP_ROOT = "type-error";
14
14
  * convention row reads test files for, and the convention table's test libraries.
15
15
  */
16
16
  const TEST_PACKAGES = new Set([
17
- ...CONVENTION_ROWS.filter((row) => row.testFiles !== undefined).map((row) => row.package),
17
+ ...CONVENTION_ROWS.filter((row) => row.testFiles !== undefined ||
18
+ row.testDirectories !== undefined ||
19
+ row.testRunner !== undefined).map((row) => row.package),
18
20
  ...TEST_LIBRARIES,
19
21
  ]);
20
22
  /**
@@ -3,6 +3,7 @@ import { runAnalysis, runBaselineWrite, unansweredCounts, } from "../analysis.js
3
3
  import { kindOf } from "../config.js";
4
4
  import { CONFORMANCE, DECLARED_GAPS } from "../conformance.js";
5
5
  import { analyzerProvenance, verdictOf } from "../findings-pass.js";
6
+ import { factsDocument } from "../facts.js";
6
7
  import { dirnamePath, joinPath, normalizePath, relativePath, resolvePath } from "../paths.js";
7
8
  import { moduleIdentity } from "../load/discover.js";
8
9
  import { partialNotes } from "../partial.js";
@@ -21,7 +22,7 @@ import { diagnose, message } from "../product.js";
21
22
  import { ownVersion } from "./version.js";
22
23
  /** The options `analyze` takes beside the ones every verb takes. */
23
24
  export const ANALYZE_OPTIONS = {
24
- plain: ["report", "baseline-write", "exit-code", "template"],
25
+ plain: ["report", "facts", "baseline-write", "exit-code", "template"],
25
26
  repeatable: ["format"],
26
27
  };
27
28
  /** The two values `--exit-code` takes; on, the default, puts the run's verdict in the exit code. */
@@ -211,8 +212,13 @@ export const analyzeVerb = ({ err, host, inputs, scope, openClient, option, repe
211
212
  const { config, provenance } = resolve(inputs());
212
213
  const invoked = host.workingDirectory();
213
214
  const baseline = option("baseline-write");
215
+ const facts = option("facts");
216
+ if (facts === "") {
217
+ throw new UsageError("--facts names the path the facts document is written to, and named none");
218
+ }
214
219
  const documents = {
215
220
  report: resolvePath(invoked, reportPath),
221
+ facts: facts === undefined ? undefined : resolvePath(invoked, facts),
216
222
  formats: formatsOf(repeated("format"), config.reporters.formats),
217
223
  baseline: baseline === undefined ? undefined : resolvePath(invoked, baseline),
218
224
  };
@@ -281,6 +287,12 @@ export const analyzeVerb = ({ err, host, inputs, scope, openClient, option, repe
281
287
  throw productFault(`built a report that breaks the report schema at ${refused.pointer || "the document"}: ${refused.keyword}: ${refused.reason}`);
282
288
  }
283
289
  writeDocument(host, documents.report, "name a path the run can write", () => json(report));
290
+ const factsPath = documents.facts;
291
+ if (factsPath !== undefined) {
292
+ writeDocument(host, factsPath, "name a path the run can write", () => [
293
+ factsDocument(version, run.generated),
294
+ ]);
295
+ }
284
296
  const options = {
285
297
  readSource: (path) => host.readFile(joinPath(targetRoot, path)),
286
298
  template,
@@ -1,12 +1,13 @@
1
- import { CONTRACT_VERSION, EXIT_CLEAN, EXIT_USAGE, SCHEMA_VERSIONS, } from "../contract/contract.generated.js";
1
+ import { CONTRACT_VERSION, EXIT_CLEAN, EXIT_USAGE, FACTS_VERSION, SCHEMA_VERSIONS, } from "../contract/contract.generated.js";
2
2
  import { CONFORMANCE } from "../conformance.js";
3
3
  import {} from "./verb.js";
4
4
  import { diagnose, LANGUAGE, PRODUCT } from "../product.js";
5
5
  import { ownVersion } from "./version.js";
6
6
  /**
7
7
  * The document `describe` writes: the analyzer, the Contract version it implements, the
8
- * report schema versions it reads, the languages it claims, and its recorded result over
9
- * the conformance corpus, a member the document omits where no run is recorded.
8
+ * report schema versions it reads, the languages it claims, the facts version it writes,
9
+ * and its recorded result over the conformance corpus, a member the document omits where
10
+ * no run is recorded.
10
11
  */
11
12
  export function describeDocument(version, recorded) {
12
13
  const document = {
@@ -15,6 +16,7 @@ export function describeDocument(version, recorded) {
15
16
  contract_version: CONTRACT_VERSION,
16
17
  schema_versions_accepted: SCHEMA_VERSIONS,
17
18
  languages: [LANGUAGE],
19
+ facts_version_written: FACTS_VERSION,
18
20
  ...(recorded === undefined
19
21
  ? {}
20
22
  : {
package/jsr.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/deadset-ts",
3
- "version": "8.0.0-dev.15",
3
+ "version": "8.0.0-dev.16",
4
4
  "license": "GPL-3.0-or-later",
5
5
  "exports": {
6
6
  ".": "./src/index.ts"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/deadset-ts",
3
- "version": "8.0.0-dev.15",
3
+ "version": "8.0.0-dev.16",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Deterministic dead-code analysis for TypeScript, class and type members included",
@@ -32,7 +32,7 @@
32
32
  "lint:fix": "eslint . --fix && prettier --write ."
33
33
  },
34
34
  "dependencies": {
35
- "@typescript/native": "npm:typescript@7.1.0-dev.20261029.1"
35
+ "@typescript/native": "npm:typescript@7.1.0-dev.20261103.1"
36
36
  },
37
37
  "overrides": {
38
38
  "qs": "^6.15.2"
package/src/analysis.ts CHANGED
@@ -14,17 +14,19 @@ import {
14
14
  configuredDeclarations,
15
15
  configuredRef,
16
16
  declarationsNamed,
17
- } from "./configured-declarations.ts";
17
+ } from "./roots/configured-declarations.ts";
18
18
  import { decorator } from "./decorator.ts";
19
19
  import { configurationNeeds, dependenciesOf, projectNeeds } from "./dependencies.ts";
20
20
  import { deprecatedDeclarations, type Deprecation } from "./deprecation.ts";
21
21
  import { moduleIdentity, type LoadedConsumer, type NotBuilt } from "./load/discover.ts";
22
22
  import type { LibraryUnfound } from "./load/classify.ts";
23
23
  import { loadRun, type LoadedRun } from "./load/load.ts";
24
- import { generatedFiles, type AppliedConvention } from "./conventions.ts";
24
+ import type { AppliedConvention } from "./entries/conventions.ts";
25
+ import { readSources, type SourceFacts } from "./entries/sources.ts";
26
+ import { readWorkflows, type Workflows } from "./entries/workflow-steps.ts";
25
27
  import { readEdgeSides } from "./edges.ts";
26
28
  import { enumGroup } from "./enum-group.ts";
27
- import { generatedFile, generatedFilesOf } from "./generated-file.ts";
29
+ import { generatedFile, generatedFilesOf, type GeneratedFact } from "./generated-file.ts";
28
30
  import { inOperandNames } from "./in-operands.ts";
29
31
  import { sourceFilesOf } from "./source-files.ts";
30
32
  import {
@@ -53,7 +55,7 @@ import { interfaceSatisfaction, settledSatisfaction } from "./interface-satisfac
53
55
  import { intraFunctionFacts, projectParts, type ProjectParts } from "./intra-function-parts.ts";
54
56
  import { type Inventory, type InventorySymbol } from "./inventory.ts";
55
57
  import { inventory } from "./inventory/enumerate.ts";
56
- import { readManifest, type ManifestEntry } from "./manifest.ts";
58
+ import { readManifest, type ManifestEntry } from "./roots/manifest.ts";
57
59
  import {
58
60
  liveFilesAt,
59
61
  matrixOf,
@@ -74,22 +76,20 @@ import {
74
76
  import type { UnansweredQuestion } from "./query.ts";
75
77
  import { byPosition, isComponentFile, type Position } from "./position.ts";
76
78
  import type { TypeErrorSkip, UnansweredCount } from "./report.ts";
77
- import { skippedRoots, skippedUnits, type SkippedUnit } from "./type-errors.ts";
79
+ import { skippedUnits, type SkippedUnit } from "./type-errors.ts";
78
80
  import { reflectiveLookup } from "./reflective-lookup.ts";
79
81
  import { references, testFileRulesOf, type Reference, type TestFileRule } from "./references.ts";
80
82
  import {
81
- roots,
83
+ rootSet,
82
84
  unmatchedEverywhere,
83
- type Root,
84
85
  type RootKind,
85
86
  type Roots,
86
87
  type HeldScripts,
87
- } from "./roots.ts";
88
+ } from "./roots/root-set.ts";
88
89
  import type { Scope } from "./scope.ts";
89
90
  import { decodedMembers, serializationContract, type Decoded } from "./serialization-contract.ts";
90
91
  import { outsideTestReferences, testFilesAt, type OutsideTest } from "./outside-tests.ts";
91
- import { mergedGlobalInterfaces } from "./augmentations.ts";
92
- import { unseenLoadsOf, type UnseenLoad } from "./unseen-loads.ts";
92
+ import type { UnseenLoad } from "./roots/unseen-loads.ts";
93
93
  import type { UnresolvedSpecifier } from "./load/specifiers.ts";
94
94
  import type { UnreadInput } from "./load/plan.ts";
95
95
  import { GLOBALS_RUNNER, namesTestGlobals } from "./load/test-globals.ts";
@@ -103,6 +103,7 @@ import type { Exemption, Mode, SweepInput } from "./sweep.ts";
103
103
  import { readTemplates, templateField } from "./template-field.ts";
104
104
  import { bytewise } from "./text/compare.ts";
105
105
  import { holdsManifest, manifestAt } from "./fs/manifest.ts";
106
+ import { runDirectories } from "./entries/run-directories.ts";
106
107
  import { globExpression } from "./text/glob.ts";
107
108
 
108
109
  /** The setting whose source decides where a finding about a configured root sits. */
@@ -176,10 +177,10 @@ interface ProjectRead {
176
177
  readonly typeErrorSkips: readonly TypeErrorSkip[];
177
178
  /** The lines each of those skips withholds every finding on. */
178
179
  readonly skipped: readonly SkippedUnit[];
179
- /** The project's generated files, by path below the target root, each with the detail its retentions name. */
180
- readonly generated: ReadonlyMap<string, string>;
181
- /** The files, absolute, an applied convention row names as test files. */
182
- readonly testFileNames: ReadonlySet<string>;
180
+ /** The project's generated files, by path below the target root, each with what decided it. */
181
+ readonly generated: ReadonlyMap<string, GeneratedFact>;
182
+ /** What the project's entry sources enter and name, read once. */
183
+ readonly sources: SourceFacts;
183
184
  }
184
185
 
185
186
  /** One target project the run analyzes, as its stage is handed it. */
@@ -196,6 +197,8 @@ interface ProjectsRead<Answer> {
196
197
  readonly references: readonly Reference[];
197
198
  /** The test files below the target root no project the run reads holds, parsed. */
198
199
  readonly outsideTests: readonly OutsideTest[];
200
+ /** The target root's workflow steps, git hooks and makefile, read once for every project. */
201
+ readonly workflows: Workflows;
199
202
  }
200
203
 
201
204
  /**
@@ -216,14 +219,13 @@ function readProjects<Answer>(
216
219
  const parse = (fileName: string, text: string) => engine.parseSourceFile(fileName, text);
217
220
  return loadRun(engine, host, scope, config, ({ plan, classified, runFiles }) => {
218
221
  const { packages, conventions } = plan;
222
+ const workflows = readWorkflows(host, targetRoot);
219
223
  const options = {
220
224
  host,
221
225
  manifest: packages.manifest,
222
226
  otherPackages: packages.entries,
223
227
  patterns: config.roots.patterns,
224
228
  entryFiles: config.ts.entry_files,
225
- conventions,
226
- testFiles: config.ts.test_files,
227
229
  parse,
228
230
  publishedAPI: config.target.kind !== undefined && kindOf(config.target.kind) === "library",
229
231
  publishedMembers: packages.published,
@@ -233,19 +235,17 @@ function readProjects<Answer>(
233
235
  const answers = classified.analyzed.map(({ plan: planned, project, skips, referenceOnly }) => {
234
236
  const read = (): ProjectRead => {
235
237
  const held = inventory(project, host, targetRoot);
236
- const found = roots(project, held, targetRoot, options);
237
- const kept = skippedRoots(project, held, skips);
238
- const rooted =
239
- kept.length === 0
240
- ? found
241
- : {
242
- ...found,
243
- liveUnderReachability: [
244
- ...found.liveUnderReachability,
245
- ...kept.map((id) => ({ id, kind: "type-error" as const, source: "" })),
246
- ],
247
- };
248
238
  const own = sourceFilesOf(project, targetRoot);
239
+ const sources = readSources({
240
+ host,
241
+ targetRoot,
242
+ files: own,
243
+ parse,
244
+ testFiles: config.ts.test_files,
245
+ conventions,
246
+ workflows,
247
+ });
248
+ const rooted = rootSet(project, held, targetRoot, { ...options, sources, skips });
249
249
  return {
250
250
  configuration: planned.id,
251
251
  configFile: project.configFile,
@@ -257,8 +257,8 @@ function readProjects<Answer>(
257
257
  .ownSourceFiles()
258
258
  .filter((file) => referenceOnly(file.fileName))
259
259
  .flatMap((file) => relativePath(absoluteRoot, file.fileName) ?? []),
260
- generated: generatedFilesOf(own, generatedFiles(conventions, own)),
261
- testFileNames: conventions.testFiles,
260
+ generated: generatedFilesOf(own, sources.generated),
261
+ sources,
262
262
  };
263
263
  };
264
264
  for (const file of project.ownSourceFiles()) {
@@ -269,7 +269,7 @@ function readProjects<Answer>(
269
269
  const consumed = new Map<string, Reference>();
270
270
  for (const { project, consumer } of classified.consumed) {
271
271
  const side = { id: consumer.id, root: consumer.path };
272
- for (const reference of consumerReferences(project, host, targetRoot, side, config)) {
272
+ for (const reference of consumerReferences(project, host, targetRoot, side, config, parse)) {
273
273
  consumed.set(referenceKey(reference), reference);
274
274
  }
275
275
  }
@@ -281,7 +281,7 @@ function readProjects<Answer>(
281
281
  ),
282
282
  parse,
283
283
  );
284
- return { answers, references: [...consumed.values()], outsideTests };
284
+ return { answers, references: [...consumed.values()], outsideTests, workflows };
285
285
  });
286
286
  }
287
287
 
@@ -463,8 +463,6 @@ interface ReadProject<Extra> {
463
463
  readonly componentWarnings: readonly ComponentWarning[];
464
464
  /** Per own file below the target root, the module specifiers its imports write. */
465
465
  readonly imports: ReadonlyMap<string, readonly string[]>;
466
- /** The global augmentations a declaration outside the project names. */
467
- readonly augmented: readonly Root[];
468
466
  /** The data members its production code decodes into. */
469
467
  readonly decoded: Decoded;
470
468
  readonly extra: Extra;
@@ -499,6 +497,8 @@ interface ReadRun<Extra> {
499
497
  readonly memberDirs: readonly string[];
500
498
  /** The directory of every nested package, absolute. */
501
499
  readonly nestedDirs: readonly string[];
500
+ /** The target root's workflow steps, git hooks and makefile, read once. */
501
+ readonly workflows: Workflows;
502
502
  }
503
503
 
504
504
  /** The manifest section of a package test evidence counts, and the sections that keep one out of it. */
@@ -564,6 +564,7 @@ function readRun<Extra>(
564
564
  read: ProjectRead,
565
565
  references: readonly Reference[],
566
566
  testFiles: readonly string[],
567
+ unseenLoads: readonly UnseenLoad[],
567
568
  ) => Extra,
568
569
  ): ReadRun<Extra> {
569
570
  const targetRoot = scope.target.path;
@@ -572,11 +573,15 @@ function readRun<Extra>(
572
573
  ? { delimiters: config.analysis.template_delimiters, files: [] }
573
574
  : readTemplates(host, targetRoot, config.analysis);
574
575
  const consumers = scope.consumers.map((consumer) => consumer.path);
576
+ const directories = runDirectories(host, targetRoot, (fileName, text) =>
577
+ engine.parseSourceFile(fileName, text),
578
+ );
575
579
  const read = readProjects<ReadProject<Extra>>(engine, host, scope, config, (project, opened) => {
576
580
  const projectRead = opened.read();
577
581
  const resolved = references(project, projectRead.held, targetRoot, {
578
582
  testFiles: config.ts.test_files,
579
- testFileNames: projectRead.testFileNames,
583
+ testFileNames: projectRead.sources.testFiles,
584
+ directories,
580
585
  });
581
586
  const evidence = evidenceRecords(
582
587
  {
@@ -605,11 +610,6 @@ function readRun<Extra>(
605
610
  testFiles: resolved.testFilePaths,
606
611
  evidence,
607
612
  componentFiles: components.map(pathOf),
608
- augmented: mergedGlobalInterfaces(
609
- project,
610
- projectRead.held,
611
- sourceFilesOf(project, targetRoot),
612
- ).map((id): Root => ({ id, kind: "global-augmentation", source: "" })),
613
613
  imports: new Map(
614
614
  project.ownSourceFiles().flatMap((file) => {
615
615
  const path = relativePath(targetRoot, file.fileName);
@@ -641,7 +641,16 @@ function readRun<Extra>(
641
641
  productionFiles(project, targetRoot, resolved.testFilePaths),
642
642
  consumers,
643
643
  ),
644
- extra: extra(project, projectRead, resolved.references, resolved.testFilePaths),
644
+ extra: extra(
645
+ project,
646
+ projectRead,
647
+ resolved.references,
648
+ resolved.testFilePaths,
649
+ resolved.unseenLoads.map((position) => ({
650
+ configuration: projectRead.configuration,
651
+ position,
652
+ })),
653
+ ),
645
654
  };
646
655
  });
647
656
  const outsidePaths = read.result.outsideTests.map((one) => one.path);
@@ -658,7 +667,12 @@ function readRun<Extra>(
658
667
  const { support, testUsed } = testSupportFiles(
659
668
  withOutside.flatMap((one) => one.projectRead.held.symbols),
660
669
  withOutside.flatMap((one) => one.references),
661
- withOutside.flatMap((one) => one.projectRead.rooted.liveUnderReachability),
670
+ // An outside declaration naming a global augmentation calls nothing of its file.
671
+ withOutside.flatMap((one) =>
672
+ one.projectRead.rooted.liveUnderReachability.filter(
673
+ (root) => root.kind !== "global-augmentation",
674
+ ),
675
+ ),
662
676
  withOutside.flatMap((one) => one.testFiles),
663
677
  testEvidence(host, targetRoot, withOutside),
664
678
  );
@@ -699,9 +713,7 @@ function readRun<Extra>(
699
713
  configuration: projectRead.configuration,
700
714
  symbols: projectRead.held.symbols,
701
715
  references: [...made, ...evidenceReferences(projectRead.held.symbols, detected.inTestFiles)],
702
- // The augmentations are rooted after test support is read: an outside declaration
703
- // naming one calls nothing of its file.
704
- roots: [...projectRead.rooted.liveUnderReachability, ...one.augmented],
716
+ roots: projectRead.rooted.liveUnderReachability,
705
717
  testFiles: [...one.testFiles, ...supportFiles],
706
718
  ...(supportFiles.length === 0 ? {} : { supportFiles }),
707
719
  ...(testUsedFiles.length === 0 ? {} : { testUsedFiles }),
@@ -769,6 +781,7 @@ function readRun<Extra>(
769
781
  conventionUses: read.plan.conventions.uses,
770
782
  memberDirs: read.plan.packages.members,
771
783
  nestedDirs: read.plan.packages.nested,
784
+ workflows: read.result.workflows,
772
785
  };
773
786
  }
774
787
 
@@ -958,7 +971,7 @@ interface EmitterExtra {
958
971
  readonly needs: ReturnType<typeof projectNeeds>;
959
972
  readonly implementations: ReturnType<typeof implementations>;
960
973
  readonly heldByInclusion: readonly string[];
961
- readonly generated: readonly string[];
974
+ readonly generated: ReadonlyMap<string, GeneratedFact>;
962
975
  readonly inOperands: readonly string[];
963
976
  readonly rooted: Roots;
964
977
  readonly directives: readonly InlineDirective[];
@@ -996,15 +1009,21 @@ export function readEmitterRun(
996
1009
  config,
997
1010
  mode,
998
1011
  detectors,
999
- (project, read, references, testFiles) => ({
1012
+ (project, read, references, testFiles, unseenLoads) => ({
1000
1013
  deprecation: deprecatedDeclarations(project, read.held),
1001
1014
  accessors: accessorsOf(project, read.held),
1002
- needs: projectNeeds(project, read.held, host, targetRoot, (fileName, text) =>
1003
- engine.parseSourceFile(fileName, text),
1015
+ needs: projectNeeds(
1016
+ project,
1017
+ read.held,
1018
+ host,
1019
+ targetRoot,
1020
+ (fileName, text) => engine.parseSourceFile(fileName, text),
1021
+ read.sources.configuration,
1022
+ read.sources.workflows,
1004
1023
  ),
1005
1024
  implementations: implementations(project, read.held),
1006
1025
  heldByInclusion: heldByInclusion(project, targetRoot),
1007
- generated: [...read.generated.keys()],
1026
+ generated: read.generated,
1008
1027
  inOperands: inOperandNames(project, targetRoot, testFiles),
1009
1028
  rooted: read.rooted,
1010
1029
  directives: inlineDirectives(project, read.held, targetRoot).filter(
@@ -1012,7 +1031,7 @@ export function readEmitterRun(
1012
1031
  ),
1013
1032
  declarationsNamed: declarationsNamed(project, read.held, configuredDeclarations(config)),
1014
1033
  parts: projectParts(project, read.held, references, targetRoot),
1015
- unseenLoads: unseenLoadsOf(project, read.configuration, targetRoot, testFiles),
1034
+ unseenLoads,
1016
1035
  }),
1017
1036
  );
1018
1037
  // Whether a declaration carries the marker decides its code, so one whose answer went
@@ -1101,6 +1120,7 @@ export function emitterInputOver(
1101
1120
  dependencies: dependenciesOf(
1102
1121
  host,
1103
1122
  targetRoot,
1123
+ read.workflows,
1104
1124
  [...projects.map((one) => one.extra.needs), configurationNeeds(read.conventionUses)],
1105
1125
  swept.sweep.candidates.map((candidate) => candidate.id),
1106
1126
  members.read,
@@ -1111,7 +1131,7 @@ export function emitterInputOver(
1111
1131
  files: {
1112
1132
  tree: sourceTree(host, targetRoot),
1113
1133
  heldByInclusion: new Set(projects.flatMap((one) => one.extra.heldByInclusion)),
1114
- generated: new Set(projects.flatMap((one) => one.extra.generated)),
1134
+ generated: new Set(projects.flatMap((one) => [...one.extra.generated.keys()])),
1115
1135
  inOperands: new Set(projects.flatMap((one) => one.extra.inOperands)),
1116
1136
  unseenLoads: projects.flatMap((one) => one.extra.unseenLoads),
1117
1137
  },
@@ -1211,6 +1231,7 @@ function readAnalysis(
1211
1231
  one.heldScripts.map((held) => ({ ...held, configFile: one.configFile })),
1212
1232
  ),
1213
1233
  unseenLoads: read.projects.flatMap((one) => one.extra.unseenLoads),
1234
+ generated: new Map(read.projects.flatMap((one) => [...one.extra.generated])),
1214
1235
  },
1215
1236
  configured: read.projects.map((one) => one.configured),
1216
1237
  symbols,
@@ -1317,6 +1338,8 @@ interface RunFacts {
1317
1338
  readonly heldScripts: readonly (HeldScripts & { readonly configFile: string })[];
1318
1339
  /** The loads of production files whose target the run cannot resolve, per configuration. */
1319
1340
  readonly unseenLoads: readonly UnseenLoad[];
1341
+ /** Every generated file of the run, by its path below the target root, each once. */
1342
+ readonly generated: ReadonlyMap<string, GeneratedFact>;
1320
1343
  }
1321
1344
 
1322
1345
  /** One configuration whose component files a run cannot read. */
@@ -25,7 +25,7 @@ import {
25
25
  type SourceFile,
26
26
  type Statement,
27
27
  } from "@typescript/native/ast";
28
- import type { ParseFile } from "./configuration-files.ts";
28
+ import type { ParseFile } from "./entries/configuration-files.ts";
29
29
  import type { Host } from "./host.ts";
30
30
  import { ancestors, dirnamePath, joinPath, resolvePath } from "./paths.ts";
31
31
  import { entriesOf } from "./fs/tree.ts";