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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/dist/src/alias-chain.js +2 -2
  2. package/dist/src/analysis.js +43 -38
  3. package/dist/src/commands/read.js +6 -7
  4. package/dist/src/conformance-record.js +1 -1
  5. package/dist/src/consumers.js +4 -2
  6. package/dist/src/contract/config.generated.js +1 -1
  7. package/dist/src/contract/contract.generated.js +120 -2
  8. package/dist/src/dependencies.js +81 -49
  9. package/dist/src/entries/build-scripts.js +222 -0
  10. package/dist/src/{configuration-files.js → entries/configuration-files.js} +20 -58
  11. package/dist/src/{convention-rows.js → entries/convention-rows.js} +120 -27
  12. package/dist/src/entries/conventions.js +739 -0
  13. package/dist/src/{html-entries.js → entries/html-entries.js} +3 -3
  14. package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +2 -2
  15. package/dist/src/entries/run-directories.js +77 -0
  16. package/dist/src/entries/sources.js +144 -0
  17. package/dist/src/entries/static-module.js +669 -0
  18. package/dist/src/entries/test-runners.js +684 -0
  19. package/dist/src/{workflow-steps.js → entries/workflow-steps.js} +18 -8
  20. package/dist/src/extensions.js +37 -0
  21. package/dist/src/facts.js +38 -0
  22. package/dist/src/file-facts.js +1 -1
  23. package/dist/src/findings/emitters.js +5 -8
  24. package/dist/src/findings/self-check.js +36 -20
  25. package/dist/src/findings-pass.js +5 -6
  26. package/dist/src/framework-lifecycle.js +1 -1
  27. package/dist/src/fs/manifest.js +15 -20
  28. package/dist/src/generated-file.js +172 -29
  29. package/dist/src/graph.js +2 -5
  30. package/dist/src/index.js +1 -1
  31. package/dist/src/injection-container.js +1 -1
  32. package/dist/src/inventory/commonjs.js +507 -0
  33. package/dist/src/inventory/enumerate.js +167 -0
  34. package/dist/src/inventory/syntax.js +236 -0
  35. package/dist/src/inventory.js +305 -705
  36. package/dist/src/javascript-file.js +26 -11
  37. package/dist/src/load/classify.js +28 -9
  38. package/dist/src/load/diagnostics.js +8 -1
  39. package/dist/src/load/discover.js +16 -8
  40. package/dist/src/load/package-scope.js +56 -0
  41. package/dist/src/load/plan.js +2 -2
  42. package/dist/src/load/synthesize.js +5 -5
  43. package/dist/src/load/workspace-manifests.js +1 -1
  44. package/dist/src/matrix.js +6 -13
  45. package/dist/src/outside-tests.js +1 -1
  46. package/dist/src/paths.js +16 -0
  47. package/dist/src/platform-calls.js +78 -0
  48. package/dist/src/platform-symbols.js +140 -0
  49. package/dist/src/position.js +41 -0
  50. package/dist/src/ref.js +11 -20
  51. package/dist/src/references.js +281 -50
  52. package/dist/src/resolve.js +1 -1
  53. package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +4 -4
  54. package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +5 -8
  55. package/dist/src/{manifest.js → roots/manifest.js} +4 -4
  56. package/dist/src/roots/published-reach.js +412 -0
  57. package/dist/src/roots/root-set.js +489 -0
  58. package/dist/src/roots/unseen-loads.js +208 -0
  59. package/dist/src/serialization-contract.js +1 -1
  60. package/dist/src/session.js +3 -0
  61. package/dist/src/source-files.js +5 -9
  62. package/dist/src/suppress-inline.js +33 -81
  63. package/dist/src/suppress.js +30 -32
  64. package/dist/src/test-support.js +4 -2
  65. package/dist/src/verbs/analyze.js +13 -1
  66. package/dist/src/verbs/describe.js +5 -3
  67. package/jsr.json +1 -1
  68. package/package.json +3 -3
  69. package/src/alias-chain.ts +2 -2
  70. package/src/analysis.ts +77 -53
  71. package/src/automatic-types.ts +1 -1
  72. package/src/commands/read.ts +6 -8
  73. package/src/conformance-record.ts +1 -1
  74. package/src/consumers.ts +5 -1
  75. package/src/contract/config.generated.ts +1 -1
  76. package/src/contract/contract.generated.ts +139 -2
  77. package/src/dependencies.ts +98 -50
  78. package/src/entries/build-scripts.ts +292 -0
  79. package/src/{configuration-files.ts → entries/configuration-files.ts} +23 -75
  80. package/src/{convention-rows.ts → entries/convention-rows.ts} +167 -28
  81. package/src/{conventions.ts → entries/conventions.ts} +347 -522
  82. package/src/{html-entries.ts → entries/html-entries.ts} +5 -5
  83. package/src/{mdx-entries.ts → entries/mdx-entries.ts} +4 -4
  84. package/src/entries/run-directories.ts +108 -0
  85. package/src/entries/sources.ts +266 -0
  86. package/src/entries/static-module.ts +857 -0
  87. package/src/entries/test-runners.ts +868 -0
  88. package/src/{workflow-steps.ts → entries/workflow-steps.ts} +29 -11
  89. package/src/exempt.ts +3 -2
  90. package/src/extensions.ts +45 -0
  91. package/src/facts.ts +44 -0
  92. package/src/file-facts.ts +2 -2
  93. package/src/findings/emitters.ts +14 -10
  94. package/src/findings/self-check.ts +41 -22
  95. package/src/findings/visibility-narrowing.ts +1 -1
  96. package/src/findings-pass.ts +5 -12
  97. package/src/framework-lifecycle.ts +1 -1
  98. package/src/fs/manifest.ts +25 -21
  99. package/src/generated-file.ts +240 -36
  100. package/src/graph.ts +3 -6
  101. package/src/index.ts +1 -1
  102. package/src/injection-container.ts +1 -1
  103. package/src/inventory/commonjs.ts +696 -0
  104. package/src/inventory/enumerate.ts +203 -0
  105. package/src/inventory/syntax.ts +311 -0
  106. package/src/inventory.ts +424 -878
  107. package/src/javascript-file.ts +29 -12
  108. package/src/load/classify.ts +49 -13
  109. package/src/load/diagnostics.ts +11 -1
  110. package/src/load/discover.ts +28 -12
  111. package/src/load/package-scope.ts +74 -0
  112. package/src/load/plan.ts +11 -2
  113. package/src/load/synthesize.ts +6 -6
  114. package/src/load/test-globals.ts +1 -1
  115. package/src/load/workspace-manifests.ts +1 -1
  116. package/src/load/workspace-run.ts +1 -1
  117. package/src/matrix.ts +7 -14
  118. package/src/outside-tests.ts +1 -1
  119. package/src/paths.ts +18 -0
  120. package/src/platform-calls.ts +119 -0
  121. package/src/platform-symbols.ts +205 -0
  122. package/src/position.ts +42 -0
  123. package/src/ref.ts +11 -20
  124. package/src/references.ts +383 -73
  125. package/src/report.ts +1 -1
  126. package/src/resolve.ts +1 -1
  127. package/src/{configured-declarations.ts → roots/configured-declarations.ts} +7 -7
  128. package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +5 -8
  129. package/src/{manifest.ts → roots/manifest.ts} +5 -5
  130. package/src/roots/published-reach.ts +481 -0
  131. package/src/{roots.ts → roots/root-set.ts} +203 -630
  132. package/src/roots/unseen-loads.ts +307 -0
  133. package/src/serialization-contract.ts +1 -1
  134. package/src/session.ts +17 -1
  135. package/src/source-files.ts +5 -10
  136. package/src/suppress-inline.ts +53 -89
  137. package/src/suppress.ts +48 -33
  138. package/src/test-support.ts +8 -3
  139. package/src/verbs/analyze.ts +17 -4
  140. package/src/verbs/describe.ts +5 -2
  141. package/src/verbs/explain.ts +1 -1
  142. package/dist/src/build-scripts.js +0 -418
  143. package/dist/src/conventions.js +0 -907
  144. package/dist/src/entry-points.js +0 -513
  145. package/dist/src/roots.js +0 -865
  146. package/dist/src/unseen-loads.js +0 -100
  147. package/src/build-scripts.ts +0 -536
  148. package/src/entry-points.ts +0 -661
  149. package/src/unseen-loads.ts +0 -134
@@ -25,6 +25,47 @@ export function isComponentFile(file) {
25
25
  export function writtenLength(file) {
26
26
  return isComponentFile(file) ? file.originalText.length : file.text.length;
27
27
  }
28
+ /** Whether one character ends a line, under the language's line terminators. */
29
+ function endsLine(char) {
30
+ return char === "\n" || char === "\r" || char === "\u2028" || char === "\u2029";
31
+ }
32
+ /** The offset where the line holding `from` ends. */
33
+ export function lineEnd(text, from) {
34
+ let at = from;
35
+ while (at < text.length && !endsLine(text[at])) {
36
+ at += 1;
37
+ }
38
+ return at;
39
+ }
40
+ /**
41
+ * The offset of the first token at or after `from`: whitespace, comments and a leading
42
+ * hashbang are trivia.
43
+ */
44
+ export function tokenStart(text, from) {
45
+ let at = from;
46
+ if (at === 0 && text.startsWith("#!")) {
47
+ at = lineEnd(text, 0);
48
+ }
49
+ for (;;) {
50
+ if (/\s/u.test(text[at] ?? "")) {
51
+ at += 1;
52
+ }
53
+ else if (text.startsWith("//", at)) {
54
+ at = lineEnd(text, at);
55
+ }
56
+ else if (text.startsWith("/*", at)) {
57
+ const close = text.indexOf("*/", at + 2);
58
+ at = close < 0 ? text.length : close + 2;
59
+ }
60
+ else {
61
+ return at;
62
+ }
63
+ }
64
+ }
65
+ /** The line, counted from one, of the first token at or after one offset of one file. */
66
+ export function startLine(file, offset) {
67
+ return file.getLineAndCharacterOfPosition(tokenStart(file.text, offset)).line + 1;
68
+ }
28
69
  /**
29
70
  * The position of one offset of one file, rendered against the target root.
30
71
  *
package/dist/src/ref.js CHANGED
@@ -1,13 +1,9 @@
1
1
  /**
2
- * The stable symbol reference: the identifier that names one declaration across
3
- * runs, across reports, in an ignore entry, in a configured root and in a
4
- * cross-language edge. It is built from names and containers only, so an edit above
5
- * a declaration leaves it unchanged, and it is compared as bytes, so there is one
6
- * canonical spelling per declaration and this module is where that spelling is
7
- * written.
8
- *
9
- * The position key of {@link ./position.ts} is the other identifier and has the
10
- * other job: it locates a finding inside one run, and the two never mix.
2
+ * The stable symbol reference: the identifier that names one declaration across runs,
3
+ * reports, ignore entries, configured roots and cross-language edges. It is built from
4
+ * names and containers only, so an edit above a declaration leaves it unchanged, and it
5
+ * is compared as bytes, so this module writes the one canonical spelling. The position
6
+ * key of {@link ./position.ts} locates a finding inside one run; the two never mix.
11
7
  */
12
8
  /** The language prefix, which namespaces every reference this analyzer emits. */
13
9
  const PREFIX = "ts://";
@@ -30,16 +26,10 @@ const TS_FILE = String.raw `${TS_DIRS}[^/#\r\n]+(?:(?:\.d)?\.[mc]?ts|\.tsx|\.[mc
30
26
  const SCOPE = `${PREFIX}${TS_PACKAGE}/${TS_FILE}`;
31
27
  const MANIFEST_SCOPE = `${PREFIX}${TS_PACKAGE}/${TS_DIRS}package\\.json`;
32
28
  /**
33
- * One expression per form the grammar defines, each anchored and each the whole of
34
- * a reference. Several forms share one expression because they differ in what the
35
- * symbol is rather than in how the reference is spelled, so the list is the set of
36
- * spellings and not the set of subjects. A reference is valid when it matches one.
37
- *
38
- * They are written here rather than read from the Contract at run time: the Contract
39
- * is data a reader outside this program consults, and a product that read it while
40
- * running would need it installed beside itself. A test compares this list against
41
- * the release this analyzer is written against, so a grammar that grows a form
42
- * arrives here as a failing test.
29
+ * One anchored expression per spelling the grammar defines; a reference is valid when it
30
+ * matches one. They are written here rather than read from the Contract at run time, so
31
+ * the product needs no Contract installed beside it; a test compares this list against
32
+ * the release this analyzer implements, so a grammar that grows a form fails it.
43
33
  */
44
34
  export const REF_EXPRESSIONS = [
45
35
  `^${SCOPE}#$`,
@@ -102,7 +92,8 @@ export function aliasFragment(name, block) {
102
92
  const head = block === undefined ? "" : `${quoted(block)}.`;
103
93
  return `${head}${nameComponent(name)}:alias`;
104
94
  }
105
- function spell(component) {
95
+ /** The canonical spelling of one component: a name, or a computed key. */
96
+ export function spell(component) {
106
97
  return component.computed ? computedComponent(component.text) : nameComponent(component.text);
107
98
  }
108
99
  /**
@@ -1,11 +1,11 @@
1
- import { isArrayLiteralExpression, isAsExpression, isBinaryExpression, isCallExpression, isDecorator, isDeleteExpression, isElementAccessExpression, isExportDeclaration, isExpressionStatement, isExternalModuleReference, isForInStatement, isForOfStatement, isForStatement, isIdentifier, isImportDeclaration, isImportEqualsDeclaration, isInterfaceDeclaration, isMetaProperty, isModuleBlock, isModuleDeclaration, isNewExpression, isNamespaceExport, isNoSubstitutionTemplateLiteral, isObjectLiteralExpression, isParenthesizedExpression, isPostfixUnaryExpression, isPrefixUnaryExpression, isPropertyAccessExpression, isPropertyAssignment, isShorthandPropertyAssignment, isSpreadAssignment, isSpreadElement, isStringLiteral, isVoidExpression, SyntaxKind, } from "@typescript/native/ast";
1
+ import { isArrayLiteralExpression, isAsExpression, isBinaryExpression, isCallExpression, isDecorator, isDeleteExpression, isElementAccessExpression, isExportDeclaration, isExpressionStatement, isExternalModuleReference, isForInStatement, isForOfStatement, isForStatement, isIdentifier, isImportDeclaration, isImportEqualsDeclaration, isInterfaceDeclaration, isMetaProperty, isModuleBlock, isModuleDeclaration, isNewExpression, isNamespaceExport, isNoSubstitutionTemplateLiteral, isObjectLiteralExpression, isParenthesizedExpression, isPostfixUnaryExpression, isPrefixUnaryExpression, isPropertyAccessExpression, isPropertyAssignment, isShorthandPropertyAssignment, isSpreadAssignment, isSpreadElement, isStringLiteral, isStringLiteralLikeNode, isVoidExpression, SyntaxKind, } from "@typescript/native/ast";
2
2
  import { parenthesesRemoved } from "./unwrap.js";
3
3
  import { aliasChains } from "./alias-chain.js";
4
4
  import { contextualUses } from "./contextual-uses.js";
5
5
  import { destructuring, propertyReadsOf } from "./destructuring.js";
6
- import { specifierCandidates } from "./configuration-files.js";
6
+ import { specifierCandidates } from "./entries/configuration-files.js";
7
7
  import { namespaceUses } from "./namespace-objects.js";
8
- import { declarationsByName, isParameterProperty, nodeKey, } from "./inventory.js";
8
+ import { isParameterProperty, nodeKey } from "./inventory.js";
9
9
  import { ambientGlobalInterfaces, augmentationReferences } from "./augmentations.js";
10
10
  import { elementAccessReferences } from "./element-access.js";
11
11
  import { indexedAccessReferences } from "./indexed-access.js";
@@ -13,11 +13,15 @@ import { keyofComparisonReferences } from "./keyof-comparisons.js";
13
13
  import { unimplementedCalleeReads } from "./unimplemented-callees.js";
14
14
  import { moduleDeclarationReferences } from "./module-declarations.js";
15
15
  import { overrideReferences } from "./overrides.js";
16
- import { dirnamePath, queriedModulePath, relativePath, resolvePath } from "./paths.js";
16
+ import { dirnamePath, queriedModulePath, relativePath, relativeURLPath, resolvePath, } from "./paths.js";
17
17
  import { byPosition, isComponentFile, renderPosition } from "./position.js";
18
18
  import { DEFAULT_BATCH_CAP, propertyTables, UNANSWERED } from "./query.js";
19
+ import { sourceFilesOf } from "./source-files.js";
19
20
  import { typeParameterReads } from "./type-parameter-reads.js";
20
21
  import { typeQueryAliases } from "./type-query-alias.js";
22
+ import { callsPlatform, isPlatformGlobal, startsThread } from "./platform-symbols.js";
23
+ import { writtenName } from "./platform-calls.js";
24
+ import { computedLoadsOf, isLoadCall } from "./roots/unseen-loads.js";
21
25
  import { computedKeyNames, iterationReferences, keyNameOf, keyedUses, } from "./symbol-keys.js";
22
26
  import { handedOn, valuesOf } from "./value-flow.js";
23
27
  import { bytewise } from "./text/compare.js";
@@ -183,25 +187,23 @@ function isImportMetaURL(node) {
183
187
  }
184
188
  /** The module URL one node builds, where it builds one. */
185
189
  function moduleURLOf(node) {
186
- const relative = (argument) => {
190
+ const literal = (argument) => {
187
191
  const at = argument === undefined ? undefined : parenthesesRemoved(argument);
188
- return at !== undefined &&
189
- (isStringLiteral(at) || isNoSubstitutionTemplateLiteral(at)) &&
190
- /^\.\.?\//u.test(at.text)
191
- ? at.text
192
- : undefined;
192
+ return isLiteralText(at) ? at.text : undefined;
193
193
  };
194
- if (isNewExpression(node) && isIdentifier(node.expression) && node.expression.text === "URL") {
194
+ const constructor = isNewExpression(node) ? writtenName(node.expression) : undefined;
195
+ if (isNewExpression(node) && constructor !== undefined) {
195
196
  const [reference, base] = node.arguments ?? [];
196
- const specifier = relative(reference);
197
+ const specifier = literal(reference);
197
198
  return specifier !== undefined && base !== undefined && isImportMetaURL(base)
198
- ? { specifier, constructor: node.expression, at: node }
199
+ ? { specifier, constructor, at: node }
199
200
  : undefined;
200
201
  }
201
202
  if (isCallExpression(node)) {
202
203
  const callee = parenthesesRemoved(node.expression);
203
- const specifier = relative(node.arguments[0]);
204
+ const specifier = literal(node.arguments[0]);
204
205
  if (specifier !== undefined &&
206
+ /^\.\.?\//u.test(specifier) &&
205
207
  isPropertyAccessExpression(callee) &&
206
208
  isMetaProperty(callee.expression) &&
207
209
  callee.expression.keywordToken === SyntaxKind.ImportKeyword &&
@@ -211,6 +213,69 @@ function moduleURLOf(node) {
211
213
  }
212
214
  return undefined;
213
215
  }
216
+ /** The constructors whose first argument addresses a worker. */
217
+ const WORKER_CONSTRUCTORS = new Set(["Worker", "SharedWorker"]);
218
+ /** Whether an options argument is an object literal whose `eval` member is written as anything but `false`. */
219
+ function evaluatesCode(options) {
220
+ const at = options === undefined ? undefined : parenthesesRemoved(options);
221
+ return (at !== undefined &&
222
+ isObjectLiteralExpression(at) &&
223
+ at.properties.some((property) => (isShorthandPropertyAssignment(property) &&
224
+ isIdentifier(property.name) &&
225
+ property.name.text === "eval") ||
226
+ (isPropertyAssignment(property) &&
227
+ (isIdentifier(property.name) || isStringLiteral(property.name)) &&
228
+ property.name.text === "eval" &&
229
+ parenthesesRemoved(property.initializer).kind !== SyntaxKind.FalseKeyword)));
230
+ }
231
+ /** The worker one node starts by a literal address, where it starts one. */
232
+ function workerOf(node) {
233
+ if (isNewExpression(node)) {
234
+ const constructor = writtenName(node.expression);
235
+ const [address, options] = node.arguments ?? [];
236
+ const literal = address === undefined ? undefined : parenthesesRemoved(address);
237
+ // A thread's constructor may be imported under another name, whose path is relative.
238
+ if (constructor === undefined ||
239
+ !isLiteralText(literal) ||
240
+ (!WORKER_CONSTRUCTORS.has(constructor.text) && !/^\.\.?\//u.test(literal.text))) {
241
+ return undefined;
242
+ }
243
+ const callee = parenthesesRemoved(node.expression);
244
+ const holder = isPropertyAccessExpression(callee)
245
+ ? parenthesesRemoved(callee.expression)
246
+ : undefined;
247
+ return {
248
+ address: literal,
249
+ constructor,
250
+ holder: holder !== undefined && isIdentifier(holder) ? holder : undefined,
251
+ evaluated: evaluatesCode(options),
252
+ at: node,
253
+ };
254
+ }
255
+ if (!isCallExpression(node)) {
256
+ return undefined;
257
+ }
258
+ const callee = parenthesesRemoved(node.expression);
259
+ const registry = isPropertyAccessExpression(callee) && callee.name.text === "register"
260
+ ? parenthesesRemoved(callee.expression)
261
+ : undefined;
262
+ const navigator = registry !== undefined &&
263
+ isPropertyAccessExpression(registry) &&
264
+ registry.name.text === "serviceWorker"
265
+ ? writtenName(registry.expression)
266
+ : undefined;
267
+ const [address] = node.arguments;
268
+ const literal = address === undefined ? undefined : parenthesesRemoved(address);
269
+ return navigator?.text === "navigator" && isLiteralText(literal)
270
+ ? { address: literal, constructor: navigator, holder: undefined, evaluated: false, at: node }
271
+ : undefined;
272
+ }
273
+ /** The file of the target a relative specifier written in `file` names, by its source or output name. */
274
+ function fileNamed(file, specifier, fileIdByName) {
275
+ return specifierCandidates(dirnamePath(file.fileName), specifier)
276
+ .map((path) => fileIdByName.get(path))
277
+ .find((id) => id !== undefined);
278
+ }
214
279
  /** Whether one node is a string literal or a template literal with no substitution. */
215
280
  function isLiteralText(node) {
216
281
  return node !== undefined && (isStringLiteral(node) || isNoSubstitutionTemplateLiteral(node));
@@ -218,10 +283,33 @@ function isLiteralText(node) {
218
283
  /**
219
284
  * The module specifier of the module one node evaluates, if it evaluates one: an import
220
285
  * declaration, an import-equals declaration of an external module, an export
221
- * declaration that names a module, and an `import()` call whose argument is literal
222
- * text. A type-only form evaluates nothing. An `import()` of a computed argument names
223
- * no module this pass can know.
286
+ * declaration that names a module, an `import()` call whose argument is literal text, and
287
+ * a call that may be a `require()` of literal text: one written as the name `require`, or
288
+ * one of a relative specifier through another name. A type-only form evaluates nothing.
289
+ * An `import()` of a computed argument names no module this pass can know.
224
290
  */
291
+ function evaluationOf(node) {
292
+ if (isCallExpression(node) && node.expression.kind !== SyntaxKind.ImportKeyword) {
293
+ const [argument] = node.arguments;
294
+ const require = writtenName(node.expression);
295
+ if (require === undefined ||
296
+ !isLiteralText(argument) ||
297
+ (require.text !== "require" && !/^\.\.?\//u.test(argument.text))) {
298
+ return undefined;
299
+ }
300
+ return { node: argument, require, whole: !discarded(node) };
301
+ }
302
+ const specifier = evaluatedBy(node);
303
+ return specifier === undefined ? undefined : { node: specifier };
304
+ }
305
+ /** Whether an expression's value is thrown away: it is a statement of its own, or `void`'s operand. */
306
+ function discarded(expression) {
307
+ let at = expression.parent;
308
+ while (isParenthesizedExpression(at)) {
309
+ at = at.parent;
310
+ }
311
+ return isExpressionStatement(at) || isVoidExpression(at);
312
+ }
225
313
  function evaluatedBy(node) {
226
314
  if (isImportDeclaration(node)) {
227
315
  return node.importClause?.phaseModifier === SyntaxKind.TypeKeyword
@@ -330,6 +418,8 @@ function sitesOf(file, declarations, fileId, unread) {
330
418
  const evaluations = [];
331
419
  const wholes = [];
332
420
  const urls = [];
421
+ const workers = [];
422
+ const loadCalls = [];
333
423
  const decorated = [];
334
424
  const parameterWrites = [];
335
425
  const namespaces = namespaceUses(file);
@@ -405,13 +495,20 @@ function sitesOf(file, declarations, fileId, unread) {
405
495
  reads.push({ ...read, from: enclosing });
406
496
  }
407
497
  links.push(...linksOf(file, node, declarations, enclosing));
408
- const evaluated = evaluatedBy(node);
498
+ const evaluated = evaluationOf(node);
409
499
  if (evaluated !== undefined) {
410
- evaluations.push({ node: evaluated, from: enclosing });
500
+ evaluations.push({ ...evaluated, from: enclosing });
411
501
  }
412
502
  const url = moduleURLOf(node);
413
503
  if (url !== undefined) {
414
- urls.push(url);
504
+ urls.push({ ...url, from: enclosing });
505
+ }
506
+ const worker = workerOf(node);
507
+ if (worker !== undefined) {
508
+ workers.push({ ...worker, from: enclosing });
509
+ }
510
+ if (isLoadCall(node)) {
511
+ loadCalls.push({ call: node, from: enclosing });
415
512
  }
416
513
  const held = declarations.get(nodeKey(file, node));
417
514
  const outer = enclosing;
@@ -445,6 +542,8 @@ function sitesOf(file, declarations, fileId, unread) {
445
542
  evaluations,
446
543
  wholes,
447
544
  urls,
545
+ workers,
546
+ loadCalls,
448
547
  decorated,
449
548
  parameterWrites,
450
549
  keyed,
@@ -611,20 +710,12 @@ function aloneIn(file, uses, writes, found) {
611
710
  return result;
612
711
  }
613
712
  /**
614
- * Every reference one project's own files make to the project's own declarations, in
615
- * position order, and the files the test-file rules classified.
616
- *
617
- * `held` is the inventory of the same project, whose declaration map is what a resolved
618
- * symbol is looked up in; `targetRoot` is the absolute path every position is rendered
619
- * against, which is the root the inventory was read against. A pass over a consumer
620
- * walks the consumer's files instead and renders against its root, as
621
- * {@link ReferenceOptions.consumer} states.
622
- *
623
- * The order is by the referencing position, then by the declaration named, the
624
- * declaration referencing it and the use. That is a total order because one name
625
- * references one declaration once from one declaration, while a chain of aliases reaches
626
- * several declarations at one position and a bare star re-export's specifier both reads
627
- * and evaluates the module it names.
713
+ * Every reference one project's own files make to its own declarations, and the files the
714
+ * test-file rules classified. `held` is the same project's inventory, where a resolved symbol
715
+ * is looked up; positions render against `targetRoot`, or a consumer's root as
716
+ * {@link ReferenceOptions.consumer} states. The order is by position, then the declaration
717
+ * named, the referencing declaration and the use: an alias chain or a bare star re-export
718
+ * names several at one position, and one name references one declaration once.
628
719
  */
629
720
  export function references(project, held, targetRoot, options) {
630
721
  const cap = options.batchCap ?? DEFAULT_BATCH_CAP;
@@ -638,6 +729,10 @@ export function references(project, held, targetRoot, options) {
638
729
  let batched = 0;
639
730
  let fileBatches = 0;
640
731
  let residueFallbacks = 0;
732
+ let platformLookups = 0;
733
+ const asked = () => {
734
+ platformLookups += 1;
735
+ };
641
736
  let shorthandLookups = 0;
642
737
  const consumer = options.consumer;
643
738
  const root = consumer?.root ?? targetRoot;
@@ -673,6 +768,8 @@ export function references(project, held, targetRoot, options) {
673
768
  }
674
769
  return moduleBodies.get(moduleId) ?? [];
675
770
  };
771
+ let ownFiles;
772
+ const unseenLoads = [];
676
773
  const exportsCache = new Map();
677
774
  /**
678
775
  * Every declaration one module exports, each re-export walked along its chain. A module
@@ -716,7 +813,7 @@ export function references(project, held, targetRoot, options) {
716
813
  * live. A name a namespace import binds may be a use of any file.
717
814
  */
718
815
  const named = (node) => {
719
- const byName = declarationsByName(held);
816
+ const byName = held.byName;
720
817
  const text = node.getText();
721
818
  const imported = importedNames(node.getSourceFile()).get(text) ?? [];
722
819
  return [
@@ -740,7 +837,7 @@ export function references(project, held, targetRoot, options) {
740
837
  return;
741
838
  }
742
839
  aliased.set(id, symbol === UNANSWERED
743
- ? (declarationsByName(held).get(alias.name) ?? []).map((one) => ({
840
+ ? (held.byName.get(alias.name) ?? []).map((one) => ({
744
841
  id: one,
745
842
  stepped: true,
746
843
  guessed: true,
@@ -783,6 +880,10 @@ export function references(project, held, targetRoot, options) {
783
880
  return { file, fileId, ...sitesOf(file, declarations, fileId, unread) };
784
881
  });
785
882
  const fileIdByName = new Map(walked.filter((one) => fileIds.has(one.fileId)).map((one) => [one.file.fileName, one.fileId]));
883
+ const loadable = walked
884
+ .filter((one) => fileIds.has(one.fileId))
885
+ .map(({ file, fileId }) => ({ file, fileId }));
886
+ const loadableById = new Map(loadable.map((one) => [one.fileId, one.file]));
786
887
  const contextual = contextualUses(project, held, chains, walked.flatMap((one) => one.literals), walked.flatMap((one) => one.flows), cap, tables);
787
888
  const writtenBy = new Map();
788
889
  for (const write of contextual.writes) {
@@ -862,8 +963,9 @@ export function references(project, held, targetRoot, options) {
862
963
  }
863
964
  }
864
965
  const keyNames = computedKeyNames(held.symbols);
865
- for (const { file, fileId, uses, reads, links, evaluations, wholes, urls, decorated, parameterWrites, keyed, } of walked) {
966
+ for (const { file, fileId, uses, reads, links, evaluations, wholes, urls, workers, loadCalls, decorated, parameterWrites, keyed, } of walked) {
866
967
  const test = testFile.get(file) === true;
968
+ const loads = computedLoadsOf(file, loadCalls, loadable, (fileName) => options.directories.bundlerRootOf(fileName));
867
969
  // A component file's markup may use any binding its blocks declare at the top level.
868
970
  const markup = isComponentFile(file);
869
971
  const bindings = markup ? importBindings(file) : [];
@@ -900,10 +1002,20 @@ export function references(project, held, targetRoot, options) {
900
1002
  ...new Set([
901
1003
  ...uses.filter((site) => site.shorthand === undefined).map((site) => site.node),
902
1004
  ...links.map((link) => link.node),
903
- ...evaluations.map((evaluation) => evaluation.node),
1005
+ ...evaluations.flatMap((evaluation) => evaluation.require === undefined
1006
+ ? [evaluation.node]
1007
+ : [
1008
+ evaluation.require,
1009
+ ...(evaluation.require.text === "require" ? [evaluation.node] : []),
1010
+ ]),
904
1011
  ...wholes.map((whole) => whole.specifier),
905
1012
  ...urls.flatMap((url) => url.constructor ?? []),
1013
+ ...workers.flatMap((worker) => worker.holder === undefined ? [worker.constructor] : [worker.constructor, worker.holder]),
906
1014
  ...bindings.map((binding) => binding.node),
1015
+ ...loads.flatMap((load) => [
1016
+ ...(load.require === undefined ? [] : [load.require]),
1017
+ ...(load.asset ? [] : load.files.map((loaded) => loaded.file)),
1018
+ ]),
907
1019
  ]),
908
1020
  ];
909
1021
  const answered = new Map();
@@ -939,7 +1051,22 @@ export function references(project, held, targetRoot, options) {
939
1051
  });
940
1052
  }
941
1053
  }
942
- for (const evaluation of evaluations) {
1054
+ const loading = evaluations.filter((one) => one.require === undefined ||
1055
+ callsPlatform(project, "require", one.require, answered.get(one.require), asked));
1056
+ // A script declares no module, so the checker names nothing for a specifier of one;
1057
+ // the compiler's module resolution still names its file.
1058
+ const unnamed = loading.flatMap(({ node }) => answered.get(node) === undefined &&
1059
+ isStringLiteralLikeNode(node) &&
1060
+ queriedFile(file, node, fileIdByName) === undefined
1061
+ ? [node]
1062
+ : []);
1063
+ const resolved = project.resolvedFileNames(unnamed);
1064
+ const scripts = new Map(unnamed.map((node, at) => {
1065
+ const name = resolved[at];
1066
+ return [node, name === undefined ? undefined : fileIdByName.get(name)];
1067
+ }));
1068
+ const wholeReads = [];
1069
+ for (const evaluation of loading) {
943
1070
  const queried = queriedFile(file, evaluation.node, fileIdByName);
944
1071
  if (queried !== undefined) {
945
1072
  found.push({
@@ -952,6 +1079,28 @@ export function references(project, held, targetRoot, options) {
952
1079
  });
953
1080
  }
954
1081
  const module = answered.get(evaluation.node);
1082
+ // Node's require resolves a relative specifier the program records no resolution for,
1083
+ // as in a TypeScript file, by its own extension search.
1084
+ const script = scripts.get(evaluation.node) ??
1085
+ (evaluation.require !== undefined &&
1086
+ module === undefined &&
1087
+ isLiteralText(evaluation.node) &&
1088
+ /^\.\.?\//u.test(evaluation.node.text)
1089
+ ? fileNamed(file, evaluation.node.text, fileIdByName)
1090
+ : undefined);
1091
+ if (script !== undefined && evaluation.whole === true && module === undefined) {
1092
+ wholeReads.push({ evaluation, fileId: script });
1093
+ }
1094
+ if (script !== undefined) {
1095
+ found.push({
1096
+ from: evaluation.from,
1097
+ to: script,
1098
+ position: renderPosition(file, root, evaluation.node.getStart()),
1099
+ use: "evaluation",
1100
+ resolution: "syntax",
1101
+ test,
1102
+ });
1103
+ }
955
1104
  if (module === undefined) {
956
1105
  continue;
957
1106
  }
@@ -971,21 +1120,37 @@ export function references(project, held, targetRoot, options) {
971
1120
  }
972
1121
  }
973
1122
  }
1123
+ // A required file the checker names no module for is read whole where the call's value
1124
+ // is used: the value is the file's exports object, which this pass does not follow.
1125
+ const required = wholeReads.flatMap((one) => {
1126
+ const loaded = loadableById.get(one.fileId);
1127
+ return loaded === undefined ? [] : [{ ...one, loaded }];
1128
+ });
1129
+ const modules = project.symbolsAt(required.map((one) => project.handle(one.loaded)), cap);
1130
+ required.forEach(({ evaluation }, at) => {
1131
+ const module = modules[at];
1132
+ const position = renderPosition(file, root, evaluation.node.getStart());
1133
+ for (const target of module === undefined ? [] : exportsRead(module)) {
1134
+ found.push({
1135
+ from: evaluation.from,
1136
+ to: target.id,
1137
+ position,
1138
+ use: "read",
1139
+ resolution: target.guessed ? "by-name" : "batch",
1140
+ test,
1141
+ });
1142
+ }
1143
+ });
974
1144
  for (const url of urls) {
975
- const constructor = url.constructor === undefined ? undefined : answered.get(url.constructor);
976
- // An augmentation of the global `URL` merges into it, which keeps the
977
- // library's declaration; a `URL` the target declares has no other.
978
- if (typeof constructor === "object" &&
979
- constructor.declarations.length > 0 &&
980
- constructor.declarations.every((handle) => ownPaths.has(handle.path))) {
1145
+ if (url.constructor !== undefined &&
1146
+ !callsPlatform(project, "URL", url.constructor, answered.get(url.constructor), asked)) {
981
1147
  continue;
982
1148
  }
983
- const loaded = specifierCandidates(dirnamePath(file.fileName), url.specifier)
984
- .map((path) => fileIdByName.get(path))
985
- .find((id) => id !== undefined);
1149
+ const path = relativeURLPath(dirnamePath(file.fileName), url.specifier);
1150
+ const loaded = path === undefined ? undefined : fileNamed(file, path, fileIdByName);
986
1151
  if (loaded !== undefined) {
987
1152
  found.push({
988
- from: fileId,
1153
+ from: url.from,
989
1154
  to: loaded,
990
1155
  position: renderPosition(file, root, url.at.getStart()),
991
1156
  use: "evaluation",
@@ -994,6 +1159,70 @@ export function references(project, held, targetRoot, options) {
994
1159
  });
995
1160
  }
996
1161
  }
1162
+ for (const worker of workers) {
1163
+ const symbol = answered.get(worker.constructor);
1164
+ const text = worker.address.text;
1165
+ let path;
1166
+ if (startsThread(project, symbol, worker.holder && answered.get(worker.holder), asked)) {
1167
+ path =
1168
+ worker.evaluated || !/^\.\.?\//u.test(text)
1169
+ ? undefined
1170
+ : resolvePath(options.directories.workingDirectoryOf(file.fileName), text);
1171
+ }
1172
+ else if ((WORKER_CONSTRUCTORS.has(worker.constructor.text) ||
1173
+ worker.constructor.text === "navigator") &&
1174
+ isPlatformGlobal(project, symbol)) {
1175
+ path = relativeURLPath(dirnamePath(file.fileName), text);
1176
+ }
1177
+ ownFiles ??= sourceFilesOf(project, targetRoot);
1178
+ for (const started of path === undefined ? [] : ownFiles.named(path)) {
1179
+ const to = declaredAt(started);
1180
+ if (to !== undefined && fileIds.has(to)) {
1181
+ found.push({
1182
+ from: worker.from,
1183
+ to,
1184
+ position: renderPosition(file, root, worker.at.getStart()),
1185
+ use: "evaluation",
1186
+ resolution: "syntax",
1187
+ test,
1188
+ });
1189
+ }
1190
+ }
1191
+ }
1192
+ // A computed load imports each file it loads where the call is written, reading what
1193
+ // the file exports unless it reads the file as text or a URL; callsPlatform owns which
1194
+ // call is a `require`.
1195
+ for (const load of loads) {
1196
+ if (load.require !== undefined &&
1197
+ !callsPlatform(project, "require", load.require, answered.get(load.require), asked)) {
1198
+ continue;
1199
+ }
1200
+ const position = renderPosition(file, root, load.call.getStart());
1201
+ if (!load.resolved && !test && consumer === undefined && !file.isDeclarationFile) {
1202
+ unseenLoads.push(position);
1203
+ }
1204
+ for (const loaded of load.files) {
1205
+ found.push({
1206
+ from: load.eager ? fileId : load.from,
1207
+ to: loaded.fileId,
1208
+ position,
1209
+ use: "evaluation",
1210
+ resolution: "syntax",
1211
+ test,
1212
+ });
1213
+ const module = load.asset ? undefined : answered.get(loaded.file);
1214
+ for (const target of module === undefined ? [] : exportsRead(module)) {
1215
+ found.push({
1216
+ from: load.from,
1217
+ to: target.id,
1218
+ position,
1219
+ use: "read",
1220
+ resolution: target.guessed ? "by-name" : "batch",
1221
+ test,
1222
+ });
1223
+ }
1224
+ }
1225
+ }
997
1226
  for (const whole of wholes) {
998
1227
  const module = answered.get(whole.specifier);
999
1228
  if (module === undefined) {
@@ -1060,7 +1289,7 @@ export function references(project, held, targetRoot, options) {
1060
1289
  }
1061
1290
  const position = renderPosition(file, root, read.key.getStart());
1062
1291
  const targets = symbol === UNANSWERED
1063
- ? (declarationsByName(held).get(read.name) ?? []).map((id) => ({
1292
+ ? (held.byName.get(read.name) ?? []).map((id) => ({
1064
1293
  id,
1065
1294
  stepped: false,
1066
1295
  guessed: true,
@@ -1187,12 +1416,14 @@ export function references(project, held, targetRoot, options) {
1187
1416
  ? found
1188
1417
  : found.map((reference) => ({ ...reference, consumer: consumer.id })),
1189
1418
  testFilePaths: tests.sort(bytewise),
1419
+ unseenLoads,
1190
1420
  cost: {
1191
1421
  batched,
1192
1422
  fileBatches,
1193
1423
  residueFallbacks,
1194
1424
  shorthandLookups,
1195
1425
  aliasSteps: chains.steps,
1426
+ platformLookups,
1196
1427
  patternBatches: destructured.cost.patternBatches,
1197
1428
  patternLookups: destructured.cost.patternLookups,
1198
1429
  contextualBatches: contextual.batches,
@@ -1,7 +1,7 @@
1
1
  import { SETTINGS } from "./contract/config.generated.js";
2
2
  import { FAMILY_KEY_LENGTH } from "./contract/contract.generated.js";
3
3
  import {} from "./config.js";
4
- import { CONVENTION_ROWS } from "./convention-rows.js";
4
+ import { CONVENTION_ROWS } from "./entries/convention-rows.js";
5
5
  import { refuseOverBound } from "./fs/document.js";
6
6
  import { dotted, isRecord, locatedFault, placeIn, pointerOf, readStrict, StrictJSONError, } from "./json-document.js";
7
7
  import { FIXED_SEVERITY_CODES } from "./kinds.js";
@@ -6,10 +6,10 @@
6
6
  */
7
7
  import { isCallExpression, isExportDeclaration, isExternalModuleReference, isImportDeclaration, isImportEqualsDeclaration, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/ast";
8
8
  import { SymbolFlags } from "@typescript/native/sync";
9
- import { resolvedTargets } from "./calls.js";
10
- import { declaredComponent } from "./inventory.js";
11
- import { must, Unanswerable } from "./query.js";
12
- import { nameComponent } from "./ref.js";
9
+ import { resolvedTargets } from "../calls.js";
10
+ import { declaredComponent } from "../inventory.js";
11
+ import { must, Unanswerable } from "../query.js";
12
+ import { nameComponent } from "../ref.js";
13
13
  /** The meanings a name of the global scope is looked up under: any of them. */
14
14
  const ANY_MEANING = SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace;
15
15
  /** The suffix that marks a static member at the end of a path. */