@cplieger/deadset-ts 7.2.0-dev.4 → 8.0.0-dev.10

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 (288) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/mapper-connection.js +2 -1
  3. package/dist/bin/node-host.js +24 -5
  4. package/dist/src/analysis.js +245 -309
  5. package/dist/src/augmentations.js +124 -18
  6. package/dist/src/automatic-types.js +170 -0
  7. package/dist/src/build-scripts.js +425 -0
  8. package/dist/src/component-layer.js +4 -2
  9. package/dist/src/components.js +9 -9
  10. package/dist/src/config.js +6 -81
  11. package/dist/src/configuration-files.js +78 -188
  12. package/dist/src/configured-declarations.js +15 -20
  13. package/dist/src/conformance-record.js +2 -2
  14. package/dist/src/consumers.js +3 -30
  15. package/dist/src/contextual-uses.js +56 -9
  16. package/dist/src/contract/config.generated.js +558 -0
  17. package/dist/src/contract/contract.generated.js +1244 -0
  18. package/dist/src/contract/report.generated.js +862 -0
  19. package/dist/src/convention-rows.js +146 -1
  20. package/dist/src/conventions.js +136 -70
  21. package/dist/src/decorator.js +21 -18
  22. package/dist/src/dependencies.js +280 -62
  23. package/dist/src/edges.js +38 -122
  24. package/dist/src/element-access.js +4 -6
  25. package/dist/src/entry-point-gaps.js +9 -18
  26. package/dist/src/entry-points.js +106 -21
  27. package/dist/src/enum-group.js +3 -10
  28. package/dist/src/erased-stores.js +4 -6
  29. package/dist/src/exempt-classes.js +1 -63
  30. package/dist/src/exempt.js +21 -40
  31. package/dist/src/failure.js +36 -0
  32. package/dist/src/file-facts.js +2 -25
  33. package/dist/src/findings/completion.js +225 -63
  34. package/dist/src/findings/dependencies-and-module-machinery.js +72 -27
  35. package/dist/src/findings/emitters.js +6 -6
  36. package/dist/src/findings/interfaces.js +4 -5
  37. package/dist/src/findings/intra-function.js +53 -19
  38. package/dist/src/findings/non-code-artifacts.js +71 -39
  39. package/dist/src/findings/reads-and-writes.js +16 -13
  40. package/dist/src/findings/self-check.js +10 -12
  41. package/dist/src/findings/unused-declarations.js +57 -28
  42. package/dist/src/findings/visibility-narrowing.js +40 -18
  43. package/dist/src/findings-pass.js +8 -15
  44. package/dist/src/framework-lifecycle.js +7 -7
  45. package/dist/src/fs/document.js +52 -0
  46. package/dist/src/fs/manifest.js +72 -0
  47. package/dist/src/fs/tree.js +88 -0
  48. package/dist/src/generated-file.js +18 -4
  49. package/dist/src/git-ignore.js +99 -0
  50. package/dist/src/graph.js +9 -4
  51. package/dist/src/html-entries.js +6 -33
  52. package/dist/src/import-bindings.js +97 -0
  53. package/dist/src/in-operands.js +2 -1
  54. package/dist/src/index.js +1 -1
  55. package/dist/src/indexed-access.js +130 -28
  56. package/dist/src/injection-container.js +1 -1
  57. package/dist/src/interface-satisfaction.js +30 -12
  58. package/dist/src/intra-function-parts.js +341 -69
  59. package/dist/src/inventory.js +54 -29
  60. package/dist/src/javascript-file.js +33 -0
  61. package/dist/src/json-document.js +198 -67
  62. package/dist/src/keyof-comparisons.js +4 -6
  63. package/dist/src/kinds.js +3 -366
  64. package/dist/src/load/classify.js +270 -0
  65. package/dist/src/load/diagnostics.js +80 -0
  66. package/dist/src/load/discover.js +277 -0
  67. package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
  68. package/dist/src/load/expected-module.js +136 -0
  69. package/dist/src/load/load.js +27 -0
  70. package/dist/src/load/module-lookup.js +37 -0
  71. package/dist/src/load/plan.js +313 -0
  72. package/dist/src/load/specifiers.js +115 -0
  73. package/dist/src/load/synthesize.js +140 -0
  74. package/dist/src/load/test-globals.js +207 -0
  75. package/dist/src/load/test-joins.js +82 -0
  76. package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
  77. package/dist/src/{workspace-programs.js → load/workspace-programs.js} +88 -61
  78. package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +60 -59
  79. package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
  80. package/dist/src/{workspace.js → load/workspace.js} +62 -124
  81. package/dist/src/manifest.js +49 -54
  82. package/dist/src/matrix.js +94 -13
  83. package/dist/src/mdx-entries.js +174 -0
  84. package/dist/src/module-declarations.js +9 -13
  85. package/dist/src/outside-tests.js +1 -1
  86. package/dist/src/overrides.js +41 -10
  87. package/dist/src/partial.js +2 -1
  88. package/dist/src/paths.js +26 -15
  89. package/dist/src/position.js +2 -1
  90. package/dist/src/print.js +11 -78
  91. package/dist/src/product.js +13 -0
  92. package/dist/src/query.js +1 -0
  93. package/dist/src/ref.js +11 -0
  94. package/dist/src/references.js +52 -31
  95. package/dist/src/report-schema.js +51 -0
  96. package/dist/src/report.js +59 -41
  97. package/dist/src/reporters/annotations.js +15 -8
  98. package/dist/src/reporters/sarif.js +16 -10
  99. package/dist/src/reporters/template.js +4 -19
  100. package/dist/src/resolve.js +245 -727
  101. package/dist/src/roots.js +374 -116
  102. package/dist/src/run.js +66 -77
  103. package/dist/src/schema.js +320 -342
  104. package/dist/src/scope.js +38 -115
  105. package/dist/src/serialization-contract.js +649 -29
  106. package/dist/src/session.js +41 -29
  107. package/dist/src/source-files.js +40 -15
  108. package/dist/src/suppress-file.js +34 -29
  109. package/dist/src/suppress-inline.js +21 -4
  110. package/dist/src/suppress.js +40 -56
  111. package/dist/src/sweep.js +32 -17
  112. package/dist/src/symbol-keys.js +105 -6
  113. package/dist/src/template-field.js +9 -19
  114. package/dist/src/test-support.js +119 -18
  115. package/dist/src/text/compare.js +26 -0
  116. package/dist/src/text/glob.js +194 -0
  117. package/dist/src/text/named-files.js +54 -0
  118. package/dist/src/text/shell.js +159 -0
  119. package/dist/src/text/yaml.js +393 -0
  120. package/dist/src/type-errors.js +27 -221
  121. package/dist/src/type-parameter-reads.js +172 -0
  122. package/dist/src/unseen-loads.js +100 -0
  123. package/dist/src/unwrap.js +23 -0
  124. package/dist/src/usage.js +10 -0
  125. package/dist/src/verbs/analyze.js +181 -125
  126. package/dist/src/verbs/describe.js +9 -16
  127. package/dist/src/verbs/explain.js +14 -9
  128. package/dist/src/verbs/print-config.js +2 -1
  129. package/dist/src/verbs/print-projects.js +6 -4
  130. package/dist/src/verbs/print-retained.js +4 -2
  131. package/dist/src/verbs/print-roots.js +4 -2
  132. package/dist/src/verbs/verb.js +1 -5
  133. package/dist/src/verbs/version.js +14 -3
  134. package/dist/src/workflow-steps.js +128 -33
  135. package/jsr.json +1 -1
  136. package/package.json +5 -4
  137. package/src/analysis.ts +333 -457
  138. package/src/augmentations.ts +152 -23
  139. package/src/automatic-types.ts +228 -0
  140. package/src/build-scripts.ts +543 -0
  141. package/src/component-layer.ts +4 -2
  142. package/src/components.ts +9 -10
  143. package/src/config.ts +31 -299
  144. package/src/configuration-files.ts +100 -191
  145. package/src/configured-declarations.ts +18 -23
  146. package/src/conformance-record.ts +2 -2
  147. package/src/consumers.ts +6 -57
  148. package/src/contextual-uses.ts +64 -12
  149. package/src/contract/config.generated.ts +818 -0
  150. package/src/contract/contract.generated.ts +1308 -0
  151. package/src/contract/report.generated.ts +1237 -0
  152. package/src/convention-rows.ts +186 -1
  153. package/src/conventions.ts +166 -90
  154. package/src/decorator.ts +28 -18
  155. package/src/dependencies.ts +356 -78
  156. package/src/edges.ts +41 -140
  157. package/src/element-access.ts +4 -7
  158. package/src/entry-point-gaps.ts +11 -20
  159. package/src/entry-points.ts +124 -28
  160. package/src/enum-group.ts +2 -11
  161. package/src/erased-stores.ts +3 -7
  162. package/src/exempt-classes.ts +1 -75
  163. package/src/exempt.ts +36 -50
  164. package/src/failure.ts +63 -0
  165. package/src/file-facts.ts +9 -24
  166. package/src/finding.ts +24 -9
  167. package/src/findings/boundary.ts +2 -0
  168. package/src/findings/completion.ts +297 -77
  169. package/src/findings/dependencies-and-module-machinery.ts +90 -31
  170. package/src/findings/emitter.ts +8 -9
  171. package/src/findings/emitters.ts +8 -8
  172. package/src/findings/interfaces.ts +4 -6
  173. package/src/findings/intra-function.ts +64 -20
  174. package/src/findings/non-code-artifacts.ts +88 -50
  175. package/src/findings/reads-and-writes.ts +20 -16
  176. package/src/findings/self-check.ts +11 -17
  177. package/src/findings/unused-declarations.ts +65 -28
  178. package/src/findings/visibility-narrowing.ts +51 -19
  179. package/src/findings-pass.ts +10 -19
  180. package/src/framework-lifecycle.ts +7 -7
  181. package/src/fs/document.ts +70 -0
  182. package/src/fs/manifest.ts +92 -0
  183. package/src/fs/tree.ts +110 -0
  184. package/src/generated-file.ts +23 -4
  185. package/src/git-ignore.ts +122 -0
  186. package/src/graph.ts +11 -3
  187. package/src/host.ts +17 -14
  188. package/src/html-entries.ts +6 -33
  189. package/src/import-bindings.ts +128 -0
  190. package/src/in-operands.ts +2 -1
  191. package/src/index.ts +1 -1
  192. package/src/indexed-access.ts +152 -30
  193. package/src/injection-container.ts +1 -1
  194. package/src/interface-satisfaction.ts +33 -13
  195. package/src/intra-function-parts.ts +449 -85
  196. package/src/inventory.ts +73 -27
  197. package/src/javascript-file.ts +37 -0
  198. package/src/json-document.ts +241 -76
  199. package/src/keyof-comparisons.ts +3 -7
  200. package/src/kinds.ts +5 -413
  201. package/src/load/classify.ts +397 -0
  202. package/src/load/diagnostics.ts +144 -0
  203. package/src/load/discover.ts +455 -0
  204. package/src/{emit-map.ts → load/emit-map.ts} +1 -1
  205. package/src/load/expected-module.ts +188 -0
  206. package/src/load/load.ts +73 -0
  207. package/src/load/module-lookup.ts +50 -0
  208. package/src/load/plan.ts +504 -0
  209. package/src/load/specifiers.ts +168 -0
  210. package/src/load/synthesize.ts +211 -0
  211. package/src/load/test-globals.ts +251 -0
  212. package/src/load/test-joins.ts +98 -0
  213. package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
  214. package/src/{workspace-programs.ts → load/workspace-programs.ts} +127 -80
  215. package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +73 -64
  216. package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
  217. package/src/{workspace.ts → load/workspace.ts} +90 -134
  218. package/src/manifest.ts +81 -75
  219. package/src/matrix.ts +154 -18
  220. package/src/mdx-entries.ts +218 -0
  221. package/src/module-declarations.ts +11 -13
  222. package/src/outside-tests.ts +1 -1
  223. package/src/overrides.ts +51 -12
  224. package/src/partial.ts +3 -2
  225. package/src/paths.ts +27 -15
  226. package/src/position.ts +2 -1
  227. package/src/print.ts +12 -91
  228. package/src/product.ts +18 -0
  229. package/src/query.ts +11 -0
  230. package/src/ref.ts +13 -0
  231. package/src/references.ts +62 -32
  232. package/src/report-schema.ts +68 -0
  233. package/src/report.ts +105 -215
  234. package/src/reporters/annotations.ts +22 -11
  235. package/src/reporters/json.ts +1 -1
  236. package/src/reporters/reporter.ts +2 -4
  237. package/src/reporters/reporters.ts +1 -1
  238. package/src/reporters/sarif.ts +29 -16
  239. package/src/reporters/template.ts +4 -20
  240. package/src/reporters/text.ts +3 -3
  241. package/src/resolve.ts +283 -1111
  242. package/src/roots.ts +490 -137
  243. package/src/run.ts +75 -91
  244. package/src/schema.ts +391 -387
  245. package/src/scope.ts +46 -138
  246. package/src/serialization-contract.ts +818 -26
  247. package/src/session.ts +77 -54
  248. package/src/source-files.ts +67 -17
  249. package/src/suppress-file.ts +36 -34
  250. package/src/suppress-inline.ts +20 -3
  251. package/src/suppress.ts +52 -63
  252. package/src/sweep.ts +53 -18
  253. package/src/symbol-keys.ts +131 -7
  254. package/src/template-field.ts +15 -25
  255. package/src/test-support.ts +155 -18
  256. package/src/text/compare.ts +27 -0
  257. package/src/text/glob.ts +210 -0
  258. package/src/text/named-files.inputs.json +1522 -0
  259. package/src/text/named-files.ts +68 -0
  260. package/src/text/shell.ts +161 -0
  261. package/src/text/yaml.ts +423 -0
  262. package/src/type-errors.ts +35 -273
  263. package/src/type-parameter-reads.ts +227 -0
  264. package/src/unseen-loads.ts +134 -0
  265. package/src/unwrap.ts +34 -0
  266. package/src/usage.ts +10 -0
  267. package/src/verbs/analyze.ts +245 -161
  268. package/src/verbs/describe.ts +14 -15
  269. package/src/verbs/explain.ts +15 -13
  270. package/src/verbs/print-config.ts +2 -1
  271. package/src/verbs/print-projects.ts +6 -4
  272. package/src/verbs/print-retained.ts +4 -2
  273. package/src/verbs/print-roots.ts +4 -2
  274. package/src/verbs/verb.ts +2 -8
  275. package/src/verbs/version.ts +15 -3
  276. package/src/workflow-steps.ts +149 -34
  277. package/dist/src/discover.js +0 -347
  278. package/dist/src/glob.js +0 -65
  279. package/dist/src/pnpm-workspace.js +0 -120
  280. package/dist/src/setup-failure.js +0 -23
  281. package/dist/src/test-joins.js +0 -101
  282. package/dist/src/version.js +0 -7
  283. package/src/discover.ts +0 -510
  284. package/src/glob.ts +0 -67
  285. package/src/pnpm-workspace.ts +0 -135
  286. package/src/setup-failure.ts +0 -39
  287. package/src/test-joins.ts +0 -120
  288. package/src/version.ts +0 -7
@@ -5,23 +5,24 @@
5
5
  * makes: a view carries a project's identity in its type so a handle cannot cross to
6
6
  * another project's checker, which holds only for views the session hands out.
7
7
  */
8
+ import { isStringLiteralLikeNode } from "@typescript/native/ast";
8
9
  import { lineOf, readComponent } from "./component-files.js";
9
10
  import { kindOf } from "./config.js";
10
- import { consumerLoads, consumerReferences, moduleIdentity, } from "./consumers.js";
11
+ import { consumerReferences } from "./consumers.js";
11
12
  import { configuredDeclarations, configuredRef, declarationsNamed, } from "./configured-declarations.js";
12
13
  import { decorator } from "./decorator.js";
13
14
  import { configurationNeeds, dependenciesOf, projectNeeds } from "./dependencies.js";
14
15
  import { deprecatedDeclarations } from "./deprecation.js";
15
- import { discoverProjects, DiscoveryError, isGuess, projectErrors, renderDiagnostic, targetRelative, } from "./discover.js";
16
- import { conventionFailuresFor, generatedFiles, readConventions, } from "./conventions.js";
16
+ import { moduleIdentity } from "./load/discover.js";
17
+ import { loadRun } from "./load/load.js";
18
+ import { generatedFiles } from "./conventions.js";
17
19
  import { readEdgeSides } from "./edges.js";
18
20
  import { enumGroup } from "./enum-group.js";
19
21
  import { generatedFile, generatedFilesOf } from "./generated-file.js";
20
22
  import { inOperandNames } from "./in-operands.js";
21
23
  import { sourceFilesOf } from "./source-files.js";
22
- import { evidenceRecords, heldRecords, disabledClasses, exemptionsOf, holdingWhile, isUnansweredRecord, retainedIn, } from "./exempt.js";
24
+ import { evidenceRecords, heldRecords, disabledClasses, exemptionsOf, holdingWhile, isUnansweredRecord, retainedIn, writtenRecords, } from "./exempt.js";
23
25
  import { heldByInclusion, sourcePaths, sourceTree } from "./file-facts.js";
24
- import { globExpression } from "./glob.js";
25
26
  import { findingsPass, recordedFindings, rowKey } from "./findings-pass.js";
26
27
  import { unmatchedRootFindings } from "./findings/self-check.js";
27
28
  import { frameworkLifecycle } from "./framework-lifecycle.js";
@@ -30,27 +31,28 @@ import { injectionContainer } from "./injection-container.js";
30
31
  import { interfaceSatisfaction, settledSatisfaction } from "./interface-satisfaction.js";
31
32
  import { intraFunctionFacts, projectParts } from "./intra-function-parts.js";
32
33
  import { inventory } from "./inventory.js";
33
- import { packageEntries, readManifest } from "./manifest.js";
34
+ import { readManifest } from "./manifest.js";
34
35
  import { liveFilesAt, matrixOf, referenceKey, sweepMatrix, } from "./matrix.js";
35
- import { dirnamePath, extensionOf, joinPath, normalizePath, relativePath, resolvePath, } from "./paths.js";
36
+ import { ancestors, dirnamePath, extensionOf, joinPath, relativePath, resolvePath, } from "./paths.js";
36
37
  import { byPosition, isComponentFile } from "./position.js";
37
- import { LANGUAGE } from "./report.js";
38
- import { SetupError, setupLine } from "./setup-failure.js";
39
- import { pathsOf, readErrors, skippedRoots, skippedUnits, } from "./type-errors.js";
38
+ import { skippedRoots, skippedUnits } from "./type-errors.js";
40
39
  import { reflectiveLookup } from "./reflective-lookup.js";
41
40
  import { references, testFileRulesOf } from "./references.js";
42
41
  import { roots, unmatchedEverywhere, } from "./roots.js";
43
- import { serializationContract } from "./serialization-contract.js";
42
+ import { decodedMembers, serializationContract } from "./serialization-contract.js";
44
43
  import { outsideTestReferences, testFilesAt } from "./outside-tests.js";
45
- import { joinedTestFiles, withJoinedTests } from "./test-joins.js";
44
+ import { mergedGlobalInterfaces } from "./augmentations.js";
45
+ import { unseenLoadsOf } from "./unseen-loads.js";
46
+ import { GLOBALS_RUNNER, namesTestGlobals } from "./load/test-globals.js";
46
47
  import { supportReferences, testSupportFiles } from "./test-support.js";
47
- import { diagnosticsOf, ownedAs, runSession, } from "./session.js";
48
48
  import { accessorsOf, storesOf } from "./stores.js";
49
49
  import { isPartKind } from "./suppress.js";
50
50
  import { bindRows, readBaseline, readIgnoreFile } from "./suppress-file.js";
51
51
  import { inlineDirectives, inlineSuppressions } from "./suppress-inline.js";
52
52
  import { readTemplates, templateField } from "./template-field.js";
53
- import { ownErrors, workspaceRun } from "./workspace-run.js";
53
+ import { bytewise } from "./text/compare.js";
54
+ import { holdsManifest, manifestAt } from "./fs/manifest.js";
55
+ import { globExpression } from "./text/glob.js";
54
56
  /** The setting whose source decides where a finding about a configured root sits. */
55
57
  const ROOTS_SETTING = "roots.patterns";
56
58
  /**
@@ -67,273 +69,77 @@ function documentOf(host, provenance, setting, targetRoot) {
67
69
  const document = resolvePath(host.workingDirectory(), origin.label);
68
70
  return relativePath(resolvePath(host.workingDirectory(), targetRoot), document) ?? "";
69
71
  }
70
- /** Two strings ordered bytewise. */
71
- function compare(a, b) {
72
- if (a === b) {
73
- return 0;
74
- }
75
- return a < b ? -1 : 1;
76
- }
77
- /**
78
- * The manifests the analysis reads, each once: the target's own, then every workspace
79
- * member's and every nested package's below the target root.
80
- */
81
- function conventionManifests(host, targetRoot, memberDirs, nestedDirs) {
82
- const found = new Map();
83
- if (host.kindOf(joinPath(targetRoot, "package.json")) === "file") {
84
- found.set(targetRoot, { dir: targetRoot, path: "package.json" });
85
- }
86
- for (const [dirs, nested] of [
87
- [memberDirs, false],
88
- [nestedDirs, true],
89
- ]) {
90
- for (const dir of dirs) {
91
- const below = relativePath(targetRoot, dir);
92
- if (below !== undefined && !found.has(dir)) {
93
- found.set(dir, { dir, path: `${below}/package.json`, nested });
94
- }
95
- }
96
- }
97
- return [...found.values()];
98
- }
99
72
  /**
100
- * The nested packages of a run: each directory below the target root, in no `node_modules`
101
- * directory and none of `known`, that holds a `package.json` and lies at or above one of
102
- * `files`, the program's source files and the compiler configuration files, in path order.
103
- */
104
- function nestedPackageDirs(host, targetRoot, files, known) {
105
- const seen = new Set();
106
- const found = [];
107
- for (const file of files) {
108
- for (let dir = dirnamePath(file); dir !== targetRoot && !seen.has(dir) && relativePath(targetRoot, dir) !== undefined; dir = dirnamePath(dir)) {
109
- seen.add(dir);
110
- const below = relativePath(targetRoot, dir) ?? "";
111
- if (!below.split("/").includes("node_modules") &&
112
- !known.has(dir) &&
113
- host.kindOf(joinPath(dir, "package.json")) === "file") {
114
- found.push(dir);
115
- }
116
- }
117
- }
118
- return found.sort(compare);
119
- }
120
- /**
121
- * Reads every project the scope discovers, and each declared consumer's, in one snapshot,
122
- * each checked for errors first. `stage` is handed a target project and enumerates and roots
123
- * it if it reads it; a consumer project is read for its references to the target. A type error
124
- * in an own file skips the unit holding it. A guess that does not load or meets a setup failure
125
- * is dropped into `notBuilt` (see {@link isGuess}); any other such project fails the run, as
126
- * does a run whose every guess was dropped.
73
+ * Reads every project the load phase analyzes through `stage`, which enumerates and roots one
74
+ * if it reads it, and each declared consumer's project for its references to the target,
75
+ * in the same snapshot. A type error in an own file skips the unit holding it.
127
76
  */
128
77
  function readProjects(engine, host, scope, config, stage) {
129
78
  const targetRoot = scope.target.path;
130
- const discovered = discoverProjects(engine, host, scope, config.analysis.configurations);
131
- if (discovered.configFiles.length === 0 && discovered.notBuilt.length === 0) {
132
- throw new DiscoveryError(`${targetRoot} holds no compiler configuration, so the run holds no program`, []);
133
- }
134
- const ids = new Map(discovered.projects.map((project) => [project.configFile, project.id]));
135
- const loads = consumerLoads(engine, host, scope, discovered.configFiles);
136
- const consumerOf = new Map(loads.flatMap((load) => load.configFiles.map((configFile) => [configFile, load.consumer])));
137
79
  const absoluteRoot = resolvePath(host.workingDirectory(), targetRoot);
138
- const workspace = workspaceRun(engine, host, absoluteRoot, discovered.configFiles, readManifest(host, targetRoot));
80
+ const testPatterns = config.ts.test_files.map(globExpression);
139
81
  const parse = (fileName, text) => engine.parseSourceFile(fileName, text);
140
- const memberDirs = workspace?.memberDirs ?? [];
141
- // The nested packages are known only once every program is open, and every visit reads
142
- // the conventions and options they decide, so both are set in the session's first step.
143
- let nestedDirs = [];
144
- let conventions = readConventions(host, parse, [], config.ts.disabledConventions);
145
- let options;
146
- const readManifests = (opened) => {
147
- nestedDirs = nestedPackageDirs(host, normalizePath(absoluteRoot), opened
148
- .filter((one) => !consumerOf.has(one.configFile))
149
- .flatMap((one) => [one.configFile, ...one.fileNames()]), new Set(memberDirs));
150
- conventions = readConventions(host, parse, conventionManifests(host, absoluteRoot, memberDirs, nestedDirs), config.ts.disabledConventions);
151
- options = {
82
+ return loadRun(engine, host, scope, config, ({ plan, classified, runFiles }) => {
83
+ const { packages, conventions } = plan;
84
+ const options = {
152
85
  host,
153
- manifest: workspace?.manifest ?? readManifest(host, targetRoot),
154
- otherPackages: [
155
- ...(workspace?.entries ?? []),
156
- ...packageEntries(host, normalizePath(absoluteRoot), nestedDirs),
157
- ],
158
- patterns: config.rootPatterns,
159
- entryFiles: config.ts.entryFiles,
86
+ manifest: packages.manifest,
87
+ otherPackages: packages.entries,
88
+ patterns: config.roots.patterns,
89
+ entryFiles: config.ts.entry_files,
160
90
  conventions,
161
- testFiles: config.ts.testFiles,
91
+ testFiles: config.ts.test_files,
162
92
  parse,
163
- publishedAPI: config.targetKind !== "" && kindOf(config.targetKind, LANGUAGE) === "library",
164
- publishedMembers: new Set((workspace?.entries ?? []).filter((entry) => entry.published).map((entry) => entry.dir)),
93
+ publishedAPI: config.target.kind !== undefined && kindOf(config.target.kind) === "library",
94
+ publishedMembers: packages.published,
95
+ runFiles,
165
96
  };
166
- };
167
- const notBuilt = [...discovered.notBuilt];
168
- const dropped = (configFile, error) => {
169
- notBuilt.push({
170
- id: ids.get(configFile) ?? configFile,
171
- configFile,
172
- error: targetRelative(error, absoluteRoot),
173
- });
174
- };
175
- const failures = [];
176
- const unbuildable = [];
177
- const setupDropped = [];
178
- const librariesUnfound = [];
179
- const consumed = new Map();
180
- const heldFiles = new Set();
181
- let outsideTests = [];
182
- const setupMet = (configFile, met) => {
183
- const [first] = met;
184
- if (first !== undefined && isGuess(discovered, configFile)) {
185
- dropped(configFile, setupLine(first));
186
- setupDropped.push(...met);
187
- }
188
- else {
189
- unbuildable.push(...met);
190
- }
191
- };
192
- const testPatterns = config.ts.testFiles.map(globExpression);
193
- const joins = discovered.derived
194
- ? joinedTestFiles(engine, host, absoluteRoot, discovered.configFiles, testPatterns)
195
- : new Map();
196
- const session = runSession(engine, [...discovered.configFiles, ...consumerOf.keys()], (opened) => {
197
- const consumer = consumerOf.get(opened.configFile);
198
- const setup = consumer === undefined ? workspace?.unbuildable(opened.configFile) : undefined;
199
- if (setup !== undefined) {
200
- setupMet(opened.configFile, [
201
- { setupClass: "workspace-member-without-source", detail: setup },
202
- ]);
203
- return undefined;
204
- }
205
- const referenceOnly = workspace === undefined || consumer !== undefined
206
- ? () => false
207
- : workspace.referenceOnly(opened.configFile, opened.rootFiles);
208
- const project = workspace === undefined || consumer !== undefined
209
- ? opened
210
- : ownedAs(opened, (file, fromExternalLibrary) => workspace.ownFile(file, fromExternalLibrary));
211
- const raw = projectErrors(diagnosticsOf(project));
212
- const errors = { load: raw.load, check: ownErrors(raw.check, referenceOnly) };
213
- const [unloaded] = errors.load;
214
- if (unloaded !== undefined &&
215
- consumer === undefined &&
216
- isGuess(discovered, project.configFile)) {
217
- dropped(project.configFile, renderDiagnostic(unloaded, (path) => relativePath(absoluteRoot, path) ?? path));
218
- return undefined;
219
- }
220
- if (errors.load.length > 0) {
221
- failures.push(...errors.load);
222
- return undefined;
223
- }
224
- const reading = readErrors(project, errors.check, {
225
- host,
226
- targetRoot: consumer === undefined
227
- ? absoluteRoot
228
- : resolvePath(host.workingDirectory(), consumer.path),
229
- paths: pathsOf(engine, project.configFile),
97
+ const heldFiles = new Set();
98
+ const answers = classified.analyzed.map(({ plan: planned, project, skips, referenceOnly }) => {
99
+ const read = () => {
100
+ const held = inventory(project, host, targetRoot);
101
+ const found = roots(project, held, targetRoot, options);
102
+ const kept = skippedRoots(project, held, skips);
103
+ const rooted = kept.length === 0
104
+ ? found
105
+ : {
106
+ ...found,
107
+ liveUnderReachability: [
108
+ ...found.liveUnderReachability,
109
+ ...kept.map((id) => ({ id, kind: "type-error", source: "" })),
110
+ ],
111
+ };
112
+ const own = sourceFilesOf(project, targetRoot);
113
+ return {
114
+ configuration: planned.id,
115
+ configFile: project.configFile,
116
+ held,
117
+ rooted,
118
+ typeErrorSkips: skips.map((one) => one.record),
119
+ skipped: skippedUnits(skips, absoluteRoot),
120
+ referenceOnly: project
121
+ .ownSourceFiles()
122
+ .filter((file) => referenceOnly(file.fileName))
123
+ .flatMap((file) => relativePath(absoluteRoot, file.fileName) ?? []),
124
+ generated: generatedFilesOf(own, generatedFiles(conventions, own)),
125
+ testFileNames: conventions.testFiles,
126
+ };
127
+ };
128
+ for (const file of project.ownSourceFiles()) {
129
+ heldFiles.add(relativePath(absoluteRoot, file.fileName) ?? file.fileName);
130
+ }
131
+ return stage(project, { configuration: planned.id, read });
230
132
  });
231
- if (consumer !== undefined && reading.missing.length > 0) {
232
- unbuildable.push(...reading.missing.map((one) => ({
233
- setupClass: "missing-consumer",
234
- detail: `the consumer ${consumer.path} does not build, its dependencies not installed: ${one.detail}`,
235
- })));
236
- return undefined;
237
- }
238
- if (reading.missing.length > 0) {
239
- setupMet(project.configFile, reading.missing);
240
- return undefined;
241
- }
242
- const unconventional = consumer === undefined
243
- ? conventionFailuresFor(conventions, project.configFile, project.ownSourceFiles().map((file) => file.fileName))
244
- : [];
245
- if (unconventional.length > 0) {
246
- setupMet(project.configFile, unconventional);
247
- return undefined;
248
- }
249
- if (consumer !== undefined) {
250
- for (const reference of consumerReferences(project, host, targetRoot, {
251
- id: consumer.id,
252
- root: consumer.path,
253
- }, config)) {
133
+ const consumed = new Map();
134
+ for (const { project, consumer } of classified.consumed) {
135
+ const side = { id: consumer.id, root: consumer.path };
136
+ for (const reference of consumerReferences(project, host, targetRoot, side, config)) {
254
137
  consumed.set(referenceKey(reference), reference);
255
138
  }
256
- return undefined;
257
139
  }
258
- librariesUnfound.push(...raw.unfound.map((one) => ({
259
- configFile: project.configFile,
260
- error: renderDiagnostic(one, (path) => relativePath(absoluteRoot, path) ?? path),
261
- })));
262
- const configuration = ids.get(project.configFile) ?? project.configFile;
263
- const read = () => {
264
- const held = inventory(project, host, targetRoot);
265
- const found = roots(project, held, targetRoot, options);
266
- const kept = skippedRoots(project, held, reading.skips);
267
- const rooted = kept.length === 0
268
- ? found
269
- : {
270
- ...found,
271
- liveUnderReachability: [
272
- ...found.liveUnderReachability,
273
- ...kept.map((id) => ({ id, kind: "type-error", source: "" })),
274
- ],
275
- };
276
- const own = sourceFilesOf(project, targetRoot);
277
- return {
278
- configuration,
279
- configFile: project.configFile,
280
- held,
281
- rooted,
282
- typeErrorSkips: reading.skips.map((one) => one.record),
283
- skipped: skippedUnits(reading.skips, absoluteRoot),
284
- referenceOnly: project
285
- .ownSourceFiles()
286
- .filter((file) => referenceOnly(file.fileName))
287
- .flatMap((file) => relativePath(absoluteRoot, file.fileName) ?? []),
288
- generated: generatedFilesOf(own, generatedFiles(conventions, own)),
289
- testFileNames: conventions.testFiles,
290
- };
291
- };
292
- for (const file of project.ownSourceFiles()) {
293
- heldFiles.add(relativePath(absoluteRoot, file.fileName) ?? file.fileName);
294
- }
295
- return stage(project, { configuration, read });
296
- }, discovered.derived ? "record" : "refuse", joins.size === 0
297
- ? workspace?.programs
298
- : withJoinedTests(engine, host, workspace?.programs ?? new Map(), joins), () => {
299
- outsideTests = testFilesAt(host, targetRoot, sourcePaths(host, targetRoot).filter((path) => !heldFiles.has(path) && testPatterns.some((pattern) => pattern.test(path))), parse);
300
- }, readManifests);
301
- for (const configFile of session.unopened) {
302
- if (consumerOf.has(configFile) || !isGuess(discovered, configFile)) {
303
- throw new DiscoveryError(`${configFile} opened no project`, []);
304
- }
305
- dropped(configFile, "the compiler opened no project for the configuration");
306
- }
307
- if (unbuildable.length > 0) {
308
- throw new SetupError(unbuildable);
309
- }
310
- if (failures.length > 0) {
311
- throw new DiscoveryError(`${String(failures.length)} error(s), so no answer was produced`, failures);
312
- }
313
- const answers = session.projects.filter((answer) => answer !== undefined);
314
- const [first] = notBuilt;
315
- if (answers.length === 0 && setupDropped.length > 0) {
316
- throw new SetupError(setupDropped);
317
- }
318
- if (answers.length === 0 && first !== undefined) {
319
- throw new DiscoveryError(`no configuration discovery derived could be built; ${first.id}: ${first.error}`, []);
320
- }
321
- return {
322
- answers,
323
- consumers: {
324
- loaded: loads.map((load) => load.consumer),
325
- references: [...consumed.values()],
326
- },
327
- notBuilt,
328
- unanswered: session.unanswered,
329
- componentsUnread: workspace?.componentsUnread ?? [],
330
- librariesUnfound,
331
- conventionsApplied: conventions.applied,
332
- conventionUses: conventions.uses,
333
- memberDirs,
334
- nestedDirs,
335
- outsideTests,
336
- };
140
+ const outsideTests = testFilesAt(host, targetRoot, sourcePaths(host, targetRoot).filter((path) => !heldFiles.has(path) && testPatterns.some((pattern) => pattern.test(path))), parse);
141
+ return { answers, references: [...consumed.values()], outsideTests };
142
+ });
337
143
  }
338
144
  /**
339
145
  * The projects the run the scope and the configuration describe analyzes, each read
@@ -342,7 +148,11 @@ function readProjects(engine, host, scope, config, stage) {
342
148
  */
343
149
  export function runProjects(engine, host, scope, config) {
344
150
  const read = readProjects(engine, host, scope, config, (_project, opened) => opened.configuration);
345
- return { configurations: read.answers, notBuilt: read.notBuilt, unanswered: read.unanswered };
151
+ return {
152
+ configurations: read.result.answers,
153
+ notBuilt: read.classified.notBuilt,
154
+ unanswered: read.unanswered,
155
+ };
346
156
  }
347
157
  /**
348
158
  * The root set of the run the scope and the configuration describe.
@@ -353,7 +163,7 @@ export function runProjects(engine, host, scope, config) {
353
163
  export function runRoots(engine, host, scope, config, provenance) {
354
164
  const targetRoot = scope.target.path;
355
165
  const read = readProjects(engine, host, scope, config, (_project, opened) => opened.read());
356
- const projects = read.answers;
166
+ const projects = read.result.answers;
357
167
  const configurations = [];
358
168
  const merged = new Map();
359
169
  for (const answer of projects) {
@@ -381,13 +191,15 @@ export function runRoots(engine, host, scope, config, provenance) {
381
191
  }
382
192
  }
383
193
  }
384
- const ordered = [...merged.values()].sort((a, b) => byPosition(a.position, b.position) || compare(a.kind, b.kind) || compare(a.source, b.source));
385
- const unmatched = unmatchedEverywhere(config.rootPatterns, projects.map((answer) => answer.rooted));
194
+ const ordered = [...merged.values()].sort((a, b) => byPosition(a.position, b.position) ||
195
+ bytewise(a.kind, b.kind) ||
196
+ bytewise(a.source, b.source));
197
+ const unmatched = unmatchedEverywhere(config.roots.patterns, projects.map((answer) => answer.rooted));
386
198
  return {
387
199
  configurations,
388
200
  roots: ordered,
389
201
  findings: unmatchedRootFindings(unmatched, documentOf(host, provenance, ROOTS_SETTING, targetRoot)),
390
- notBuilt: read.notBuilt,
202
+ notBuilt: read.classified.notBuilt,
391
203
  unanswered: read.unanswered,
392
204
  };
393
205
  }
@@ -403,6 +215,44 @@ export const DETECTORS = new Map([
403
215
  ["framework-lifecycle", frameworkLifecycle],
404
216
  ["serialization-contract", serializationContract],
405
217
  ]);
218
+ /** The manifest section of a package test evidence counts, and the sections that keep one out of it. */
219
+ const DEV_SECTION = "devDependencies";
220
+ const OTHER_SECTIONS = ["dependencies", "peerDependencies", "optionalDependencies"];
221
+ /**
222
+ * The imports of a run's own files and the manifests below the target root that test
223
+ * evidence reads: a package is a devDependency of a file where the nearest manifest at or
224
+ * above it that declares the package declares it under `devDependencies` alone.
225
+ */
226
+ function testEvidence(host, targetRoot, projects) {
227
+ const imports = new Map();
228
+ for (const one of projects) {
229
+ for (const [path, specifiers] of one.imports) {
230
+ imports.set(path, specifiers);
231
+ }
232
+ }
233
+ const manifests = new Map();
234
+ const manifestIn = (dir) => {
235
+ if (!manifests.has(dir)) {
236
+ manifests.set(dir, manifestAt(host, dir));
237
+ }
238
+ return manifests.get(dir);
239
+ };
240
+ const declares = (section, name) => typeof section === "object" && section !== null && Object.hasOwn(section, name);
241
+ const root = resolvePath(host.workingDirectory(), targetRoot);
242
+ return {
243
+ imports,
244
+ devDependency: (path, name) => {
245
+ for (const dir of ancestors(dirnamePath(joinPath(root, path)), root)) {
246
+ const manifest = manifestIn(dir);
247
+ const other = OTHER_SECTIONS.some((one) => declares(manifest?.[one], name));
248
+ if (declares(manifest?.[DEV_SECTION], name) || other) {
249
+ return !other;
250
+ }
251
+ }
252
+ return false;
253
+ },
254
+ };
255
+ }
406
256
  /**
407
257
  * Reads every project of the run, its references and exemptions, and `extra`'s answer,
408
258
  * while the project's view is open, and merges the projects into one matrix in the run's
@@ -413,13 +263,13 @@ function readRun(engine, host, scope, config, mode, detectors, extra) {
413
263
  const targetRoot = scope.target.path;
414
264
  const disabled = disabledClasses(config);
415
265
  const templates = disabled.has("template-field")
416
- ? { delimiters: config.analysis.templateDelimiters, files: [] }
266
+ ? { delimiters: config.analysis.template_delimiters, files: [] }
417
267
  : readTemplates(host, targetRoot, config.analysis);
418
268
  const consumers = scope.consumers.map((consumer) => consumer.path);
419
269
  const read = readProjects(engine, host, scope, config, (project, opened) => {
420
270
  const projectRead = opened.read();
421
271
  const resolved = references(project, projectRead.held, targetRoot, {
422
- testFiles: config.ts.testFiles,
272
+ testFiles: config.ts.test_files,
423
273
  testFileNames: projectRead.testFileNames,
424
274
  });
425
275
  const evidence = evidenceRecords({
@@ -429,11 +279,14 @@ function readRun(engine, host, scope, config, mode, detectors, extra) {
429
279
  templates,
430
280
  ts: config.ts,
431
281
  consumers,
432
- ...(config.analysis.generatedFiles === "include"
282
+ ...(config.analysis.generated_files === "include"
433
283
  ? {}
434
284
  : { generated: projectRead.generated }),
435
285
  }, detectors, disabled);
436
286
  const components = project.ownSourceFiles().filter(isComponentFile);
287
+ const injected = project.program
288
+ .getSourceFileNames()
289
+ .some((name) => name.endsWith(`/node_modules/${GLOBALS_RUNNER}/globals.d.ts`));
437
290
  const pathOf = (file) => relativePath(targetRoot, file.fileName) ?? file.fileName;
438
291
  return {
439
292
  projectRead,
@@ -441,45 +294,81 @@ function readRun(engine, host, scope, config, mode, detectors, extra) {
441
294
  testFiles: resolved.testFilePaths,
442
295
  evidence,
443
296
  componentFiles: components.map(pathOf),
297
+ augmented: mergedGlobalInterfaces(project, projectRead.held, sourceFilesOf(project, targetRoot)).map((id) => ({ id, kind: "global-augmentation", source: "" })),
298
+ imports: new Map(project.ownSourceFiles().flatMap((file) => {
299
+ const path = relativePath(targetRoot, file.fileName);
300
+ if (path === undefined) {
301
+ return [];
302
+ }
303
+ const specifiers = file.imports.filter(isStringLiteralLikeNode).map((one) => one.text);
304
+ // A file calling the globals a program declares is written for their runner.
305
+ return [
306
+ [
307
+ path,
308
+ injected && namesTestGlobals(project, file)
309
+ ? [...specifiers, GLOBALS_RUNNER]
310
+ : specifiers,
311
+ ],
312
+ ];
313
+ })),
444
314
  componentWarnings: components.flatMap((file) => readComponent(file.originalText, extensionOf(file.fileName)).warnings.map((warning) => ({
445
315
  path: pathOf(file),
446
316
  line: lineOf(file.originalText, warning.offset),
447
317
  reason: warning.reason,
448
318
  }))),
319
+ decoded: decodedMembers(project, projectRead.held, productionFiles(project, targetRoot, resolved.testFilePaths), consumers),
449
320
  extra: extra(project, projectRead, resolved.references, resolved.testFilePaths),
450
321
  };
451
322
  });
452
- const outsidePaths = read.outsideTests.map((one) => one.path);
453
- const withOutside = read.answers.map((one) => ({
323
+ const outsidePaths = read.result.outsideTests.map((one) => one.path);
324
+ const withOutside = read.result.answers.map((one) => ({
454
325
  ...one,
455
326
  references: [
456
327
  ...one.references,
457
- ...outsideTestReferences(read.outsideTests, one.projectRead.held, targetRoot),
328
+ ...outsideTestReferences(read.result.outsideTests, one.projectRead.held, targetRoot),
458
329
  ],
459
330
  testFiles: [...one.testFiles, ...outsidePaths],
460
331
  }));
461
332
  // Test-support code is code only test code imports in any configuration of the run: a
462
333
  // configuration that holds no test file says nothing about what the tests import.
463
- const support = testSupportFiles(withOutside.flatMap((one) => one.projectRead.held.symbols), withOutside.flatMap((one) => one.references), withOutside.flatMap((one) => one.projectRead.rooted.liveUnderReachability), withOutside.flatMap((one) => one.testFiles));
464
- const settled = settledSatisfaction(withOutside.map((one) => ({ evidence: one.evidence, held: one.projectRead.held })), [...withOutside.flatMap((one) => one.references), ...read.consumers.references]);
334
+ const { support, testUsed } = testSupportFiles(withOutside.flatMap((one) => one.projectRead.held.symbols), withOutside.flatMap((one) => one.references), withOutside.flatMap((one) => one.projectRead.rooted.liveUnderReachability), withOutside.flatMap((one) => one.testFiles), testEvidence(host, targetRoot, withOutside));
335
+ // A member a decode fills or a copy carries is written: a value decoded and sent again carries it on.
336
+ const written = new Set([
337
+ ...countedWrites([...withOutside.flatMap((one) => one.references), ...read.result.references], support, mode),
338
+ ...withOutside.flatMap((one) => [
339
+ ...one.decoded.filled,
340
+ ...one.decoded.parsed,
341
+ ...one.decoded.copied,
342
+ ]),
343
+ ]);
344
+ const evidence = withOutside.map((one) => writtenRecords(one.evidence, written));
345
+ const settled = settledSatisfaction(withOutside.map((one, index) => ({
346
+ evidence: evidence[index] ?? [],
347
+ held: one.projectRead.held,
348
+ })), [...withOutside.flatMap((one) => one.references), ...read.result.references]);
465
349
  const answers = withOutside.map((one, index) => {
466
350
  const { projectRead } = one;
467
351
  const satisfied = settled[index];
468
- const detected = heldRecords(satisfied?.evidence ?? one.evidence, {
352
+ const detected = heldRecords(satisfied?.evidence ?? evidence[index] ?? [], {
469
353
  mode,
470
354
  testFiles: new Set([...one.testFiles, ...support]),
471
355
  });
472
356
  const exempt = detected.records;
473
357
  const made = supportReferences([...one.references, ...(satisfied?.references ?? [])], support);
474
358
  const held = new Set(projectRead.held.symbols.map((symbol) => symbol.position.path));
475
- const supportFiles = [...support].filter((path) => held.has(path)).sort();
359
+ const supportFiles = [...support].filter((path) => held.has(path)).sort(bytewise);
360
+ const testUsedFiles = [...testUsed].filter((path) => held.has(path)).sort(bytewise);
476
361
  const configured = {
477
362
  configuration: projectRead.configuration,
478
363
  symbols: projectRead.held.symbols,
479
364
  references: [...made, ...evidenceReferences(projectRead.held.symbols, detected.inTestFiles)],
480
- roots: projectRead.rooted.liveUnderReachability,
481
- testFiles: one.testFiles,
365
+ // The augmentations are rooted after test support is read: an outside declaration
366
+ // naming one calls nothing of its file.
367
+ roots: [...projectRead.rooted.liveUnderReachability, ...one.augmented],
368
+ testFiles: [...one.testFiles, ...supportFiles],
482
369
  ...(supportFiles.length === 0 ? {} : { supportFiles }),
370
+ ...(testUsedFiles.length === 0 ? {} : { testUsedFiles }),
371
+ ...(projectRead.held.ambient.size === 0 ? {} : { ambient: projectRead.held.ambient }),
483
372
  ...(projectRead.referenceOnly.length === 0
484
373
  ? {}
485
374
  : { referenceOnly: projectRead.referenceOnly }),
@@ -498,15 +387,19 @@ function readRun(engine, host, scope, config, mode, detectors, extra) {
498
387
  .filter((reference) => reference.resolution === "by-name")
499
388
  .map((reference) => reference.to),
500
389
  ...exempt.filter(isUnansweredRecord).map((record) => record.id),
390
+ ...one.decoded.unanswered,
501
391
  ],
392
+ filledOutside: one.decoded.filled,
502
393
  typeErrorSkips: projectRead.typeErrorSkips,
394
+ unmappedEntries: projectRead.rooted.unmappedEntries,
395
+ heldScripts: projectRead.rooted.heldScripts,
503
396
  skipped: projectRead.skipped,
504
397
  componentFiles: one.componentFiles,
505
398
  componentWarnings: one.componentWarnings,
506
399
  extra: one.extra,
507
400
  };
508
401
  });
509
- const consumed = read.consumers.references;
402
+ const consumed = read.result.references;
510
403
  const projects = answers.map((one) => consumed.length === 0
511
404
  ? one
512
405
  : {
@@ -526,15 +419,17 @@ function readRun(engine, host, scope, config, mode, detectors, extra) {
526
419
  ]),
527
420
  ],
528
421
  projects,
529
- consumers: read.consumers.loaded,
530
- notBuilt: read.notBuilt,
422
+ consumers: read.plan.discovery.consumers,
423
+ notBuilt: read.classified.notBuilt,
531
424
  unanswered: read.unanswered,
532
- componentsUnread: read.componentsUnread,
533
- librariesUnfound: read.librariesUnfound,
534
- conventionsApplied: read.conventionsApplied,
535
- conventionUses: read.conventionUses,
536
- memberDirs: read.memberDirs,
537
- nestedDirs: read.nestedDirs,
425
+ componentsUnread: read.plan.workspace?.componentsUnread ?? [],
426
+ librariesUnfound: read.classified.librariesUnfound,
427
+ unresolvedSpecifiers: read.classified.unresolvedSpecifiers,
428
+ unread: read.plan.unread,
429
+ conventionsApplied: read.plan.conventions.applied,
430
+ conventionUses: read.plan.conventions.uses,
431
+ memberDirs: read.plan.packages.members,
432
+ nestedDirs: read.plan.packages.nested,
538
433
  };
539
434
  }
540
435
  /**
@@ -587,6 +482,13 @@ function evidenceReferences(symbols, inTestFiles) {
587
482
  ];
588
483
  });
589
484
  }
485
+ /**
486
+ * The declarations a write the mode counts stores into, a write a test-support file
487
+ * makes being a test's.
488
+ */
489
+ function countedWrites(references, support, mode) {
490
+ return new Set(supportReferences(references, support).flatMap((reference) => reference.use === "write" && !(mode.production && reference.test) ? [reference.to] : []));
491
+ }
590
492
  /**
591
493
  * The declarations every reference the mode counts stores into, a loaded consumer's
592
494
  * reference counting as a read: what the write-only kind would report but for a record.
@@ -657,7 +559,7 @@ function sweptOf(read, input) {
657
559
  unanswered: read.unanswered,
658
560
  heldByUnanswered: [...held],
659
561
  skipped: read.projects.flatMap((one) => one.skipped),
660
- componentFiles: [...new Set(read.projects.flatMap((one) => one.componentFiles))].sort(compare),
562
+ componentFiles: [...new Set(read.projects.flatMap((one) => one.componentFiles))].sort(bytewise),
661
563
  };
662
564
  }
663
565
  /**
@@ -671,6 +573,11 @@ function sweptOf(read, input) {
671
573
  export function runSweep(engine, host, scope, config, input, detectors = DETECTORS) {
672
574
  return sweptOf(readRun(engine, host, scope, config, input.mode, detectors, () => undefined), input);
673
575
  }
576
+ /** The project's own files below the target root that are no test file. */
577
+ function productionFiles(project, targetRoot, testFiles) {
578
+ const tests = new Set(testFiles);
579
+ return [...sourceFilesOf(project, targetRoot).byPath].flatMap(([path, file]) => tests.has(path) ? [] : [file]);
580
+ }
674
581
  /** Reads a run's projects with every per-project fact an emitter reads. */
675
582
  export function readEmitterRun(engine, host, scope, config, mode, detectors) {
676
583
  const targetRoot = scope.target.path;
@@ -686,8 +593,9 @@ export function readEmitterRun(engine, host, scope, config, mode, detectors) {
686
593
  directives: inlineDirectives(project, read.held, targetRoot).filter((directive) => !read.referenceOnly.includes(directive.site.path)),
687
594
  declarationsNamed: declarationsNamed(project, read.held, configuredDeclarations(config)),
688
595
  parts: projectParts(project, read.held, references, targetRoot),
596
+ unseenLoads: unseenLoadsOf(project, read.configuration, targetRoot, testFiles),
689
597
  }));
690
- // Whether a declaration carries the marker decides its code, so one whose marker went
598
+ // Whether a declaration carries the marker decides its code, so one whose answer went
691
599
  // unread is reported by nothing.
692
600
  const unread = run.projects.flatMap((one) => one.extra.deprecation.unanswered);
693
601
  return unread.length === 0
@@ -698,7 +606,8 @@ export function readEmitterRun(engine, host, scope, config, mode, detectors) {
698
606
  * The workspace members whose dependencies the run judges: `read` holds those a built
699
607
  * project holds a file below, `unbuilt` the others whose own directory, the nearest
700
608
  * manifest's above the configuration, holds a configuration the run could not build. A
701
- * member no configuration holds a file of is in neither.
609
+ * member no configuration holds a file of is in neither. A nested package no built project
610
+ * holds a file below joins `unbuilt` the same way.
702
611
  */
703
612
  function judgedMembers(host, targetRoot, read) {
704
613
  const root = resolvePath(host.workingDirectory(), targetRoot);
@@ -717,20 +626,23 @@ function judgedMembers(host, targetRoot, read) {
717
626
  judged.unbuilt.push(dir);
718
627
  }
719
628
  }
629
+ for (const dir of read.nestedDirs) {
630
+ const below = relativePath(root, dir);
631
+ if (below !== undefined &&
632
+ owners.has(dir) &&
633
+ ![...held].some((path) => path.startsWith(`${below}/`))) {
634
+ judged.unbuilt.push(dir);
635
+ }
636
+ }
720
637
  return judged;
721
638
  }
722
639
  /** The directory of the nearest `package.json` at or above a file, inside `root`, else `root`. */
723
640
  function nearestManifestDir(host, root, file) {
724
- for (let dir = dirnamePath(file); relativePath(root, dir) !== undefined; dir = dirnamePath(dir)) {
725
- if (host.kindOf(joinPath(dir, "package.json")) === "file") {
726
- return dir;
727
- }
728
- }
729
- return root;
641
+ return ancestors(dirnamePath(file), root).find((dir) => holdsManifest(host, dir)) ?? root;
730
642
  }
731
643
  /**
732
644
  * The input every emitter reads over one sweep of a read run: the facts beside the sweep
733
- * each kind family reads, every per-project one read in the same session as the sweep.
645
+ * each emitter reads, every per-project one read in the same session as the sweep.
734
646
  */
735
647
  export function emitterInputOver(host, scope, config, read, input, selfCheck) {
736
648
  const targetRoot = scope.target.path;
@@ -752,6 +664,7 @@ export function emitterInputOver(host, scope, config, read, input, selfCheck) {
752
664
  heldByInclusion: new Set(projects.flatMap((one) => one.extra.heldByInclusion)),
753
665
  generated: new Set(projects.flatMap((one) => one.extra.generated)),
754
666
  inOperands: new Set(projects.flatMap((one) => one.extra.inOperands)),
667
+ unseenLoads: projects.flatMap((one) => one.extra.unseenLoads),
755
668
  },
756
669
  boundary: {
757
670
  consumers: {
@@ -760,6 +673,7 @@ export function emitterInputOver(host, scope, config, read, input, selfCheck) {
760
673
  },
761
674
  encapsulated: readManifest(host, targetRoot).declaresExports,
762
675
  edges: readEdgeSides(host, targetRoot),
676
+ filledOutside: new Set(projects.flatMap((one) => one.filledOutside)),
763
677
  },
764
678
  selfCheck,
765
679
  intraFunction: intraFunctionFacts(projects.map((one) => one.extra.parts), projects.flatMap((one) => one.configured.references), new Set([...swept.exempt.map((one) => one.id), ...swept.heldByUnanswered])),
@@ -770,7 +684,7 @@ export function emitterInputOver(host, scope, config, read, input, selfCheck) {
770
684
  * directives, the ignore file's entries and the baseline's rows are bound before any
771
685
  * sweep, and the run is swept once under every bound record's mark and once under none,
772
686
  * so a record is decided against the finding its code would have produced. The
773
- * self-check family reports the refused suppressions and the roots that named nothing.
687
+ * configuration family reports the refused suppressions and the roots that named nothing.
774
688
  * Beside the inputs it answers the projects and test-file rules a report names.
775
689
  */
776
690
  function readAnalysis(engine, host, scope, config, provenance, mode, detectors) {
@@ -788,7 +702,7 @@ function readAnalysis(engine, host, scope, config, provenance, mode, detectors)
788
702
  id: one.configured.configuration,
789
703
  configFile: one.configFile,
790
704
  })),
791
- testFileRules: testFileRulesOf(config.ts.testFiles, read.projects.flatMap((one) => one.configured.testFiles), read.projects.flatMap((one) => one.configured.supportFiles ?? [])),
705
+ testFileRules: testFileRulesOf(config.ts.test_files, read.projects.flatMap((one) => one.configured.testFiles), read.projects.flatMap((one) => one.configured.supportFiles ?? [])),
792
706
  typeErrorSkips: read.projects.flatMap((one) => one.typeErrorSkips),
793
707
  componentWarnings: [
794
708
  ...new Map(read.projects
@@ -800,7 +714,12 @@ function readAnalysis(engine, host, scope, config, provenance, mode, detectors)
800
714
  unanswered: read.unanswered,
801
715
  componentsUnread: read.componentsUnread,
802
716
  librariesUnfound: read.librariesUnfound,
717
+ unresolvedSpecifiers: read.unresolvedSpecifiers,
718
+ unread: read.unread,
803
719
  conventionsApplied: read.conventionsApplied,
720
+ unmappedEntries: unmappedEverywhere(read.projects),
721
+ heldScripts: read.projects.flatMap((one) => one.heldScripts.map((held) => ({ ...held, configFile: one.configFile }))),
722
+ unseenLoads: read.projects.flatMap((one) => one.extra.unseenLoads),
804
723
  },
805
724
  configured: read.projects.map((one) => one.configured),
806
725
  symbols,
@@ -816,7 +735,7 @@ function inputsOver(host, scope, config, provenance, mode, read, documents) {
816
735
  };
817
736
  const facts = {
818
737
  refusals: suppressions.refusals,
819
- unmatchedRoots: unmatchedEverywhere(config.rootPatterns, read.projects.map((one) => one.extra.rooted)),
738
+ unmatchedRoots: unmatchedEverywhere(config.roots.patterns, read.projects.map((one) => one.extra.rooted)),
820
739
  rootsDocument: documentOf(host, provenance, ROOTS_SETTING, targetRoot),
821
740
  unmatchedDeclarations: configuredDeclarations(config)
822
741
  .filter((_, at) => !read.projects.some((one) => one.extra.declarationsNamed[at] === true))
@@ -837,9 +756,22 @@ function inputsOver(host, scope, config, provenance, mode, read, documents) {
837
756
  suppressions,
838
757
  };
839
758
  }
759
+ /** The manifest entries every project of the run reads back to no file, in the first project's order. */
760
+ function unmappedEverywhere(projects) {
761
+ const key = (entry) => `${entry.dir}\u0000${entry.member}`;
762
+ const [first, ...rest] = projects;
763
+ const others = rest.map((one) => new Set(one.unmappedEntries.map(key)));
764
+ const found = new Map();
765
+ for (const entry of first?.unmappedEntries ?? []) {
766
+ if (others.every((one) => one.has(key(entry)))) {
767
+ found.set(key(entry), entry);
768
+ }
769
+ }
770
+ return [...found.values()];
771
+ }
840
772
  /**
841
773
  * The analysis of the run the scope and the configuration describe: the run read with its
842
- * suppressions bound, every family's findings with the suppressions applied and the
774
+ * suppressions bound, every emitter's findings with the suppressions applied and the
843
775
  * declared edges evaluated, the projects and test-file rules the report names, and the sweep
844
776
  * an explanation reads.
845
777
  */
@@ -850,12 +782,16 @@ export function runAnalysis(engine, host, scope, config, provenance, mode, detec
850
782
  }
851
783
  /**
852
784
  * One count per configuration of the run in which the checker left a question unanswered:
853
- * the questions asked in its project, and the declarations of its own the run holds for them.
785
+ * the questions asked in its project, a formed program's manifest that does not parse or cannot
786
+ * be read and a package whose command strings name files the project holds each counted as one, and the
787
+ * declarations of its own the run holds for them.
854
788
  */
855
789
  export function unansweredCounts(analysis) {
856
790
  const held = new Set(analysis.swept.heldByUnanswered);
857
791
  return analysis.run.projects.flatMap((project) => {
858
- const questions = analysis.run.unanswered.filter((one) => one.configFile === project.configFile).length;
792
+ const questions = analysis.run.unanswered.filter((one) => one.configFile === project.configFile).length +
793
+ analysis.run.unread.filter((one) => one.kind === "manifest" && one.configFile === project.configFile).length +
794
+ analysis.run.heldScripts.filter((one) => one.configFile === project.configFile).length;
859
795
  if (questions === 0) {
860
796
  return [];
861
797
  }