@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
@@ -0,0 +1,236 @@
1
+ /**
2
+ * The loads by a computed path: those whose files the analysis resolves, and those it
3
+ * cannot, whose files are roots the analysis does not see, so the configuration's roots
4
+ * are never complete.
5
+ */
6
+ import { isBinaryExpression, isCallExpression, isIdentifier, isMetaProperty, isNoSubstitutionTemplateLiteral, isPropertyAccessExpression, isStringLiteral, isTemplateExpression, SyntaxKind, } from "@typescript/native/ast";
7
+ import { amdLoadOf, amdUnresolved } from "../amd.js";
8
+ import { membersNamed, textsOf, writtenValue } from "../entries/static-module.js";
9
+ import { writtenName } from "../platform-calls.js";
10
+ import { cannotStartPackageName, dirnamePath, hasURLScheme, isAbsolutePath, relativePath, resolvePath, startsRelativePath, } from "../paths.js";
11
+ import { unwrapped } from "../unwrap.js";
12
+ import { globExpression } from "../text/glob.js";
13
+ /** The literal text a computed path starts with: a template's head, or a concatenation's first literal. */
14
+ function leadingLiteral(node) {
15
+ const at = unwrapped(node);
16
+ if (isTemplateExpression(at)) {
17
+ return at.head.text;
18
+ }
19
+ if (isBinaryExpression(at) && at.operatorToken.kind === SyntaxKind.PlusToken) {
20
+ const left = unwrapped(at.left);
21
+ return isStringLiteral(left) || isNoSubstitutionTemplateLiteral(left)
22
+ ? left.text
23
+ : leadingLiteral(at.left);
24
+ }
25
+ return undefined;
26
+ }
27
+ /**
28
+ * The directory below `dir` a computed load's leading literal names, every file of which
29
+ * the load reaches, or none where it names a package, `dir` itself or a directory holding
30
+ * it, each of which would name every sibling.
31
+ */
32
+ function computedLoadDirectory(dir, prefix) {
33
+ if (!startsRelativePath(prefix) || !prefix.endsWith("/")) {
34
+ return undefined;
35
+ }
36
+ const target = resolvePath(dir, prefix);
37
+ return target === dir || relativePath(target, dir) !== undefined ? undefined : target;
38
+ }
39
+ /** Whether one node is a call that may load a module by a computed path. */
40
+ export function isLoadCall(node) {
41
+ if (!isCallExpression(node)) {
42
+ return false;
43
+ }
44
+ return (node.expression.kind === SyntaxKind.ImportKeyword ||
45
+ writtenName(node.expression) !== undefined ||
46
+ isMetaGlob(node));
47
+ }
48
+ /**
49
+ * The files one `import.meta.glob` call loads, as Vite's `toAbsoluteGlob` reads its globs
50
+ * (https://vite.dev/guide/features.html#glob-import): `/` against the project root, `./`
51
+ * and `../` against a literal `base` (read against the root where it starts with `/`) or the
52
+ * calling file's directory, `**` as written, and `!` removing what it matches. Undefined
53
+ * where Vite reads a glob through an alias, the root is unknown, or a glob or base is computed.
54
+ */
55
+ function globbed(file, first, options, others, rootOf) {
56
+ const patterns = textsOf(writtenValue(first), true);
57
+ const bases = globOption(options, "base");
58
+ const [written, ...more] = bases ?? [];
59
+ const base = written === undefined ? undefined : textsOf(written, false);
60
+ if (patterns === undefined ||
61
+ bases === undefined ||
62
+ more.length > 0 ||
63
+ (written !== undefined && base === undefined)) {
64
+ return undefined;
65
+ }
66
+ const dir = dirnamePath(file.fileName);
67
+ const root = () => rootOf(file.fileName);
68
+ const against = (at) => {
69
+ if (!isAbsolutePath(at)) {
70
+ return resolvePath(dir, at);
71
+ }
72
+ const top = root();
73
+ return top === undefined ? undefined : resolvePath(top, `.${at}`);
74
+ };
75
+ const from = base?.[0] === undefined ? dir : against(base[0]);
76
+ const keep = [];
77
+ const drop = [];
78
+ for (const written of patterns) {
79
+ const negated = written.startsWith("!");
80
+ const pattern = negated ? written.slice(1) : written;
81
+ let path;
82
+ if (isAbsolutePath(pattern)) {
83
+ path = against(pattern);
84
+ }
85
+ else if (startsRelativePath(pattern)) {
86
+ path = from === undefined ? undefined : resolvePath(from, pattern);
87
+ }
88
+ else if (pattern.startsWith("**")) {
89
+ path = pattern;
90
+ }
91
+ if (path === undefined) {
92
+ return undefined;
93
+ }
94
+ (negated ? drop : keep).push(globExpression(path));
95
+ }
96
+ return others.filter((candidate) => keep.some((glob) => glob.test(candidate.file.fileName)) &&
97
+ !drop.some((glob) => glob.test(candidate.file.fileName)));
98
+ }
99
+ /**
100
+ * The computed loads among one file's load calls, each loading the `candidates` but the
101
+ * file itself: an `import()` or `require()` whose path starts with a relative literal naming
102
+ * a directory below the file's loads every candidate below it, as a bundler's context module
103
+ * reads it, and an `import.meta.glob` every candidate its globs match ({@link globbed}). A
104
+ * call of a computed path that names no such directory and no package is an unresolved load,
105
+ * and so is an AMD call one of whose dependencies names no path ({@link amdUnresolved}).
106
+ */
107
+ export function computedLoadsOf(file, calls, candidates, rootOf) {
108
+ const dir = dirnamePath(file.fileName);
109
+ const others = candidates.filter((candidate) => candidate.file !== file);
110
+ return calls.flatMap(({ call, from }) => {
111
+ const [first, options] = call.arguments;
112
+ if (first === undefined) {
113
+ return [];
114
+ }
115
+ if (isMetaGlob(call)) {
116
+ const files = globbed(file, first, options, others, rootOf);
117
+ const asset = readsAsAsset(options);
118
+ return [
119
+ {
120
+ call,
121
+ from,
122
+ require: undefined,
123
+ amd: false,
124
+ files: files ?? [],
125
+ resolved: files !== undefined,
126
+ asset,
127
+ eager: isEager(options),
128
+ },
129
+ ];
130
+ }
131
+ const amd = amdLoadOf(call);
132
+ if (amd !== undefined) {
133
+ return amdUnresolved(amd)
134
+ ? [
135
+ {
136
+ call,
137
+ from,
138
+ require: amd.callee,
139
+ amd: true,
140
+ files: [],
141
+ resolved: false,
142
+ asset: false,
143
+ eager: false,
144
+ },
145
+ ]
146
+ : [];
147
+ }
148
+ const argument = unwrapped(first);
149
+ const require = call.expression.kind === SyntaxKind.ImportKeyword ? undefined : writtenName(call.expression);
150
+ if (!mayNamePath(argument)) {
151
+ return [];
152
+ }
153
+ const prefix = leadingLiteral(argument);
154
+ if (namesPackage(prefix)) {
155
+ return [];
156
+ }
157
+ const target = prefix === undefined ? undefined : computedLoadDirectory(dir, prefix);
158
+ const files = target === undefined
159
+ ? []
160
+ : others.filter((candidate) => relativePath(target, candidate.file.fileName) !== undefined);
161
+ return [
162
+ {
163
+ call,
164
+ from,
165
+ require,
166
+ amd: false,
167
+ files,
168
+ resolved: target !== undefined,
169
+ asset: false,
170
+ eager: false,
171
+ },
172
+ ];
173
+ });
174
+ }
175
+ /**
176
+ * Whether a computed argument may be a module path: anything but a string, which names its
177
+ * module, and a value no path is, a number, a boolean, `null`, an object, an array, a
178
+ * function or a class.
179
+ */
180
+ function mayNamePath(argument) {
181
+ return !NO_PATH.has(argument.kind);
182
+ }
183
+ const NO_PATH = new Set([
184
+ SyntaxKind.StringLiteral,
185
+ SyntaxKind.NoSubstitutionTemplateLiteral,
186
+ SyntaxKind.NumericLiteral,
187
+ SyntaxKind.BigIntLiteral,
188
+ SyntaxKind.TrueKeyword,
189
+ SyntaxKind.FalseKeyword,
190
+ SyntaxKind.NullKeyword,
191
+ SyntaxKind.ObjectLiteralExpression,
192
+ SyntaxKind.ArrayLiteralExpression,
193
+ SyntaxKind.ArrowFunction,
194
+ SyntaxKind.FunctionExpression,
195
+ SyntaxKind.ClassExpression,
196
+ SyntaxKind.RegularExpressionLiteral,
197
+ ]);
198
+ /** Whether one call is `import.meta.glob(...)`. */
199
+ function isMetaGlob(node) {
200
+ if (!isPropertyAccessExpression(node.expression)) {
201
+ return false;
202
+ }
203
+ const { expression, name } = node.expression;
204
+ return (isIdentifier(name) &&
205
+ name.text === "glob" &&
206
+ isMetaProperty(expression) &&
207
+ expression.keywordToken === SyntaxKind.ImportKeyword);
208
+ }
209
+ /** The values the options of an `import.meta.glob` write for one key, undefined where a spread may hide one. */
210
+ function globOption(options, key) {
211
+ return options === undefined ? [] : membersNamed(writtenValue(options), key);
212
+ }
213
+ /** Whether the options of an `import.meta.glob` set `eager` to `true`. */
214
+ function isEager(options) {
215
+ return (globOption(options, "eager")?.some((value) => value.kind === "boolean" && value.value) === true);
216
+ }
217
+ /** The query parameters that make Vite load a file as its text or its URL, each written bare. */
218
+ const ASSET_QUERY = /(?:^\?|&)(?:raw|url|inline)(?:&|$)/u;
219
+ /**
220
+ * Whether the options of an `import.meta.glob` read each file as text or as a URL: a
221
+ * `query` string holding a bare `raw`, `url` or `inline`, or an `as` of `raw` or `url`,
222
+ * which Vite reads as that query (https://vite.dev/guide/features.html#glob-import). Any
223
+ * other query is read as loading the module, which keeps more than it may.
224
+ */
225
+ function readsAsAsset(options) {
226
+ const texts = (key) => (globOption(options, key) ?? []).flatMap((value) => value.kind === "text" ? [value.text] : []);
227
+ return (texts("as").some((text) => text === "raw" || text === "url") ||
228
+ texts("query").some((text) => ASSET_QUERY.test(text.startsWith("?") ? text : `?${text}`)));
229
+ }
230
+ /** Whether a computed path's leading literal names a package, whose files are none of the target's. */
231
+ function namesPackage(prefix) {
232
+ return (prefix !== undefined &&
233
+ prefix !== "" &&
234
+ !cannotStartPackageName(prefix) &&
235
+ !hasURLScheme(prefix));
236
+ }
@@ -12,7 +12,7 @@ import { SignatureKind, SymbolFlags, TypeFlags, } from "@typescript/native/sync"
12
12
  import { aliasChains } from "./alias-chain.js";
13
13
  import { calleeName, resolvedTargets, valuesPassed } from "./calls.js";
14
14
  import { classMembers, memberComponent, typesAt } from "./class-members.js";
15
- import { names, namesADeclaration, resolveEntries, spelledEntry, } from "./configured-declarations.js";
15
+ import { names, namesADeclaration, resolveEntries, spelledEntry, } from "./roots/configured-declarations.js";
16
16
  import { nodeKey } from "./inventory.js";
17
17
  import { renderPosition } from "./position.js";
18
18
  import { must, Unanswerable } from "./query.js";
@@ -898,7 +898,8 @@ export function serializationContract(input) {
898
898
  }
899
899
  /**
900
900
  * The data members the decodes and copies of `files` fill, as {@link Decoded} names them.
901
- * A generic declared outside the analysis, or only ambiently, fills the type that a type
901
+ * A generic declared outside the analysis, or only ambiently, or one that reads a value
902
+ * {@link readTypes} finds untyped as its own type parameter, fills the type that a type
902
903
  * parameter of its own no parameter names binds, where the awaited result carries that
903
904
  * type through its data members rather than only naming it, as a store's methods do: no
904
905
  * argument hands the value over, so it fills it where the analysis cannot see, and may
@@ -951,6 +952,10 @@ function decodedOf(project, held, files, consumers) {
951
952
  for (const file of files) {
952
953
  file.forEachChild(visit);
953
954
  }
955
+ const read = readTypes(project, untyped);
956
+ // A generic of the analysis that reads an untyped value as its own type parameter is a
957
+ // client of what made the value, and fills what that parameter binds as an outside one does.
958
+ const clients = new Set(read.made.flatMap((type) => (type.isTypeParameter() ? [type.id] : [])));
954
959
  const filled = [];
955
960
  const resolved = queries.resolvedSignatures(calls).map(must);
956
961
  for (const signature of resolved) {
@@ -963,7 +968,8 @@ function decodedOf(project, held, files, consumers) {
963
968
  const node = declaration === undefined ? undefined : project.declarationAt(declaration)?.node;
964
969
  if (declaration === undefined ||
965
970
  (inAnalysis(declaration.path) &&
966
- (node === undefined || (node.flags & NodeFlags.Ambient) === 0))) {
971
+ (node === undefined || (node.flags & NodeFlags.Ambient) === 0) &&
972
+ !generic.some((id) => clients.has(id)))) {
967
973
  continue;
968
974
  }
969
975
  const result = must(queries.returnTypeOf(declared));
@@ -982,7 +988,6 @@ function decodedOf(project, held, files, consumers) {
982
988
  filled.push({ carried: awaited(project, given), types });
983
989
  }
984
990
  }
985
- const read = readTypes(project, untyped);
986
991
  const copies = copiedNames(project, spreads, calls);
987
992
  if (filled.length === 0 &&
988
993
  read.parsed.length === 0 &&
@@ -5,12 +5,11 @@
5
5
  */
6
6
  import { commonDirectory, emittedFrom } from "./load/emit-map.js";
7
7
  import { dirnamePath, joinPath, relativePath, resolvePath } from "./paths.js";
8
+ import { OUTPUT_EXTENSIONS } from "./extensions.js";
8
9
  /** The one character a subpath pattern of a manifest's `exports` holds. */
9
10
  const SUBPATH_WILDCARD = "*";
10
11
  /** The extension a source file's name drops when the project writes it out. */
11
12
  const SOURCE_EXTENSION = /\.[cm]?[jt]sx?$/u;
12
- /** The extensions the project may write a source file's output under. */
13
- const OUTPUT_EXTENSIONS = [".js", ".mjs", ".cjs", ".jsx", ".d.ts", ".d.mts", ".d.cts"];
14
13
  /**
15
14
  * Whether one path is what a subpath pattern stands for: the text before the
16
15
  * wildcard, any run of characters including the solidus, then the text after it.
@@ -43,13 +42,10 @@ function layoutOf(project, fileNames) {
43
42
  };
44
43
  }
45
44
  /**
46
- * The lookups over one project's own files.
47
- *
48
- * A path that names an emitted file is read back to the source the project would
49
- * emit it from through the project's own emit mapping and nothing else: the
50
- * extension is one the compiler emits from a source extension, and a path below the
51
- * output directory is read below the directory the compiler emits from, which the
52
- * configuration declares or which is the directory every input file shares.
45
+ * The lookups over one project's own files. A path naming an emitted file is read back to its
46
+ * source through the project's own emit mapping alone: the extension is one the compiler emits
47
+ * from a source extension, and a path below the output directory is read below the directory
48
+ * the compiler emits from, which the configuration declares or every input file shares.
53
49
  */
54
50
  export function sourceFilesOf(project, targetRoot) {
55
51
  const byName = new Map();
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { isGetAccessorDeclaration, isSetAccessorDeclaration, } from "@typescript/native/ast";
7
7
  import { nodeKey } from "./inventory.js";
8
+ import { classedAsTest, madeByTestCode, withheldBy } from "./tests/file-class.js";
8
9
  /** The class members one project's own files declare by a getter or a setter. */
9
10
  export function accessorsOf(project, held) {
10
11
  const found = [];
@@ -25,9 +26,10 @@ export function accessorsOf(project, held) {
25
26
  /** The run's stores, from what each configuration contributed, the records and the mode. */
26
27
  export function storesOf(per, exempt, mode) {
27
28
  return {
28
- references: per.flatMap((one) => one.references.filter((reference) => !mode.production || !reference.test)),
29
+ references: per.flatMap((one) => one.references.filter((reference) => !withheldBy(mode, reference, one.support))),
29
30
  readInTests: new Set(per.flatMap((one) => one.references
30
- .filter((reference) => reference.test && reference.use !== "write")
31
+ .filter((reference) => reference.use !== "write" &&
32
+ classedAsTest(madeByTestCode(reference, one.support), reference.consumer !== undefined, mode))
31
33
  .map((reference) => reference.to))),
32
34
  accessors: new Set(per.flatMap((one) => one.accessors)),
33
35
  exempt: new Set(exempt.map((record) => record.id)),
@@ -4,9 +4,9 @@
4
4
  * ignore entry or a baseline row. A bound record marks its declaration live before the
5
5
  * sweep, unless its code reports a part or a dependency row, and is in effect when it
6
6
  * withholds a finding its code would have produced there, dormant when the configuration
7
- * withholds every such finding anyway, and stale otherwise.
7
+ * or a hold withholds every such finding anyway, and stale otherwise.
8
8
  */
9
- import { KIND_ROWS, SYMBOL_SUBJECTS, } from "./contract/contract.generated.js";
9
+ import { KIND_ROWS, REMEDIES, SYMBOL_SUBJECTS, } from "./contract/contract.generated.js";
10
10
  import { UsageError } from "./usage.js";
11
11
  import { positionKey } from "./position.js";
12
12
  import { isRef } from "./ref.js";
@@ -66,6 +66,8 @@ export function isSymbolRef(value) {
66
66
  export function blankReason(reason) {
67
67
  return reason === undefined || /^[ \t\r\n]*$/u.test(reason);
68
68
  }
69
+ /** No hold. */
70
+ const NO_HOLDS = { withholds: () => false, holdsRecord: () => false };
69
71
  /** The subject of the kind each live code names. */
70
72
  const SUBJECT_OF_CODE = new Map(KIND_ROWS.map((row) => [row.code, row.subject]));
71
73
  /** The subject a finding about one symbol kind is about. */
@@ -78,32 +80,25 @@ function subjectOf(kind) {
78
80
  function rowOf(subject) {
79
81
  return subject === "file" || subject === "dependency" || subject === "record";
80
82
  }
81
- /**
82
- * Whether a finding's subject is a row of a document rather than a declaration. Apart
83
- * from a dependency row, nothing withholds a finding about one.
84
- */
83
+ /** Whether a finding's subject is a row of a document rather than a declaration. */
85
84
  export function isRowSubject(kind) {
86
85
  return rowOf(subjectOf(kind));
87
86
  }
88
- /** Whether a code reports a row of a document. */
89
- export function isRowCode(code) {
90
- return rowOf(SUBJECT_OF_CODE.get(code));
87
+ /** The subject a code reports, which kinds.json's remedies table answers for. */
88
+ export function subjectOfCode(code) {
89
+ return SUBJECT_OF_CODE.get(code);
91
90
  }
92
91
  /**
93
- * Whether a finding's subject is a dependency row. An ignore entry or a baseline row
94
- * naming the row's reference and file withholds its finding and marks nothing, and an
95
- * inline directive never binds one.
92
+ * Whether a record of `mechanism` can name a finding about `subject`, as kinds.json's
93
+ * remedies table lists the suppressions of each subject. A subject the table does not
94
+ * know is a declaration's.
96
95
  */
97
- export function isDependencySubject(kind) {
98
- return subjectOf(kind) === "dependency";
99
- }
100
- /** Whether a code reports a dependency row, which only an ignore entry or a baseline row names. */
101
- export function isDependencyCode(code) {
102
- return SUBJECT_OF_CODE.get(code) === "dependency";
96
+ export function names(subject, mechanism) {
97
+ return REMEDIES[subject ?? "declaration"].suppressions.includes(mechanism);
103
98
  }
104
- /** Whether a code reports a row of a document that no record of any mechanism can name. */
105
- export function isUnsuppressibleCode(code) {
106
- return isRowCode(code) && !isDependencyCode(code);
99
+ /** Whether a finding about a symbol of `kind` is one a record of `mechanism` can name. */
100
+ export function namesKind(kind, mechanism) {
101
+ return names(subjectOf(kind), mechanism);
107
102
  }
108
103
  /**
109
104
  * Whether a finding's subject is a part of the declaration its reference names: a part
@@ -136,14 +131,13 @@ export function pairs(record, finding) {
136
131
  if (record.code !== finding.code) {
137
132
  return false;
138
133
  }
139
- if (isDependencySubject(finding.symbol.kind)) {
140
- return (record.mechanism !== "inline" &&
141
- record.symbol === finding.symbol.ref &&
142
- record.path === finding.position.path);
143
- }
144
- if (isRowSubject(finding.symbol.kind)) {
134
+ const subject = subjectOf(finding.symbol.kind);
135
+ if (!names(subject, record.mechanism)) {
145
136
  return false;
146
137
  }
138
+ if (subject === "dependency") {
139
+ return record.symbol === finding.symbol.ref && record.path === finding.position.path;
140
+ }
147
141
  if (record.bound !== "" && record.bound === positionKey(finding.position)) {
148
142
  return true;
149
143
  }
@@ -155,10 +149,10 @@ export function pairs(record, finding) {
155
149
  * there: swept with no mark for a liveness kind, and as swept otherwise. The first record
156
150
  * in reading order claims a finding, so a second naming it is stale; a part-kind record
157
151
  * claims every finding of its code about a part of its declaration. A record is dormant
158
- * when the dials withhold every finding it claims, read from those findings alone, or when
159
- * it claims none and its code's severity is `allow`.
152
+ * when the dials or `holds` withhold every finding it claims, or when it claims none and
153
+ * its code is at `allow` or a hold keeps its declaration live.
160
154
  */
161
- export function ledgerOf(records, would, dials) {
155
+ export function ledgerOf(records, would, dials, holds = NO_HOLDS) {
162
156
  const taken = new Set();
163
157
  const verdicts = [];
164
158
  for (const record of records) {
@@ -166,13 +160,17 @@ export function ledgerOf(records, would, dials) {
166
160
  const first = would.find(open);
167
161
  const claimed = first === undefined ? [] : isPartKind(record.code) ? would.filter(open) : [first];
168
162
  if (claimed.length === 0) {
169
- verdicts.push(record.bound !== "" && dials.allows(record.code) ? "dormant" : "stale");
163
+ verdicts.push(record.bound !== "" && (dials.allows(record.code) || holds.holdsRecord(record))
164
+ ? "dormant"
165
+ : "stale");
170
166
  continue;
171
167
  }
172
168
  for (const finding of claimed) {
173
169
  taken.add(claimKey(finding));
174
170
  }
175
- verdicts.push(claimed.every((finding) => dials.withholds(finding)) ? "dormant" : "in-effect");
171
+ verdicts.push(claimed.every((finding) => dials.withholds(finding) || holds.withholds(finding))
172
+ ? "dormant"
173
+ : "in-effect");
176
174
  }
177
175
  return {
178
176
  verdicts,
package/dist/src/sweep.js CHANGED
@@ -6,6 +6,22 @@
6
6
  * declaration only unreachable ones reference is live under the first, dead under the second.
7
7
  */
8
8
  import { namesACaller, OUTSIDE } from "./graph.js";
9
+ import { edgeWithheldBy } from "./tests/file-class.js";
10
+ /** The references to one declaration the mode counts: under `production` none test code made but those it classes as production. */
11
+ function countedRefs(made, mode) {
12
+ if (made === undefined) {
13
+ return 0;
14
+ }
15
+ if (!mode.production) {
16
+ return made.production + made.test;
17
+ }
18
+ return made.production + (mode.consumerTestsProduction === true ? made.consumerTest : 0);
19
+ }
20
+ /** The references to one declaration, split as the mode classes them, whatever it counts. */
21
+ export function splitRefs(made, mode) {
22
+ const moved = mode.consumerTestsProduction === true ? (made?.consumerTest ?? 0) : 0;
23
+ return { production: (made?.production ?? 0) + moved, test: (made?.test ?? 0) - moved };
24
+ }
9
25
  /**
10
26
  * Answers which declarations of one graph are dead under the input's mode.
11
27
  *
@@ -30,13 +46,7 @@ export function sweep(graph, input) {
30
46
  called[root.at] = true;
31
47
  }
32
48
  }
33
- const counted = (at) => {
34
- const made = graph.made[at];
35
- if (made === undefined) {
36
- return 0;
37
- }
38
- return input.mode.production ? made.production : made.production + made.test;
39
- };
49
+ const counted = (at) => countedRefs(graph.made[at], input.mode);
40
50
  const reached = reachable(graph, held, input.mode);
41
51
  const liveUnder = graph.symbols.map((_symbol, at) => {
42
52
  const relations = [];
@@ -60,10 +70,11 @@ export function sweep(graph, input) {
60
70
  if (dead[at] !== true) {
61
71
  return;
62
72
  }
73
+ const split = splitRefs(graph.made[at], input.mode);
63
74
  candidates.push({
64
75
  id: symbol.id,
65
- productionRefs: graph.made[at]?.production ?? 0,
66
- testRefs: graph.made[at]?.test ?? 0,
76
+ productionRefs: split.production,
77
+ testRefs: split.test,
67
78
  relation: counted(at) === 0 ? "reference-counting" : "reachability",
68
79
  testOfDeadCode: testOfDeadCode[at] === true,
69
80
  });
@@ -100,11 +111,12 @@ export function reachedWithTheTests(graph, held, namespaces = true) {
100
111
  return reachable({ ...graph, rooted: [...graph.rooted, ...tests] }, flagged(graph, held), { production: false }, namespaces);
101
112
  }
102
113
  /**
103
- * The references a loaded consumer makes that the mode counts. A production mode counts
104
- * none a consumer's test file made, as it counts none of the target's.
114
+ * The references a loaded consumer or an unheld file makes that the mode counts. A
115
+ * production mode counts no test reference, but a loaded consumer's where the mode classes
116
+ * a consumer's tests as production.
105
117
  */
106
118
  function callsOf(graph, mode) {
107
- return graph.consumed.filter((edge) => !mode.production || !edge.test);
119
+ return graph.consumed.filter((edge) => !edgeWithheldBy(mode, edge));
108
120
  }
109
121
  /** Per declaration of the graph, whether one of the identifiers names it. */
110
122
  function flagged(graph, ids) {
@@ -149,7 +161,7 @@ function reachable(graph, held, mode, namespaces = true) {
149
161
  const walk = (holding) => {
150
162
  for (let at = queue.pop(); at !== undefined; at = queue.pop()) {
151
163
  for (const edge of graph.out[at] ?? []) {
152
- if (!holding && mode.production && edge.test) {
164
+ if (!holding && edgeWithheldBy(mode, edge)) {
153
165
  continue;
154
166
  }
155
167
  enter(edge.to, namespaces && !edge.evaluation);