@cplieger/deadset-ts 8.0.0-dev.1 → 8.0.0-dev.11

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 (276) 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 +181 -341
  5. package/dist/src/augmentations.js +106 -13
  6. package/dist/src/automatic-types.js +10 -29
  7. package/dist/src/build-scripts.js +425 -0
  8. package/dist/src/component-layer.js +4 -2
  9. package/dist/src/components.js +2 -3
  10. package/dist/src/config.js +6 -82
  11. package/dist/src/configuration-files.js +28 -176
  12. package/dist/src/configured-declarations.js +15 -20
  13. package/dist/src/conformance-record.js +1 -1
  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 +73 -3
  20. package/dist/src/conventions.js +36 -66
  21. package/dist/src/dependencies.js +103 -40
  22. package/dist/src/edges.js +38 -122
  23. package/dist/src/element-access.js +4 -6
  24. package/dist/src/entry-point-gaps.js +2 -8
  25. package/dist/src/entry-points.js +11 -27
  26. package/dist/src/enum-group.js +3 -10
  27. package/dist/src/erased-stores.js +4 -6
  28. package/dist/src/exempt-classes.js +1 -63
  29. package/dist/src/exempt.js +21 -40
  30. package/dist/src/failure.js +36 -0
  31. package/dist/src/file-facts.js +2 -25
  32. package/dist/src/findings/completion.js +139 -72
  33. package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
  34. package/dist/src/findings/emitters.js +5 -5
  35. package/dist/src/findings/interfaces.js +4 -5
  36. package/dist/src/findings/intra-function.js +13 -10
  37. package/dist/src/findings/non-code-artifacts.js +7 -8
  38. package/dist/src/findings/reads-and-writes.js +16 -13
  39. package/dist/src/findings/self-check.js +10 -12
  40. package/dist/src/findings/unused-declarations.js +41 -32
  41. package/dist/src/findings/visibility-narrowing.js +19 -16
  42. package/dist/src/findings-pass.js +8 -15
  43. package/dist/src/framework-lifecycle.js +7 -7
  44. package/dist/src/fs/document.js +52 -0
  45. package/dist/src/fs/manifest.js +72 -0
  46. package/dist/src/fs/tree.js +88 -0
  47. package/dist/src/generated-file.js +7 -4
  48. package/dist/src/git-ignore.js +39 -26
  49. package/dist/src/graph.js +5 -3
  50. package/dist/src/html-entries.js +6 -33
  51. package/dist/src/in-operands.js +2 -1
  52. package/dist/src/index.js +1 -1
  53. package/dist/src/injection-container.js +1 -1
  54. package/dist/src/intra-function-parts.js +63 -43
  55. package/dist/src/inventory.js +2 -21
  56. package/dist/src/javascript-file.js +33 -0
  57. package/dist/src/json-document.js +198 -67
  58. package/dist/src/keyof-comparisons.js +4 -6
  59. package/dist/src/kinds.js +3 -366
  60. package/dist/src/load/classify.js +270 -0
  61. package/dist/src/load/diagnostics.js +80 -0
  62. package/dist/src/load/discover.js +277 -0
  63. package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
  64. package/dist/src/load/expected-module.js +136 -0
  65. package/dist/src/load/load.js +27 -0
  66. package/dist/src/load/module-lookup.js +37 -0
  67. package/dist/src/load/plan.js +313 -0
  68. package/dist/src/load/specifiers.js +115 -0
  69. package/dist/src/load/synthesize.js +140 -0
  70. package/dist/src/load/test-globals.js +207 -0
  71. package/dist/src/load/test-joins.js +82 -0
  72. package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
  73. package/dist/src/{workspace-programs.js → load/workspace-programs.js} +89 -66
  74. package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +23 -65
  75. package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
  76. package/dist/src/{workspace.js → load/workspace.js} +62 -124
  77. package/dist/src/manifest.js +49 -54
  78. package/dist/src/matrix.js +76 -4
  79. package/dist/src/mdx-entries.js +3 -26
  80. package/dist/src/outside-tests.js +1 -1
  81. package/dist/src/partial.js +2 -1
  82. package/dist/src/paths.js +26 -15
  83. package/dist/src/position.js +2 -1
  84. package/dist/src/print.js +11 -79
  85. package/dist/src/product.js +13 -0
  86. package/dist/src/query.js +1 -0
  87. package/dist/src/ref.js +1 -0
  88. package/dist/src/references.js +48 -30
  89. package/dist/src/report-schema.js +51 -0
  90. package/dist/src/report.js +57 -41
  91. package/dist/src/reporters/annotations.js +15 -8
  92. package/dist/src/reporters/sarif.js +16 -10
  93. package/dist/src/reporters/template.js +4 -19
  94. package/dist/src/resolve.js +245 -729
  95. package/dist/src/roots.js +273 -87
  96. package/dist/src/run.js +66 -77
  97. package/dist/src/schema.js +320 -343
  98. package/dist/src/scope.js +38 -115
  99. package/dist/src/serialization-contract.js +595 -21
  100. package/dist/src/session.js +34 -29
  101. package/dist/src/source-files.js +1 -1
  102. package/dist/src/suppress-file.js +25 -28
  103. package/dist/src/suppress-inline.js +3 -2
  104. package/dist/src/suppress.js +33 -51
  105. package/dist/src/sweep.js +7 -9
  106. package/dist/src/symbol-keys.js +3 -5
  107. package/dist/src/template-field.js +9 -19
  108. package/dist/src/test-support.js +8 -11
  109. package/dist/src/text/compare.js +26 -0
  110. package/dist/src/text/glob.js +194 -0
  111. package/dist/src/text/named-files.js +54 -0
  112. package/dist/src/text/shell.js +159 -0
  113. package/dist/src/text/yaml.js +393 -0
  114. package/dist/src/type-errors.js +11 -232
  115. package/dist/src/unseen-loads.js +100 -0
  116. package/dist/src/unwrap.js +23 -0
  117. package/dist/src/usage.js +10 -0
  118. package/dist/src/verbs/analyze.js +166 -126
  119. package/dist/src/verbs/describe.js +9 -16
  120. package/dist/src/verbs/explain.js +10 -7
  121. package/dist/src/verbs/print-config.js +2 -1
  122. package/dist/src/verbs/print-projects.js +6 -4
  123. package/dist/src/verbs/print-retained.js +4 -2
  124. package/dist/src/verbs/print-roots.js +4 -2
  125. package/dist/src/verbs/verb.js +1 -5
  126. package/dist/src/verbs/version.js +14 -3
  127. package/dist/src/workflow-steps.js +62 -64
  128. package/jsr.json +1 -1
  129. package/package.json +5 -4
  130. package/src/analysis.ts +245 -502
  131. package/src/augmentations.ts +135 -17
  132. package/src/automatic-types.ts +11 -29
  133. package/src/build-scripts.ts +543 -0
  134. package/src/component-layer.ts +4 -2
  135. package/src/components.ts +2 -4
  136. package/src/config.ts +31 -302
  137. package/src/configuration-files.ts +34 -179
  138. package/src/configured-declarations.ts +18 -23
  139. package/src/conformance-record.ts +1 -1
  140. package/src/consumers.ts +6 -57
  141. package/src/contextual-uses.ts +64 -12
  142. package/src/contract/config.generated.ts +818 -0
  143. package/src/contract/contract.generated.ts +1308 -0
  144. package/src/contract/report.generated.ts +1237 -0
  145. package/src/convention-rows.ts +85 -3
  146. package/src/conventions.ts +46 -84
  147. package/src/dependencies.ts +118 -46
  148. package/src/edges.ts +41 -140
  149. package/src/element-access.ts +4 -7
  150. package/src/entry-point-gaps.ts +3 -9
  151. package/src/entry-points.ts +12 -38
  152. package/src/enum-group.ts +2 -11
  153. package/src/erased-stores.ts +3 -7
  154. package/src/exempt-classes.ts +1 -75
  155. package/src/exempt.ts +36 -50
  156. package/src/failure.ts +63 -0
  157. package/src/file-facts.ts +9 -24
  158. package/src/finding.ts +14 -9
  159. package/src/findings/boundary.ts +2 -0
  160. package/src/findings/completion.ts +181 -84
  161. package/src/findings/dependencies-and-module-machinery.ts +4 -7
  162. package/src/findings/emitter.ts +8 -9
  163. package/src/findings/emitters.ts +7 -7
  164. package/src/findings/interfaces.ts +4 -6
  165. package/src/findings/intra-function.ts +13 -11
  166. package/src/findings/non-code-artifacts.ts +9 -11
  167. package/src/findings/reads-and-writes.ts +20 -16
  168. package/src/findings/self-check.ts +11 -17
  169. package/src/findings/unused-declarations.ts +45 -32
  170. package/src/findings/visibility-narrowing.ts +20 -17
  171. package/src/findings-pass.ts +10 -19
  172. package/src/framework-lifecycle.ts +7 -7
  173. package/src/fs/document.ts +70 -0
  174. package/src/fs/manifest.ts +92 -0
  175. package/src/fs/tree.ts +110 -0
  176. package/src/generated-file.ts +7 -4
  177. package/src/git-ignore.ts +50 -26
  178. package/src/graph.ts +5 -3
  179. package/src/host.ts +12 -5
  180. package/src/html-entries.ts +6 -33
  181. package/src/in-operands.ts +2 -1
  182. package/src/index.ts +1 -1
  183. package/src/injection-container.ts +1 -1
  184. package/src/intra-function-parts.ts +73 -44
  185. package/src/inventory.ts +2 -20
  186. package/src/javascript-file.ts +37 -0
  187. package/src/json-document.ts +241 -76
  188. package/src/keyof-comparisons.ts +3 -7
  189. package/src/kinds.ts +5 -413
  190. package/src/load/classify.ts +397 -0
  191. package/src/load/diagnostics.ts +144 -0
  192. package/src/load/discover.ts +455 -0
  193. package/src/{emit-map.ts → load/emit-map.ts} +1 -1
  194. package/src/load/expected-module.ts +188 -0
  195. package/src/load/load.ts +73 -0
  196. package/src/load/module-lookup.ts +50 -0
  197. package/src/load/plan.ts +504 -0
  198. package/src/load/specifiers.ts +168 -0
  199. package/src/load/synthesize.ts +211 -0
  200. package/src/load/test-globals.ts +251 -0
  201. package/src/load/test-joins.ts +98 -0
  202. package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
  203. package/src/{workspace-programs.ts → load/workspace-programs.ts} +128 -85
  204. package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +33 -76
  205. package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
  206. package/src/{workspace.ts → load/workspace.ts} +90 -134
  207. package/src/manifest.ts +81 -75
  208. package/src/matrix.ts +122 -4
  209. package/src/mdx-entries.ts +3 -26
  210. package/src/outside-tests.ts +1 -1
  211. package/src/partial.ts +3 -2
  212. package/src/paths.ts +27 -15
  213. package/src/position.ts +2 -1
  214. package/src/print.ts +12 -92
  215. package/src/product.ts +18 -0
  216. package/src/query.ts +11 -0
  217. package/src/ref.ts +1 -0
  218. package/src/references.ts +52 -31
  219. package/src/report-schema.ts +68 -0
  220. package/src/report.ts +101 -224
  221. package/src/reporters/annotations.ts +22 -11
  222. package/src/reporters/json.ts +1 -1
  223. package/src/reporters/reporter.ts +2 -4
  224. package/src/reporters/reporters.ts +1 -1
  225. package/src/reporters/sarif.ts +29 -16
  226. package/src/reporters/template.ts +4 -20
  227. package/src/reporters/text.ts +3 -3
  228. package/src/resolve.ts +283 -1114
  229. package/src/roots.ts +339 -104
  230. package/src/run.ts +75 -91
  231. package/src/schema.ts +391 -388
  232. package/src/scope.ts +46 -138
  233. package/src/serialization-contract.ts +763 -22
  234. package/src/session.ts +70 -54
  235. package/src/source-files.ts +1 -1
  236. package/src/suppress-file.ts +24 -33
  237. package/src/suppress-inline.ts +2 -2
  238. package/src/suppress.ts +43 -58
  239. package/src/sweep.ts +23 -9
  240. package/src/symbol-keys.ts +2 -5
  241. package/src/template-field.ts +15 -25
  242. package/src/test-support.ts +17 -13
  243. package/src/text/compare.ts +27 -0
  244. package/src/text/glob.ts +210 -0
  245. package/src/text/named-files.inputs.json +1522 -0
  246. package/src/text/named-files.ts +68 -0
  247. package/src/text/shell.ts +161 -0
  248. package/src/text/yaml.ts +423 -0
  249. package/src/type-errors.ts +18 -288
  250. package/src/unseen-loads.ts +134 -0
  251. package/src/unwrap.ts +34 -0
  252. package/src/usage.ts +10 -0
  253. package/src/verbs/analyze.ts +224 -162
  254. package/src/verbs/describe.ts +14 -15
  255. package/src/verbs/explain.ts +10 -11
  256. package/src/verbs/print-config.ts +2 -1
  257. package/src/verbs/print-projects.ts +6 -4
  258. package/src/verbs/print-retained.ts +4 -2
  259. package/src/verbs/print-roots.ts +4 -2
  260. package/src/verbs/verb.ts +2 -8
  261. package/src/verbs/version.ts +15 -3
  262. package/src/workflow-steps.ts +74 -64
  263. package/dist/src/base-url.js +0 -18
  264. package/dist/src/discover.js +0 -468
  265. package/dist/src/glob.js +0 -65
  266. package/dist/src/pnpm-workspace.js +0 -120
  267. package/dist/src/setup-failure.js +0 -23
  268. package/dist/src/test-joins.js +0 -101
  269. package/dist/src/version.js +0 -7
  270. package/src/base-url.ts +0 -24
  271. package/src/discover.ts +0 -653
  272. package/src/glob.ts +0 -67
  273. package/src/pnpm-workspace.ts +0 -135
  274. package/src/setup-failure.ts +0 -39
  275. package/src/test-joins.ts +0 -120
  276. package/src/version.ts +0 -7
package/src/print.ts CHANGED
@@ -1,96 +1,16 @@
1
- import {
2
- renderOrigin,
3
- type Config,
4
- type DeclarationEntry,
5
- type Provenance,
6
- type Provider,
7
- } from "./config.ts";
8
-
9
- /** One declaration entry as a document writes it: the members of its shape alone. */
10
- function declarationOf(entry: DeclarationEntry): Record<string, string> {
11
- switch (entry.shape) {
12
- case "symbol":
13
- return { symbol: entry.symbol };
14
- case "module":
15
- return { module: entry.module, name: entry.name };
16
- case "global":
17
- return { global: entry.global };
18
- }
19
- }
20
-
21
- /** One provider entry as a document writes it: the members of its shape alone. */
22
- function providerOf(entry: Provider): Record<string, unknown> {
23
- const installed = { name: entry.name, languages: entry.languages, command: entry.command };
24
- if (entry.shape === "installed") {
25
- return installed;
26
- }
27
- return { ...installed, source: entry.source, version: entry.version, digest: entry.digest };
28
- }
1
+ import { renderOrigin, type Provenance, type ResolvedConfig } from "./config.ts";
2
+ import { bytewise } from "./text/compare.ts";
29
3
 
30
4
  /**
31
- * The resolved configuration and its provenance as one JSON object, indented,
32
- * ending in a newline.
33
- *
34
- * The output is an instance of the closed key list, so reading it back as a
35
- * repository configuration resolves to the same configuration: the provenance
36
- * object is a declared key resolution ignores. Keys are written in the schema's
37
- * order and the severity and provenance objects in ascending key order, so one
38
- * configuration renders to one string whatever order a source named its settings
39
- * in.
5
+ * The resolved configuration and its provenance as one JSON object, indented, ending
6
+ * in a newline: the configuration in the order resolution gives it, then the provenance
7
+ * object in ascending key order. Read back as a repository configuration it resolves to
8
+ * the same configuration, because resolution checks the provenance object and then
9
+ * ignores it.
40
10
  */
41
- export function printConfig(config: Config, provenance: Provenance): string {
42
- const document = {
43
- contract_version: config.contractVersion,
44
- target: { kind: config.targetKind },
45
- analysis: {
46
- languages: config.analysis.languages,
47
- min_confidence: config.analysis.minConfidence,
48
- generated_files: config.analysis.generatedFiles,
49
- consumer_tests: config.analysis.consumerTests,
50
- configurations: config.analysis.configurations.map((entry) =>
51
- entry.shape === "project"
52
- ? { id: entry.id, project: entry.project }
53
- : { id: entry.id, os: entry.os, arch: entry.arch, tags: entry.tags },
54
- ),
55
- matrix: { complete: config.analysis.matrixComplete },
56
- template_dirs: config.analysis.templateDirs,
57
- template_delimiters: {
58
- left: config.analysis.templateDelimiters.left,
59
- right: config.analysis.templateDelimiters.right,
60
- },
61
- },
62
- consumers: { complete: config.consumersComplete },
63
- roots: { patterns: config.rootPatterns },
64
- severity: Object.fromEntries([...config.severity].sort(([a], [b]) => (a < b ? -1 : 1))),
65
- exemptions: { disabled: config.exemptionsDisabled },
66
- reporters: {
67
- formats: config.reporters.formats,
68
- sort: config.reporters.sort,
69
- cascade: config.reporters.cascade,
70
- max_findings: config.reporters.maxFindings,
71
- fail_on: config.reporters.failOn,
72
- },
73
- providers: { analyzers: config.providers.analyzers.map(providerOf) },
74
- go: {},
75
- ts: {
76
- test_files: config.ts.testFiles,
77
- entry_files: config.ts.entryFiles,
78
- component_extensions: config.ts.componentExtensions,
79
- disabled_conventions: config.ts.disabledConventions,
80
- injection_registrations: config.ts.injectionRegistrations.map(declarationOf),
81
- lifecycle_contracts: config.ts.lifecycleContracts.map((entry) => ({
82
- components: entry.components.map(declarationOf),
83
- bases: entry.bases.map(declarationOf),
84
- members: entry.members,
85
- })),
86
- serializers: config.ts.serializers.map(declarationOf),
87
- roots_complete: config.ts.rootsComplete,
88
- },
89
- provenance: Object.fromEntries(
90
- [...provenance]
91
- .sort(([a], [b]) => (a < b ? -1 : 1))
92
- .map(([path, origin]) => [path, renderOrigin(origin)]),
93
- ),
94
- };
95
- return `${JSON.stringify(document, undefined, 2)}\n`;
11
+ export function printConfig(config: ResolvedConfig, provenance: Provenance): string {
12
+ const origins = [...provenance]
13
+ .sort(([a], [b]) => bytewise(a, b))
14
+ .map(([path, origin]): [string, string] => [path, renderOrigin(origin)]);
15
+ return `${JSON.stringify({ ...config, provenance: Object.fromEntries(origins) }, undefined, 2)}\n`;
96
16
  }
package/src/product.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { LANGUAGE_ROWS } from "./contract/contract.generated.ts";
2
+ import type { Writer } from "./run.ts";
3
+
4
+ /** The language this analyzer reads. */
5
+ export const LANGUAGE = "ts";
6
+
7
+ /** The analyzer's name: the one its language's row names, which every output spells it with. */
8
+ export const PRODUCT = LANGUAGE_ROWS[LANGUAGE].analyzer;
9
+
10
+ /** Writes one diagnostic line to the error stream, named for the analyzer. */
11
+ export function diagnose(err: Writer, text: string): void {
12
+ err.write(`${PRODUCT}: ${text}\n`);
13
+ }
14
+
15
+ /** The text one thrown value carries. */
16
+ export function message(error: unknown): string {
17
+ return error instanceof Error ? error.message : String(error);
18
+ }
package/src/query.ts CHANGED
@@ -211,6 +211,8 @@ export interface Queries {
211
211
  symbolOfType(type: Type): Answer<TSSymbol | undefined>;
212
212
  signaturesOf(type: Type, kind: SignatureKind): Answer<readonly Signature[]>;
213
213
  resolvedSignature(node: Node): Answer<Signature | undefined>;
214
+ /** The signature each call-like node resolves to. */
215
+ resolvedSignatures(nodes: readonly Node[], cap?: number): Answer<Signature | undefined>[];
214
216
  parameterType(signature: Signature, index: number): Answer<Type | undefined>;
215
217
  /** The generic signature an instantiated one instantiates; none for another signature. */
216
218
  genericOf(signature: Signature): Answer<Signature | undefined>;
@@ -544,6 +546,15 @@ export function queriesOf(
544
546
  () => nodeLocation(node),
545
547
  () => checker.getResolvedSignature(node),
546
548
  ),
549
+ resolvedSignatures: (nodes, cap = DEFAULT_BATCH_CAP) =>
550
+ each(
551
+ "getResolvedSignature",
552
+ nodes,
553
+ cap,
554
+ nodeLocation,
555
+ (node) => checker.getResolvedSignature.gen(node),
556
+ (node) => checker.getResolvedSignature(node),
557
+ ),
547
558
  parameterType: (signature, index) =>
548
559
  one(
549
560
  "getParameterType",
package/src/ref.ts CHANGED
@@ -52,6 +52,7 @@ export const REF_EXPRESSIONS: readonly string[] = [
52
52
  `^${SCOPE}#${TS_HEAD}:import$`,
53
53
  `^${SCOPE}#${TS_HEAD}(?:\\.${TS_MEMBER})+$`,
54
54
  `^${SCOPE}#${TS_HEAD}(?:\\.${TS_MEMBER})+:static$`,
55
+ `^${SCOPE}#${TS_HEAD}(?:\\.${TS_MEMBER})+\\.${TS_QUOTED}:case$`,
55
56
  `^${SCOPE}#${TS_HEAD}(?:\\.${TS_MEMBER})*(?::static)?<${TS_IDENT}>$`,
56
57
  `^${SCOPE}#(?:${TS_HEAD}(?:\\.${TS_MEMBER})*(?::static)?)?\\(${TS_IDENT}\\)$`,
57
58
  `^${MANIFEST_SCOPE}#${TS_PACKAGE}:(?:dependency|dev-dependency|peer-dependency)$`,
package/src/references.ts CHANGED
@@ -15,7 +15,10 @@ import {
15
15
  isIdentifier,
16
16
  isImportDeclaration,
17
17
  isImportEqualsDeclaration,
18
+ isInterfaceDeclaration,
18
19
  isMetaProperty,
20
+ isModuleBlock,
21
+ isModuleDeclaration,
19
22
  isNewExpression,
20
23
  isNamespaceExport,
21
24
  isNoSubstitutionTemplateLiteral,
@@ -35,13 +38,13 @@ import {
35
38
  type ParameterDeclaration,
36
39
  type SourceFile,
37
40
  } from "@typescript/native/ast";
41
+ import { parenthesesRemoved } from "./unwrap.ts";
38
42
  import type { Symbol as TSSymbol } from "@typescript/native/sync";
39
43
  import { aliasChains, type ChainTarget } from "./alias-chain.ts";
40
44
  import { contextualUses, type FlowAt, type LiteralAt } from "./contextual-uses.ts";
41
45
  import { destructuring, propertyReadsOf, type PropertyRead } from "./destructuring.ts";
42
46
  import { specifierCandidates } from "./configuration-files.ts";
43
47
  import { namespaceUses } from "./namespace-objects.ts";
44
- import { globExpression } from "./glob.ts";
45
48
  import {
46
49
  declarationsByName,
47
50
  isParameterProperty,
@@ -49,7 +52,7 @@ import {
49
52
  type Inventory,
50
53
  type InventorySymbol,
51
54
  } from "./inventory.ts";
52
- import { augmentationReferences } from "./augmentations.ts";
55
+ import { ambientGlobalInterfaces, augmentationReferences } from "./augmentations.ts";
53
56
  import { elementAccessReferences } from "./element-access.ts";
54
57
  import { indexedAccessReferences } from "./indexed-access.ts";
55
58
  import { keyofComparisonReferences } from "./keyof-comparisons.ts";
@@ -70,6 +73,8 @@ import {
70
73
  type KeyedAccess,
71
74
  } from "./symbol-keys.ts";
72
75
  import { handedOn, valuesOf } from "./value-flow.ts";
76
+ import { bytewise } from "./text/compare.ts";
77
+ import { globExpression } from "./text/glob.ts";
73
78
 
74
79
  /**
75
80
  * Every use one project's own files make of the project's own declarations.
@@ -312,15 +317,6 @@ function isName(node: Node): boolean {
312
317
  return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PrivateIdentifier;
313
318
  }
314
319
 
315
- /** The expression one node is, through however many parentheses are written around it. */
316
- function unparenthesized(node: Node): Node {
317
- let held = node;
318
- while (isParenthesizedExpression(held)) {
319
- held = held.expression;
320
- }
321
- return held;
322
- }
323
-
324
320
  /**
325
321
  * Records the name one store writes into.
326
322
  *
@@ -330,7 +326,7 @@ function unparenthesized(node: Node): Node {
330
326
  * target writes each name it binds, through its spreads and nested patterns.
331
327
  */
332
328
  function markWrite(target: Node, writes: Set<number>, accesses: Set<Node>): void {
333
- const held = unparenthesized(target);
329
+ const held = parenthesesRemoved(target);
334
330
  if (isName(held)) {
335
331
  writes.add(held.pos);
336
332
  return;
@@ -345,7 +341,7 @@ function markWrite(target: Node, writes: Set<number>, accesses: Set<Node>): void
345
341
  accesses.add(held);
346
342
  // A store through a member's value lands in storage every holder of the value
347
343
  // reads, so it reads the member; a variable only ever stored into stays written.
348
- if (!isPropertyAccessExpression(unparenthesized(held.expression))) {
344
+ if (!isPropertyAccessExpression(parenthesesRemoved(held.expression))) {
349
345
  markWrite(held.expression, writes, accesses);
350
346
  }
351
347
  return;
@@ -521,7 +517,7 @@ interface FileSites {
521
517
 
522
518
  /** Whether one node is `import.meta.url`. */
523
519
  function isImportMetaURL(node: Node): boolean {
524
- const at = unparenthesized(node);
520
+ const at = parenthesesRemoved(node);
525
521
  return (
526
522
  isPropertyAccessExpression(at) &&
527
523
  isMetaProperty(at.expression) &&
@@ -533,7 +529,7 @@ function isImportMetaURL(node: Node): boolean {
533
529
  /** The module URL one node builds, where it builds one. */
534
530
  function moduleURLOf(node: Node): ModuleURL | undefined {
535
531
  const relative = (argument: Node | undefined): string | undefined => {
536
- const at = argument === undefined ? undefined : unparenthesized(argument);
532
+ const at = argument === undefined ? undefined : parenthesesRemoved(argument);
537
533
  return at !== undefined &&
538
534
  (isStringLiteral(at) || isNoSubstitutionTemplateLiteral(at)) &&
539
535
  /^\.\.?\//u.test(at.text)
@@ -548,7 +544,7 @@ function moduleURLOf(node: Node): ModuleURL | undefined {
548
544
  : undefined;
549
545
  }
550
546
  if (isCallExpression(node)) {
551
- const callee = unparenthesized(node.expression);
547
+ const callee = parenthesesRemoved(node.expression);
552
548
  const specifier = relative(node.arguments[0]);
553
549
  if (
554
550
  specifier !== undefined &&
@@ -866,7 +862,7 @@ export function testFileRulesOf(
866
862
  };
867
863
  }),
868
864
  ...(support === 0 ? [] : [{ rule: TEST_SUPPORT_RULE, matched: support }]),
869
- ].sort((a, b) => compare(a.rule, b.rule));
865
+ ].sort((a, b) => bytewise(a.rule, b.rule));
870
866
  }
871
867
 
872
868
  const importedCache = new WeakMap<
@@ -1000,7 +996,7 @@ function aloneIn(
1000
996
  nodes.every((node) => sameTarget.some((one) => inside(node, one.value))),
1001
997
  )
1002
998
  .map(([name]) => name)
1003
- .sort(compare);
999
+ .sort(bytewise);
1004
1000
  if (names.length > 0) {
1005
1001
  result.push({ index: write.index, names });
1006
1002
  }
@@ -1008,14 +1004,6 @@ function aloneIn(
1008
1004
  return result;
1009
1005
  }
1010
1006
 
1011
- /** Two strings ordered bytewise, which is the order every set of a run is read in. */
1012
- function compare(a: string, b: string): number {
1013
- if (a === b) {
1014
- return 0;
1015
- }
1016
- return a < b ? -1 : 1;
1017
- }
1018
-
1019
1007
  /**
1020
1008
  * Every reference one project's own files make to the project's own declarations, in
1021
1009
  * position order, and the files the test-file rules classified.
@@ -1610,6 +1598,39 @@ export function references<Brand>(
1610
1598
  }
1611
1599
  }
1612
1600
 
1601
+ /** The ambient global interfaces of the walked files that merge into a declaration outside the project's own files. */
1602
+ const libraryMerged = (scripts: ReadonlySet<string>): ReadonlySet<string> => {
1603
+ const names = new Map<string, Node>();
1604
+ const visit = (node: Node): void => {
1605
+ if (isInterfaceDeclaration(node)) {
1606
+ const id = declaredAt(node);
1607
+ if (id !== undefined) {
1608
+ names.set(id, node.name);
1609
+ }
1610
+ } else if (isModuleDeclaration(node) || isModuleBlock(node)) {
1611
+ node.forEachChild(visit);
1612
+ }
1613
+ };
1614
+ for (const { file } of walked) {
1615
+ file.forEachChild(visit);
1616
+ }
1617
+ const global = new Set(ambientGlobalInterfaces(held, [...names.keys()], scripts));
1618
+ const asked = [...names].filter(([id]) => global.has(id));
1619
+ if (asked.length === 0) {
1620
+ return new Set();
1621
+ }
1622
+ const answers = project.symbolsAt(asked.map(([, name]) => project.handle(name)));
1623
+ return new Set(
1624
+ asked.flatMap(([id], at) => {
1625
+ const symbol = answers[at];
1626
+ return typeof symbol === "object" &&
1627
+ symbol.declarations.some((handle) => !ownPaths.has(handle.path))
1628
+ ? [id]
1629
+ : [];
1630
+ }),
1631
+ );
1632
+ };
1633
+
1613
1634
  const usesOf = new Map(walked.map((one) => [one.file, one.uses]));
1614
1635
  for (const [file, writes] of valued) {
1615
1636
  const uses = usesOf.get(file) ?? [];
@@ -1637,13 +1658,13 @@ export function references<Brand>(
1637
1658
  const scripts = new Set(
1638
1659
  walked.filter((one) => one.file.externalModuleIndicator === undefined).map((one) => one.fileId),
1639
1660
  );
1640
- found.push(...augmentationReferences(held, found, scripts));
1661
+ found.push(...augmentationReferences(held, found, scripts, libraryMerged(scripts)));
1641
1662
  found.sort(
1642
1663
  (a, b) =>
1643
1664
  byPosition(a.position, b.position) ||
1644
- compare(a.to, b.to) ||
1645
- compare(a.from, b.from) ||
1646
- compare(a.use, b.use),
1665
+ bytewise(a.to, b.to) ||
1666
+ bytewise(a.from, b.from) ||
1667
+ bytewise(a.use, b.use),
1647
1668
  );
1648
1669
 
1649
1670
  return {
@@ -1651,7 +1672,7 @@ export function references<Brand>(
1651
1672
  consumer === undefined
1652
1673
  ? found
1653
1674
  : found.map((reference) => ({ ...reference, consumer: consumer.id })),
1654
- testFilePaths: tests.sort(compare),
1675
+ testFilePaths: tests.sort(bytewise),
1655
1676
  cost: {
1656
1677
  batched,
1657
1678
  fileBatches,
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Holds a report to `contract/report.schema.json` and `contract/finding.schema.json`
3
+ * before it is written, reading the schemas the generator copies from the Contract, so
4
+ * this analyzer never writes a document a reader refuses.
5
+ */
6
+
7
+ import { REPORT_SCHEMAS, type Report } from "./contract/report.generated.ts";
8
+ import { schemaRefusals, type SchemaRefusal } from "./schema.ts";
9
+
10
+ const reportSchema = schemaRefusals(REPORT_SCHEMAS, "report.schema.json");
11
+
12
+ /**
13
+ * What the report schema refuses in one report document, none for an instance, and, as
14
+ * no schema keyword can state it, a components listing that is not one entry per
15
+ * component a finding or an edge evaluation's finding names.
16
+ */
17
+ export function reportRefusals(document: unknown): readonly SchemaRefusal[] {
18
+ const found = reportSchema(document);
19
+ return found.length > 0 ? found : listingRefusals(document as Report);
20
+ }
21
+
22
+ /** The components listing held to the components the findings name, once each. */
23
+ function listingRefusals(report: Report): readonly SchemaRefusal[] {
24
+ if (report.components === undefined) {
25
+ return [];
26
+ }
27
+ const refuse = (pointer: string, reason: string): SchemaRefusal[] => [
28
+ { pointer, keyword: "components", reason, member: "" },
29
+ ];
30
+ const listed = new Set<string>();
31
+ for (const [i, entry] of report.components.entries()) {
32
+ if (listed.has(entry.id)) {
33
+ return refuse(
34
+ `/components/${String(i)}`,
35
+ `the entry lists component ${entry.id} a second time`,
36
+ );
37
+ }
38
+ listed.add(entry.id);
39
+ }
40
+ const named = [
41
+ ...report.findings.map(
42
+ (finding, i) => [`/findings/${String(i)}`, finding.component.id] as const,
43
+ ),
44
+ ...report.edge_evaluations.flatMap((one, i) =>
45
+ one.finding === undefined
46
+ ? []
47
+ : [[`/edge_evaluations/${String(i)}/finding`, one.finding.component.id] as const],
48
+ ),
49
+ ];
50
+ for (const [pointer, id] of named) {
51
+ if (!listed.has(id)) {
52
+ return refuse(
53
+ pointer,
54
+ `the finding names component ${id}, which the components listing lacks`,
55
+ );
56
+ }
57
+ }
58
+ const ids = new Set(named.map(([, id]) => id));
59
+ for (const [i, entry] of report.components.entries()) {
60
+ if (!ids.has(entry.id)) {
61
+ return refuse(
62
+ `/components/${String(i)}`,
63
+ `the entry lists component ${entry.id}, which no finding names`,
64
+ );
65
+ }
66
+ }
67
+ return [];
68
+ }