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

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 (207) hide show
  1. package/dist/src/alias-chain.js +0 -5
  2. package/dist/src/amd.js +81 -0
  3. package/dist/src/analysis.js +94 -54
  4. package/dist/src/augmentations.js +23 -24
  5. package/dist/src/bindings.js +382 -0
  6. package/dist/src/calls.js +25 -8
  7. package/dist/src/commands/read.js +6 -7
  8. package/dist/src/commands/tools.js +5 -9
  9. package/dist/src/component-files.js +3 -2
  10. package/dist/src/conformance-record.js +1 -1
  11. package/dist/src/consumers.js +9 -10
  12. package/dist/src/contextual-uses.js +1 -17
  13. package/dist/src/contract/config.generated.js +5 -2
  14. package/dist/src/contract/contract.generated.js +119 -2
  15. package/dist/src/contract/report.generated.js +6 -2
  16. package/dist/src/dependencies.js +98 -71
  17. package/dist/src/destructuring.js +1 -19
  18. package/dist/src/element-access.js +179 -164
  19. package/dist/src/entries/build-scripts.js +222 -0
  20. package/dist/src/{configuration-files.js → entries/configuration-files.js} +22 -64
  21. package/dist/src/{convention-rows.js → entries/convention-rows.js} +120 -27
  22. package/dist/src/entries/conventions.js +739 -0
  23. package/dist/src/{html-entries.js → entries/html-entries.js} +9 -12
  24. package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +3 -3
  25. package/dist/src/entries/run-directories.js +77 -0
  26. package/dist/src/entries/sources.js +144 -0
  27. package/dist/src/entries/static-module.js +670 -0
  28. package/dist/src/entries/test-runners.js +684 -0
  29. package/dist/src/{workflow-steps.js → entries/workflow-steps.js} +18 -8
  30. package/dist/src/extensions.js +37 -0
  31. package/dist/src/facts.js +38 -0
  32. package/dist/src/findings/completion.js +40 -4
  33. package/dist/src/findings/emitters.js +5 -8
  34. package/dist/src/{implementations.js → findings/implementations.js} +3 -3
  35. package/dist/src/{in-operands.js → findings/in-operands.js} +2 -2
  36. package/dist/src/findings/self-check.js +36 -20
  37. package/dist/src/findings-pass.js +5 -6
  38. package/dist/src/framework-lifecycle.js +42 -15
  39. package/dist/src/fs/manifest.js +15 -20
  40. package/dist/src/generated-file.js +172 -29
  41. package/dist/src/graph.js +14 -8
  42. package/dist/src/import-bindings.js +73 -28
  43. package/dist/src/index/sites.js +557 -0
  44. package/dist/src/index.js +1 -1
  45. package/dist/src/indexed-access.js +523 -478
  46. package/dist/src/injection-container.js +1 -1
  47. package/dist/src/interface-satisfaction.js +1 -1
  48. package/dist/src/inventory/commonjs.js +62 -17
  49. package/dist/src/inventory/enumerate.js +2 -1
  50. package/dist/src/javascript-file.js +26 -11
  51. package/dist/src/keyof-comparisons.js +124 -103
  52. package/dist/src/load/classify.js +28 -9
  53. package/dist/src/load/diagnostics.js +8 -1
  54. package/dist/src/load/discover.js +15 -7
  55. package/dist/src/load/expected-module.js +2 -2
  56. package/dist/src/load/plan.js +2 -2
  57. package/dist/src/load/specifiers.js +2 -4
  58. package/dist/src/load/synthesize.js +10 -5
  59. package/dist/src/load/workspace-manifests.js +1 -1
  60. package/dist/src/load/workspace-resolution.js +2 -2
  61. package/dist/src/matrix.js +12 -17
  62. package/dist/src/module-declarations.js +70 -85
  63. package/dist/src/overrides.js +134 -112
  64. package/dist/src/paths.js +56 -1
  65. package/dist/src/platform-calls.js +78 -0
  66. package/dist/src/platform-symbols.js +134 -0
  67. package/dist/src/references/read-rule.js +1 -0
  68. package/dist/src/references/read-rules.js +48 -0
  69. package/dist/src/references/sink.js +151 -0
  70. package/dist/src/references/sources.js +450 -0
  71. package/dist/src/references.js +111 -1116
  72. package/dist/src/resolve.js +1 -1
  73. package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +7 -4
  74. package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +5 -8
  75. package/dist/src/{manifest.js → roots/manifest.js} +4 -4
  76. package/dist/src/roots/published-reach.js +412 -0
  77. package/dist/src/roots/root-set.js +489 -0
  78. package/dist/src/roots/unseen-loads.js +236 -0
  79. package/dist/src/serialization-contract.js +9 -4
  80. package/dist/src/source-files.js +5 -9
  81. package/dist/src/stores.js +4 -2
  82. package/dist/src/suppress.js +30 -32
  83. package/dist/src/sweep.js +25 -13
  84. package/dist/src/symbol-keys.js +89 -83
  85. package/dist/src/tests/file-class.js +76 -0
  86. package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
  87. package/dist/src/{test-support.js → tests/support.js} +5 -12
  88. package/dist/src/type-parameter-reads.js +178 -139
  89. package/dist/src/unimplemented-callees.js +262 -242
  90. package/dist/src/verbs/analyze.js +15 -3
  91. package/dist/src/verbs/describe.js +5 -3
  92. package/dist/src/verbs/explain.js +50 -28
  93. package/dist/src/verbs/print-retained.js +2 -2
  94. package/jsr.json +1 -1
  95. package/package.json +3 -3
  96. package/src/alias-chain.ts +0 -7
  97. package/src/amd.ts +130 -0
  98. package/src/analysis.ts +156 -70
  99. package/src/augmentations.ts +41 -27
  100. package/src/automatic-types.ts +1 -1
  101. package/src/bindings.ts +460 -0
  102. package/src/calls.ts +25 -7
  103. package/src/commands/read.ts +6 -8
  104. package/src/commands/tools.ts +5 -11
  105. package/src/component-files.ts +4 -2
  106. package/src/conformance-record.ts +1 -1
  107. package/src/consumers.ts +12 -11
  108. package/src/contextual-uses.ts +2 -22
  109. package/src/contract/config.generated.ts +5 -2
  110. package/src/contract/contract.generated.ts +138 -2
  111. package/src/contract/report.generated.ts +6 -2
  112. package/src/dependencies.ts +117 -71
  113. package/src/destructuring.ts +1 -31
  114. package/src/element-access.ts +188 -182
  115. package/src/entries/build-scripts.ts +292 -0
  116. package/src/{configuration-files.ts → entries/configuration-files.ts} +33 -82
  117. package/src/{convention-rows.ts → entries/convention-rows.ts} +167 -28
  118. package/src/{conventions.ts → entries/conventions.ts} +349 -523
  119. package/src/{html-entries.ts → entries/html-entries.ts} +19 -18
  120. package/src/{mdx-entries.ts → entries/mdx-entries.ts} +5 -5
  121. package/src/entries/run-directories.ts +108 -0
  122. package/src/entries/sources.ts +266 -0
  123. package/src/entries/static-module.ts +858 -0
  124. package/src/entries/test-runners.ts +868 -0
  125. package/src/{workflow-steps.ts → entries/workflow-steps.ts} +29 -11
  126. package/src/exempt.ts +3 -2
  127. package/src/extensions.ts +45 -0
  128. package/src/facts.ts +46 -0
  129. package/src/file-facts.ts +1 -1
  130. package/src/findings/boundary.ts +2 -0
  131. package/src/findings/completion.ts +43 -4
  132. package/src/findings/emitter.ts +1 -1
  133. package/src/findings/emitters.ts +14 -10
  134. package/src/{implementations.ts → findings/implementations.ts} +4 -4
  135. package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
  136. package/src/findings/self-check.ts +41 -22
  137. package/src/findings/visibility-narrowing.ts +1 -1
  138. package/src/findings-pass.ts +5 -12
  139. package/src/framework-lifecycle.ts +43 -15
  140. package/src/fs/manifest.ts +25 -21
  141. package/src/generated-file.ts +240 -36
  142. package/src/graph.ts +27 -11
  143. package/src/import-bindings.ts +98 -29
  144. package/src/index/sites.ts +781 -0
  145. package/src/index.ts +1 -1
  146. package/src/indexed-access.ts +570 -527
  147. package/src/injection-container.ts +1 -1
  148. package/src/interface-satisfaction.ts +1 -1
  149. package/src/inventory/commonjs.ts +102 -17
  150. package/src/inventory/enumerate.ts +2 -1
  151. package/src/javascript-file.ts +29 -12
  152. package/src/keyof-comparisons.ts +131 -116
  153. package/src/load/classify.ts +49 -13
  154. package/src/load/diagnostics.ts +11 -1
  155. package/src/load/discover.ts +27 -11
  156. package/src/load/expected-module.ts +8 -2
  157. package/src/load/plan.ts +11 -2
  158. package/src/load/specifiers.ts +8 -5
  159. package/src/load/synthesize.ts +12 -6
  160. package/src/load/test-globals.ts +1 -1
  161. package/src/load/workspace-manifests.ts +1 -1
  162. package/src/load/workspace-resolution.ts +9 -2
  163. package/src/load/workspace-run.ts +1 -1
  164. package/src/matrix.ts +13 -17
  165. package/src/module-declarations.ts +74 -98
  166. package/src/overrides.ts +162 -134
  167. package/src/paths.ts +65 -1
  168. package/src/platform-calls.ts +119 -0
  169. package/src/platform-symbols.ts +189 -0
  170. package/src/references/read-rule.ts +60 -0
  171. package/src/references/read-rules.ts +55 -0
  172. package/src/references/sink.ts +213 -0
  173. package/src/references/sources.ts +632 -0
  174. package/src/references.ts +174 -1492
  175. package/src/report.ts +2 -2
  176. package/src/resolve.ts +1 -1
  177. package/src/{configured-declarations.ts → roots/configured-declarations.ts} +10 -7
  178. package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +5 -8
  179. package/src/{manifest.ts → roots/manifest.ts} +5 -5
  180. package/src/roots/published-reach.ts +481 -0
  181. package/src/{roots.ts → roots/root-set.ts} +203 -630
  182. package/src/roots/unseen-loads.ts +345 -0
  183. package/src/serialization-contract.ts +9 -4
  184. package/src/source-files.ts +5 -10
  185. package/src/stores.ts +13 -2
  186. package/src/suppress.ts +48 -33
  187. package/src/sweep.ts +37 -14
  188. package/src/symbol-keys.ts +102 -102
  189. package/src/tests/file-class.ts +118 -0
  190. package/src/{outside-tests.ts → tests/outside.ts} +16 -10
  191. package/src/{test-support.ts → tests/support.ts} +15 -21
  192. package/src/type-parameter-reads.ts +187 -158
  193. package/src/unimplemented-callees.ts +297 -280
  194. package/src/verbs/analyze.ts +19 -5
  195. package/src/verbs/describe.ts +5 -2
  196. package/src/verbs/explain.ts +75 -32
  197. package/src/verbs/print-retained.ts +2 -2
  198. package/dist/src/build-scripts.js +0 -418
  199. package/dist/src/conventions.js +0 -907
  200. package/dist/src/entry-points.js +0 -444
  201. package/dist/src/own-body.js +0 -28
  202. package/dist/src/roots.js +0 -865
  203. package/dist/src/unseen-loads.js +0 -189
  204. package/src/build-scripts.ts +0 -536
  205. package/src/entry-points.ts +0 -584
  206. package/src/own-body.ts +0 -42
  207. package/src/unseen-loads.ts +0 -282
@@ -2,9 +2,13 @@
2
2
  * The generated-file class on TypeScript: every declaration in a file that marks itself
3
3
  * generated, below a directory an applied convention row names as generated, or minified,
4
4
  * is retained, because a generator owns the file and a deletion there is undone at the next
5
- * generation.
5
+ * generation. The same decision is each `generated` entry of the facts document.
6
6
  */
7
- import { minified } from "./javascript-file.js";
7
+ import { isIdentifier, isImportDeclaration, isPropertyAssignment, isStringLiteralLikeNode, isVariableStatement, NodeFlags, } from "@typescript/native/ast";
8
+ import { GENERATORS } from "./contract/contract.generated.js";
9
+ import { minifiedBy } from "./javascript-file.js";
10
+ import { bytewise } from "./text/compare.js";
11
+ import { satisfies } from "./version-range.js";
8
12
  /** The generated-code header, matched against each line of a leading comment. */
9
13
  const HEADER_LINE = /^\/\/ Code generated .* DO NOT EDIT\.$/u;
10
14
  /** The documentation tag `@generated` as a word of a comment. */
@@ -15,9 +19,17 @@ const GENERATED_BY = /^\/\/ Generated by \S/u;
15
19
  const GENERATED_WORD = /\bgenerated\b/iu;
16
20
  /** The phrases a generator's notice asks a reader not to edit the file by. */
17
21
  const KEEP_OUT = /\b(?:do not edit|will be overwritten|should not make any changes)\b/iu;
18
- /** The comments before a text's first token, a leading hashbang line skipped. */
22
+ /** The comments before a text's first token, a leading hashbang line skipped, each as its lines. */
19
23
  function leadingComments(text) {
20
24
  const comments = [];
25
+ const lineAt = (offset) => text.slice(0, offset).split(/\r\n|[\n\r\u2028\u2029]/u).length;
26
+ const lines = (from, to) => {
27
+ const first = lineAt(from);
28
+ return text
29
+ .slice(from, to)
30
+ .split(/\r\n|[\n\r\u2028\u2029]/u)
31
+ .map((one, index) => ({ text: one, line: first + index }));
32
+ };
21
33
  let at = text.startsWith("\uFEFF") ? 1 : 0;
22
34
  if (text.startsWith("#!", at)) {
23
35
  const end = text.indexOf("\n", at);
@@ -30,7 +42,7 @@ function leadingComments(text) {
30
42
  if (text.startsWith("//", at)) {
31
43
  const end = text.slice(at).search(/[\r\n\u2028\u2029]/u);
32
44
  const stop = end === -1 ? text.length : at + end;
33
- comments.push(text.slice(at, stop));
45
+ comments.push(lines(at, stop));
34
46
  at = stop;
35
47
  }
36
48
  else if (text.startsWith("/*", at)) {
@@ -38,7 +50,7 @@ function leadingComments(text) {
38
50
  if (end === -1) {
39
51
  return comments;
40
52
  }
41
- comments.push(text.slice(at, end + 2));
53
+ comments.push(lines(at, end + 2));
42
54
  at = end + 2;
43
55
  }
44
56
  else {
@@ -46,43 +58,168 @@ function leadingComments(text) {
46
58
  }
47
59
  }
48
60
  }
49
- /** The detail a file's leading comments mark it generated by, or undefined where they mark nothing. */
50
- export function generatedByComment(text) {
51
- const comments = leadingComments(text);
52
- if (comments.some((one) => one.split(/\r?\n/u).some((line) => HEADER_LINE.test(line)))) {
53
- return "generated by the header line its leading comments hold";
61
+ /** The mark one file's leading comments make it generated by, or undefined where they mark nothing. */
62
+ export function generatedMark(text) {
63
+ const lines = leadingComments(text).flat();
64
+ const header = lines.find((one) => HEADER_LINE.test(one.text));
65
+ if (header !== undefined) {
66
+ return { evidence: "header", line: header.line, detail: header.text };
54
67
  }
55
- if (comments.some((one) => GENERATED_TAG.test(one))) {
56
- return "generated by the @generated tag its leading comments hold";
68
+ const tag = lines.find((one) => GENERATED_TAG.test(one.text));
69
+ if (tag !== undefined) {
70
+ return { evidence: "tag", line: tag.line, detail: tag.text };
57
71
  }
58
- const notice = comments.join("\n");
59
- if (GENERATED_BY.test(comments[0] ?? "") ||
60
- (GENERATED_WORD.test(notice) && KEEP_OUT.test(notice))) {
61
- return "generated by the notice its leading comments hold";
72
+ const [first] = lines;
73
+ if (first !== undefined && GENERATED_BY.test(first.text)) {
74
+ return { evidence: "notice", line: first.line, detail: first.text };
75
+ }
76
+ const notice = lines.map((one) => one.text).join("\n");
77
+ if (GENERATED_WORD.test(notice) && KEEP_OUT.test(notice)) {
78
+ const marked = lines.find((one) => GENERATED_WORD.test(one.text) || KEEP_OUT.test(one.text));
79
+ return marked === undefined
80
+ ? undefined
81
+ : { evidence: "notice", line: marked.line, detail: marked.text };
82
+ }
83
+ return undefined;
84
+ }
85
+ /** What one form's first group captures, for each line the form matches. */
86
+ function after(lines, form) {
87
+ return lines.flatMap((one) => form.exec(one.text)?.[1] ?? []);
88
+ }
89
+ /**
90
+ * Where each plugin's output states its version and its schema files, as `references-ts.36`
91
+ * states: a plugin missing here states no version, and one with no schema form names none.
92
+ */
93
+ const STATEMENTS = {
94
+ "protoc-gen-es": {
95
+ version: /^\/\/ @generated by protoc-gen-es (\S+)/u,
96
+ schemaFiles: /^\/\/ @generated from file (\S+)/u,
97
+ },
98
+ "protoc-gen-connect-es": {
99
+ version: /^\/\/ @generated by protoc-gen-connect-es (\S+)/u,
100
+ schemaFiles: /^\/\/ @generated from file (\S+)/u,
101
+ },
102
+ "protobuf-ts": {
103
+ version: /^\/\/ @generated by protobuf-ts (\S+)/u,
104
+ schemaFiles: /^\/\/ @generated from protobuf file "([^"]+)"/u,
105
+ },
106
+ "ts-proto": {
107
+ version: /^\/\/\s+protoc-gen-ts_proto\s+(\S+)$/u,
108
+ schemaFiles: /^\/\/ source: (.+)$/u,
109
+ },
110
+ "protoc-gen-grpc-web": {
111
+ version: /^\/\/\s+protoc-gen-grpc-web\s+(\S+)$/u,
112
+ schemaFiles: /^\/\/ source: (.+)$/u,
113
+ },
114
+ "protoc-gen-js": { schemaFiles: /^\/\/ source: (.+)$/u },
115
+ "ts-protoc-gen": { schemaFiles: /^\/\/ file: (.+)$/u },
116
+ };
117
+ /** A ts-proto service path literal: `/<package>.<service>/<method>`. */
118
+ const SERVICE_PATH = /^\/(?:\w+\.)+\w+\/\w+$/u;
119
+ /** Whether one ts-proto file writes a service path, as `references-ts.36` reads one. */
120
+ function writesServicePath(file) {
121
+ const named = file.statements.some((statement) => isVariableStatement(statement) &&
122
+ (statement.declarationList.flags & NodeFlags.Const) !== 0 &&
123
+ statement.declarationList.declarations.some((one) => isIdentifier(one.name) &&
124
+ one.name.text.endsWith("ServiceName") &&
125
+ one.initializer !== undefined &&
126
+ isStringLiteralLikeNode(one.initializer)));
127
+ let found = named;
128
+ const visit = (node) => {
129
+ if (found) {
130
+ return;
131
+ }
132
+ if (isPropertyAssignment(node) &&
133
+ isIdentifier(node.name) &&
134
+ node.name.text === "path" &&
135
+ isStringLiteralLikeNode(node.initializer) &&
136
+ SERVICE_PATH.test(node.initializer.text)) {
137
+ found = true;
138
+ return;
139
+ }
140
+ node.forEachChild(visit);
141
+ };
142
+ file.forEachChild(visit);
143
+ return found;
144
+ }
145
+ /** Whether one file imports a module by its specifier. */
146
+ function imports(file, module) {
147
+ return file.statements.some((statement) => isImportDeclaration(statement) &&
148
+ isStringLiteralLikeNode(statement.moduleSpecifier) &&
149
+ statement.moduleSpecifier.text === module);
150
+ }
151
+ /** The option sets the rows of `references-ts.36` name, by row; every other row's is met by any output. */
152
+ const OPTION_SETS = {
153
+ "ts-proto-2-output-schema": (file) => imports(file, "ts-proto-descriptors"),
154
+ "ts-proto-2": writesServicePath,
155
+ "ts-proto-2-types": (file) => !writesServicePath(file),
156
+ };
157
+ /** The first generator row one file matches, in `generators.json`'s order, as `bindings.3` reads it. */
158
+ export function generatorOf(file) {
159
+ const lines = leadingComments(file.text).flat();
160
+ for (const row of GENERATORS) {
161
+ const header = new RegExp(row.header, "u");
162
+ if (!lines.some((one) => header.test(one.text))) {
163
+ continue;
164
+ }
165
+ const statements = STATEMENTS[row.plugin] ?? {};
166
+ const [version] = statements.version === undefined ? [] : after(lines, statements.version);
167
+ if ("versions" in row &&
168
+ (version === undefined ||
169
+ !satisfies(version.replace(/^v/u, ""), `>=${row.versions.min.replace(/^v/u, "")} <=${row.versions.max.replace(/^v/u, "")}`))) {
170
+ continue;
171
+ }
172
+ if (OPTION_SETS[row.id]?.(file) === false) {
173
+ continue;
174
+ }
175
+ const schemaFiles = statements.schemaFiles === undefined
176
+ ? []
177
+ : [
178
+ ...new Set(after(lines, statements.schemaFiles).filter((one) => one.endsWith(".proto"))),
179
+ ].sort(bytewise);
180
+ return { row: row.id, ...(version === undefined ? {} : { version }), schemaFiles };
62
181
  }
63
182
  return undefined;
64
183
  }
65
184
  /**
66
- * Every own file of one project that is generated, by its path below the target root,
67
- * with the detail a retention in it names: its leading comments' mark, else the row
68
- * naming its directory as generated, else its minified text.
185
+ * Every own file of one project that is generated, by its path below the target root, with
186
+ * what decided it: its leading comments' mark, else the row naming its directory as
187
+ * generated, else its minified name or text; and the generator row it matches.
69
188
  */
70
189
  export function generatedFilesOf(files, byRow) {
71
190
  const found = new Map();
72
191
  for (const [path, file] of files.byPath) {
73
192
  const row = byRow.get(path);
74
- const detail = generatedByComment(file.text) ??
193
+ const minified = row === undefined ? minifiedBy(file.fileName, file.text) : undefined;
194
+ const fact = generatedMark(file.text) ??
75
195
  (row === undefined
76
- ? minified(file.fileName, file.text)
77
- ? "generated as a minified JavaScript file"
78
- : undefined
79
- : `generated below a directory the convention row ${row} names`);
80
- if (detail !== undefined) {
81
- found.set(path, detail);
196
+ ? minified === undefined
197
+ ? undefined
198
+ : { evidence: "minified", line: 1, detail: minified }
199
+ : { evidence: "convention", line: 1, detail: row });
200
+ if (fact === undefined) {
201
+ continue;
82
202
  }
203
+ const generator = generatorOf(file);
204
+ found.set(path, generator === undefined ? fact : { ...fact, generator });
83
205
  }
84
206
  return found;
85
207
  }
208
+ /** The detail a retention in a generated file names, from what decided the file. */
209
+ function retentionDetail(fact) {
210
+ switch (fact.evidence) {
211
+ case "header":
212
+ return "generated by the header line its leading comments hold";
213
+ case "tag":
214
+ return "generated by the @generated tag its leading comments hold";
215
+ case "notice":
216
+ return "generated by the notice its leading comments hold";
217
+ case "convention":
218
+ return `generated below a directory the convention row ${fact.detail} names`;
219
+ case "minified":
220
+ return "generated as a minified JavaScript file";
221
+ }
222
+ }
86
223
  /** The generated-file detector, recording each file's first line. */
87
224
  export function generatedFile(input) {
88
225
  const generated = input.generated;
@@ -90,9 +227,15 @@ export function generatedFile(input) {
90
227
  return [];
91
228
  }
92
229
  return input.held.symbols.flatMap((symbol) => {
93
- const detail = generated.get(symbol.position.path);
94
- return symbol.kind === "file" || detail === undefined
230
+ const fact = generated.get(symbol.position.path);
231
+ return symbol.kind === "file" || fact === undefined
95
232
  ? []
96
- : [{ id: symbol.id, detail, site: { path: symbol.position.path, line: 1, column: 1 } }];
233
+ : [
234
+ {
235
+ id: symbol.id,
236
+ detail: retentionDetail(fact),
237
+ site: { path: symbol.position.path, line: 1, column: 1 },
238
+ },
239
+ ];
97
240
  });
98
241
  }
package/dist/src/graph.js CHANGED
@@ -4,6 +4,7 @@
4
4
  * nothing, so a sweep over it is a function of the values it was built from, and the
5
5
  * inventory's order is the order every set a sweep answers reads in.
6
6
  */
7
+ import { madeByTestCode } from "./tests/file-class.js";
7
8
  /** The position of a declaration the inventory does not hold. */
8
9
  export const OUTSIDE = -1;
9
10
  /**
@@ -32,8 +33,8 @@ const SWEPT = {
32
33
  };
33
34
  /**
34
35
  * Whether a root of each kind names a caller, which holds it live under both relations: a
35
- * tool reads what it loads and what its strings name, a runtime runs a test, setup or
36
- * worker file, a page's module script, a command, a script or a workflow step, the manifest
36
+ * tool reads what it loads and what its strings name, a runtime runs a test or setup
37
+ * file, a page's module script, a command, a script or a workflow step, the manifest
37
38
  * names what enters the package, an alias global stands for its export, an outside
38
39
  * declaration names a global interface merging into it, and an entry file, a configured root
39
40
  * and a pattern assert a caller. A published API supposes a consumer: reachability alone.
@@ -54,8 +55,6 @@ const NAMES_A_CALLER = {
54
55
  "configuration-string": true,
55
56
  "bundler-entry": true,
56
57
  "test-runner": true,
57
- "browser-tests": true,
58
- worker: true,
59
58
  "html-entry": true,
60
59
  "workflow-step": true,
61
60
  "document-import": true,
@@ -81,24 +80,31 @@ export function graphOf(symbols, references, roots, testFiles, supportFiles = []
81
80
  const at = (id) => index.get(id) ?? OUTSIDE;
82
81
  const tests = new Set(testFiles);
83
82
  const out = symbols.map(() => []);
84
- const made = symbols.map(() => ({ production: 0, test: 0 }));
83
+ const made = symbols.map(() => ({ production: 0, test: 0, consumerTest: 0 }));
85
84
  const consumed = [];
86
85
  for (const reference of references) {
87
86
  const to = at(reference.to);
88
87
  if (to === OUTSIDE) {
89
88
  continue;
90
89
  }
90
+ const test = madeByTestCode(reference, supports);
91
91
  const counts = made[to];
92
92
  if (counts !== undefined) {
93
- if (reference.test) {
93
+ if (test) {
94
94
  counts.test += 1;
95
+ counts.consumerTest += reference.consumer === undefined ? 0 : 1;
95
96
  }
96
97
  else {
97
98
  counts.production += 1;
98
99
  }
99
100
  }
100
- const edge = { to, test: reference.test, evaluation: reference.use === "evaluation" };
101
- if (reference.consumer !== undefined || reference.unheld === true) {
101
+ const edge = {
102
+ to,
103
+ test,
104
+ evaluation: reference.use === "evaluation",
105
+ consumer: reference.consumer !== undefined,
106
+ };
107
+ if (edge.consumer || reference.unheld === true) {
102
108
  consumed.push(edge);
103
109
  continue;
104
110
  }
@@ -2,7 +2,7 @@
2
2
  * The import bindings of one file: the names its import declarations bind, which only
3
3
  * the file itself can read.
4
4
  */
5
- import { isExportSpecifier, isExternalModuleReference, isIdentifier, isImportDeclaration, isImportEqualsDeclaration, isJsxElement, isJsxFragment, isJsxSelfClosingElement, isNamespaceImport, SyntaxKind, } from "@typescript/native/ast";
5
+ import { isExportDeclaration, isExportSpecifier, isExternalModuleReference, isImportDeclaration, isImportEqualsDeclaration, isJsxElement, isJsxFragment, isJsxSelfClosingElement, isNamedExports, isNamespaceImport, SyntaxKind, } from "@typescript/native/ast";
6
6
  /** The extensions of the files whose import bindings are judged: no markup can read them. */
7
7
  const JUDGED = /\.(?:[mc]?ts|tsx|[mc]?js|jsx)$/u;
8
8
  /** The compiler's `jsx` settings whose emit calls no factory the file must hold. */
@@ -13,41 +13,86 @@ const DOC_COMMENT = /\/\*\*[\s\S]*?\*\//gu;
13
13
  const DOC_REFERENCE = /\{[^{}]*\}|@see\s+[A-Za-z_$][\w$.]*/gu;
14
14
  /** The factory a pragma comment names: `@jsx h` or `@jsxFrag Fragment`. */
15
15
  const PRAGMA = /@jsx(?:Frag)?\s+([A-Za-z_$][\w$]*)/gu;
16
+ const bindingsCache = new WeakMap();
16
17
  /**
17
- * The names one file's import declarations bind: the default, named and namespace names of
18
- * an `import` declaration, and the name of an `import x = require()` declaration that is
19
- * not exported. None is judged in a declaration file or a component file.
18
+ * Every name one import declaration binds, in source order: the default, named and
19
+ * namespace names of an `import` declaration and the name of an `import x = …` declaration.
20
+ * A namespace import and an import assignment bind a whole module.
20
21
  */
21
- export function importBindings(file) {
22
- if (file.isDeclarationFile || !JUDGED.test(file.fileName)) {
23
- return [];
22
+ export function declarationBindings(declaration) {
23
+ if (isImportEqualsDeclaration(declaration)) {
24
+ return [
25
+ {
26
+ name: declaration.name,
27
+ equals: {
28
+ exported: (declaration.modifiers ?? []).some((modifier) => modifier.kind === SyntaxKind.ExportKeyword),
29
+ external: isExternalModuleReference(declaration.moduleReference),
30
+ },
31
+ },
32
+ ];
24
33
  }
34
+ const clause = declaration.importClause;
25
35
  const found = [];
26
- for (const statement of file.statements) {
27
- if (isImportDeclaration(statement)) {
28
- const clause = statement.importClause;
29
- if (clause?.name !== undefined) {
30
- found.push(clause.name);
31
- }
32
- const named = clause?.namedBindings;
33
- if (named !== undefined && isNamespaceImport(named)) {
34
- found.push(named.name);
35
- }
36
- else if (named !== undefined) {
37
- for (const element of named.elements) {
38
- found.push(element.name);
39
- }
40
- }
41
- }
42
- else if (isImportEqualsDeclaration(statement) &&
43
- isExternalModuleReference(statement.moduleReference) &&
44
- isIdentifier(statement.name) &&
45
- !(statement.modifiers ?? []).some((modifier) => modifier.kind === SyntaxKind.ExportKeyword)) {
46
- found.push(statement.name);
36
+ if (clause?.name !== undefined) {
37
+ found.push({ name: clause.name, imported: "default" });
38
+ }
39
+ const named = clause?.namedBindings;
40
+ if (named !== undefined && isNamespaceImport(named)) {
41
+ found.push({ name: named.name });
42
+ }
43
+ else if (named !== undefined) {
44
+ for (const element of named.elements) {
45
+ found.push({ name: element.name, imported: element.propertyName?.text ?? element.name.text });
47
46
  }
48
47
  }
49
48
  return found;
50
49
  }
50
+ /** Every name one file's top-level import declarations bind, in source order. */
51
+ export function bindingsOf(file) {
52
+ const known = bindingsCache.get(file);
53
+ if (known !== undefined) {
54
+ return known;
55
+ }
56
+ const found = file.statements.flatMap((statement) => isImportDeclaration(statement) || isImportEqualsDeclaration(statement)
57
+ ? declarationBindings(statement)
58
+ : []);
59
+ bindingsCache.set(file, found);
60
+ return found;
61
+ }
62
+ /**
63
+ * The export names an import or a re-export declaration reads from the module it names,
64
+ * `undefined` standing for the whole module where a namespace or a star reads it; none for
65
+ * any other node.
66
+ */
67
+ export function importedNames(node) {
68
+ if (isImportDeclaration(node)) {
69
+ return declarationBindings(node).map((binding) => binding.imported);
70
+ }
71
+ if (!isExportDeclaration(node)) {
72
+ return [];
73
+ }
74
+ const clause = node.exportClause;
75
+ return clause === undefined || !isNamedExports(clause)
76
+ ? [undefined]
77
+ : clause.elements.map((one) => (one.propertyName ?? one.name).text);
78
+ }
79
+ /** The export names {@link importedNames} gives one node, without the whole module. */
80
+ export function namesImported(node) {
81
+ return importedNames(node).flatMap((name) => name ?? []);
82
+ }
83
+ /**
84
+ * The bindings of one file that are judged as imports: those of {@link bindingsOf} but an
85
+ * `import x = …` that is exported or names no external module. None is judged in a
86
+ * declaration file or a component file.
87
+ */
88
+ export function importBindings(file) {
89
+ if (file.isDeclarationFile || !JUDGED.test(file.fileName)) {
90
+ return [];
91
+ }
92
+ return bindingsOf(file).flatMap((binding) => binding.equals !== undefined && (binding.equals.exported || !binding.equals.external)
93
+ ? []
94
+ : [binding.name]);
95
+ }
51
96
  /** Whether one identifier names a binding in a local export, which reads it whatever it resolves to. */
52
97
  export function exportsLocally(use) {
53
98
  return isExportSpecifier(use.parent);