@cplieger/deadset-ts 8.0.0-dev.15 → 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 (134) hide show
  1. package/dist/src/analysis.js +41 -37
  2. package/dist/src/commands/read.js +6 -7
  3. package/dist/src/conformance-record.js +1 -1
  4. package/dist/src/consumers.js +3 -1
  5. package/dist/src/contract/config.generated.js +1 -1
  6. package/dist/src/contract/contract.generated.js +119 -2
  7. package/dist/src/dependencies.js +79 -48
  8. package/dist/src/entries/build-scripts.js +222 -0
  9. package/dist/src/{configuration-files.js → entries/configuration-files.js} +20 -58
  10. package/dist/src/{convention-rows.js → entries/convention-rows.js} +120 -27
  11. package/dist/src/entries/conventions.js +739 -0
  12. package/dist/src/{html-entries.js → entries/html-entries.js} +3 -3
  13. package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +2 -2
  14. package/dist/src/entries/run-directories.js +77 -0
  15. package/dist/src/entries/sources.js +144 -0
  16. package/dist/src/entries/static-module.js +669 -0
  17. package/dist/src/entries/test-runners.js +684 -0
  18. package/dist/src/{workflow-steps.js → entries/workflow-steps.js} +18 -8
  19. package/dist/src/extensions.js +37 -0
  20. package/dist/src/facts.js +38 -0
  21. package/dist/src/findings/emitters.js +5 -8
  22. package/dist/src/findings/self-check.js +36 -20
  23. package/dist/src/findings-pass.js +5 -6
  24. package/dist/src/framework-lifecycle.js +1 -1
  25. package/dist/src/fs/manifest.js +15 -20
  26. package/dist/src/generated-file.js +172 -29
  27. package/dist/src/graph.js +2 -4
  28. package/dist/src/index.js +1 -1
  29. package/dist/src/injection-container.js +1 -1
  30. package/dist/src/inventory/commonjs.js +62 -17
  31. package/dist/src/inventory/enumerate.js +2 -1
  32. package/dist/src/javascript-file.js +26 -11
  33. package/dist/src/load/classify.js +28 -9
  34. package/dist/src/load/diagnostics.js +8 -1
  35. package/dist/src/load/discover.js +15 -7
  36. package/dist/src/load/plan.js +2 -2
  37. package/dist/src/load/synthesize.js +5 -5
  38. package/dist/src/load/workspace-manifests.js +1 -1
  39. package/dist/src/matrix.js +6 -13
  40. package/dist/src/outside-tests.js +1 -1
  41. package/dist/src/paths.js +16 -0
  42. package/dist/src/platform-calls.js +78 -0
  43. package/dist/src/platform-symbols.js +140 -0
  44. package/dist/src/references.js +202 -32
  45. package/dist/src/resolve.js +1 -1
  46. package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +4 -4
  47. package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +5 -8
  48. package/dist/src/{manifest.js → roots/manifest.js} +4 -4
  49. package/dist/src/roots/published-reach.js +412 -0
  50. package/dist/src/roots/root-set.js +489 -0
  51. package/dist/src/roots/unseen-loads.js +208 -0
  52. package/dist/src/serialization-contract.js +1 -1
  53. package/dist/src/source-files.js +5 -9
  54. package/dist/src/suppress.js +30 -32
  55. package/dist/src/test-support.js +4 -2
  56. package/dist/src/verbs/analyze.js +13 -1
  57. package/dist/src/verbs/describe.js +5 -3
  58. package/jsr.json +1 -1
  59. package/package.json +2 -2
  60. package/src/analysis.ts +75 -52
  61. package/src/automatic-types.ts +1 -1
  62. package/src/commands/read.ts +6 -8
  63. package/src/conformance-record.ts +1 -1
  64. package/src/consumers.ts +4 -0
  65. package/src/contract/config.generated.ts +1 -1
  66. package/src/contract/contract.generated.ts +138 -2
  67. package/src/dependencies.ts +96 -49
  68. package/src/entries/build-scripts.ts +292 -0
  69. package/src/{configuration-files.ts → entries/configuration-files.ts} +23 -75
  70. package/src/{convention-rows.ts → entries/convention-rows.ts} +167 -28
  71. package/src/{conventions.ts → entries/conventions.ts} +347 -522
  72. package/src/{html-entries.ts → entries/html-entries.ts} +5 -5
  73. package/src/{mdx-entries.ts → entries/mdx-entries.ts} +4 -4
  74. package/src/entries/run-directories.ts +108 -0
  75. package/src/entries/sources.ts +266 -0
  76. package/src/entries/static-module.ts +857 -0
  77. package/src/entries/test-runners.ts +868 -0
  78. package/src/{workflow-steps.ts → entries/workflow-steps.ts} +29 -11
  79. package/src/exempt.ts +3 -2
  80. package/src/extensions.ts +45 -0
  81. package/src/facts.ts +44 -0
  82. package/src/file-facts.ts +1 -1
  83. package/src/findings/emitters.ts +14 -10
  84. package/src/findings/self-check.ts +41 -22
  85. package/src/findings/visibility-narrowing.ts +1 -1
  86. package/src/findings-pass.ts +5 -12
  87. package/src/framework-lifecycle.ts +1 -1
  88. package/src/fs/manifest.ts +25 -21
  89. package/src/generated-file.ts +240 -36
  90. package/src/graph.ts +3 -5
  91. package/src/index.ts +1 -1
  92. package/src/injection-container.ts +1 -1
  93. package/src/inventory/commonjs.ts +102 -17
  94. package/src/inventory/enumerate.ts +2 -1
  95. package/src/javascript-file.ts +29 -12
  96. package/src/load/classify.ts +49 -13
  97. package/src/load/diagnostics.ts +11 -1
  98. package/src/load/discover.ts +27 -11
  99. package/src/load/plan.ts +11 -2
  100. package/src/load/synthesize.ts +6 -6
  101. package/src/load/test-globals.ts +1 -1
  102. package/src/load/workspace-manifests.ts +1 -1
  103. package/src/load/workspace-run.ts +1 -1
  104. package/src/matrix.ts +7 -14
  105. package/src/outside-tests.ts +1 -1
  106. package/src/paths.ts +18 -0
  107. package/src/platform-calls.ts +119 -0
  108. package/src/platform-symbols.ts +205 -0
  109. package/src/references.ts +281 -34
  110. package/src/report.ts +1 -1
  111. package/src/resolve.ts +1 -1
  112. package/src/{configured-declarations.ts → roots/configured-declarations.ts} +7 -7
  113. package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +5 -8
  114. package/src/{manifest.ts → roots/manifest.ts} +5 -5
  115. package/src/roots/published-reach.ts +481 -0
  116. package/src/{roots.ts → roots/root-set.ts} +203 -630
  117. package/src/roots/unseen-loads.ts +307 -0
  118. package/src/serialization-contract.ts +1 -1
  119. package/src/source-files.ts +5 -10
  120. package/src/suppress.ts +48 -33
  121. package/src/test-support.ts +8 -3
  122. package/src/verbs/analyze.ts +17 -4
  123. package/src/verbs/describe.ts +5 -2
  124. package/src/verbs/explain.ts +1 -1
  125. package/dist/src/build-scripts.js +0 -418
  126. package/dist/src/conventions.js +0 -907
  127. package/dist/src/entry-points.js +0 -444
  128. package/dist/src/own-body.js +0 -28
  129. package/dist/src/roots.js +0 -865
  130. package/dist/src/unseen-loads.js +0 -189
  131. package/src/build-scripts.ts +0 -536
  132. package/src/entry-points.ts +0 -584
  133. package/src/own-body.ts +0 -42
  134. package/src/unseen-loads.ts +0 -282
@@ -0,0 +1,140 @@
1
+ /**
2
+ * The platform's names read through a program's checker: its `require` and `URL`, as
3
+ * `platform-calls.ts` decides them, its worker constructors and `navigator`, and Node's
4
+ * `Worker`.
5
+ */
6
+ import { isArrowFunction, isClassDeclaration, isClassExpression, isFunctionDeclaration, isFunctionExpression, isMethodDeclaration, isModuleDeclaration, isPropertyAssignment, isPropertyDeclaration, isSourceFile, isStringLiteralLikeNode, isVariableDeclaration, } from "@typescript/native/ast";
7
+ import { SymbolFlags } from "@typescript/native/sync";
8
+ import { UNANSWERED } from "./query.js";
9
+ import { boundExports, NAMESPACE } from "./entries/static-module.js";
10
+ import { isAmbient } from "./inventory.js";
11
+ import { callsPlatformBy, carriedNames } from "./platform-calls.js";
12
+ import { unwrapped } from "./unwrap.js";
13
+ /** The symbol one symbol names through its aliases, or the symbol itself where it is none. */
14
+ function dealiased(project, symbol, asked) {
15
+ if ((symbol.flags & SymbolFlags.Alias) === 0) {
16
+ return symbol;
17
+ }
18
+ asked();
19
+ return project.queries.aliased(symbol);
20
+ }
21
+ /** Each declaration of `symbol` in a file of the target, read in this project's program. */
22
+ function ownDeclarations(project, symbol) {
23
+ const own = project.ownPaths();
24
+ return symbol.declarations.flatMap((handle) => {
25
+ const node = own.has(handle.path) ? project.declarationAt(handle)?.node : undefined;
26
+ return node === undefined ? [] : [node];
27
+ });
28
+ }
29
+ /**
30
+ * Whether one declaration writes a function or a class with a body (roots.23): a function
31
+ * or method declaration with a body, a class declaration, or a variable, a property or a
32
+ * class field whose initializer is a function, an arrow function or a class expression,
33
+ * under no `declare` modifier and in no declaration file.
34
+ */
35
+ function writesBody(node) {
36
+ const value = (isVariableDeclaration(node) || isPropertyAssignment(node) || isPropertyDeclaration(node)) &&
37
+ node.initializer !== undefined
38
+ ? unwrapped(node.initializer)
39
+ : node;
40
+ return ((isFunctionDeclaration(value) || isMethodDeclaration(value)
41
+ ? value.body !== undefined
42
+ : isFunctionExpression(value) ||
43
+ isArrowFunction(value) ||
44
+ isClassDeclaration(value) ||
45
+ isClassExpression(value)) && !isAmbient(value.getSourceFile(), value));
46
+ }
47
+ /**
48
+ * What the checker binds `symbol` to, one alias step at a time: every name a step of the
49
+ * chain carries it by, and the declarations of the target its last step reaches, which
50
+ * prove it the target's own where one {@link writesBody}. A chain that ends at no
51
+ * declaration, or that the checker leaves unanswered, reaches none.
52
+ */
53
+ function boundBy(project, symbol, asked) {
54
+ if (typeof symbol !== "object") {
55
+ return undefined;
56
+ }
57
+ const names = new Set();
58
+ const stepped = new Set();
59
+ for (let at = symbol; at !== undefined && !stepped.has(at.id);) {
60
+ stepped.add(at.id);
61
+ names.add(at.name);
62
+ const own = ownDeclarations(project, at);
63
+ for (const node of own) {
64
+ carriedNames(node).forEach((one) => names.add(one));
65
+ }
66
+ if ((at.flags & SymbolFlags.Alias) === 0) {
67
+ return { names, own, provenOwn: own.some(writesBody) };
68
+ }
69
+ asked();
70
+ const next = project.aliasStepOf(at);
71
+ at = typeof next === "object" ? next : undefined;
72
+ }
73
+ return { names, own: [], provenOwn: false };
74
+ }
75
+ /** Each written name's binding, as this project's checker answers it. */
76
+ function checkerBinding(project, asked) {
77
+ return (written) => {
78
+ asked();
79
+ return boundBy(project, project.symbolsAt([project.handle(written)])[0], asked);
80
+ };
81
+ }
82
+ /**
83
+ * Whether a call or construction through `callee`, the batch's answer for which is `symbol`,
84
+ * is one of the platform's `require` or `URL` (roots.23, references-ts.1), as
85
+ * {@link callsPlatformBy} decides it over this project's checker, each request it puts
86
+ * counted by `asked`.
87
+ */
88
+ export function callsPlatform(project, name, callee, symbol, asked) {
89
+ const others = checkerBinding(project, asked);
90
+ return callsPlatformBy(name, callee, (written) => written === callee ? boundBy(project, symbol, asked) : others(written));
91
+ }
92
+ /**
93
+ * Whether a worker's constructor or `navigator`, the batch's answer for which is `symbol`, is
94
+ * the platform's (references-ts.2): a name the checker leaves unanswered, or one whose symbol
95
+ * is no alias and has a declaration outside the target's files.
96
+ */
97
+ export function isPlatformGlobal(project, symbol) {
98
+ if (symbol === UNANSWERED) {
99
+ return true;
100
+ }
101
+ const own = project.ownPaths();
102
+ return (symbol !== undefined &&
103
+ (symbol.flags & SymbolFlags.Alias) === 0 &&
104
+ symbol.declarations.some((handle) => !own.has(handle.path)));
105
+ }
106
+ /** The module names Node's worker threads are imported by. */
107
+ const THREADS = new Set(["worker_threads", "node:worker_threads"]);
108
+ function namesThreads(node) {
109
+ return node !== undefined && isStringLiteralLikeNode(node) && THREADS.has(node.text);
110
+ }
111
+ /** Whether one declaration is written in the body of `declare module "worker_threads"`. */
112
+ function inThreadsModule(node) {
113
+ for (let at = node; !isSourceFile(at); at = at.parent) {
114
+ if (isModuleDeclaration(at) && namesThreads(at.name)) {
115
+ return true;
116
+ }
117
+ }
118
+ return false;
119
+ }
120
+ /**
121
+ * Whether a `Worker` constructor, the batch's answer for which is `symbol`, is Node's
122
+ * (references-ts.2): its symbol resolves to a declaration of `worker_threads`, or one of its
123
+ * own declarations binds the module's `Worker`, or `holder`, the namespace it is read
124
+ * through, binds the module, each binding read as {@link boundExports} reads one, which
125
+ * decides where the module has no declaration. Each request it puts is counted by `asked`.
126
+ */
127
+ export function startsThread(project, symbol, holder, asked) {
128
+ const bindingOf = checkerBinding(project, asked);
129
+ const declared = (one, member) => typeof one === "object" &&
130
+ ownDeclarations(project, one).some((node) => boundExports(node, (specifier) => THREADS.has(specifier), bindingOf).includes(member));
131
+ if (declared(symbol, "Worker") || declared(holder, NAMESPACE)) {
132
+ return true;
133
+ }
134
+ const named = typeof symbol === "object" ? dealiased(project, symbol, asked) : undefined;
135
+ return (typeof named === "object" &&
136
+ named.declarations.some((handle) => {
137
+ const node = project.declarationAt(handle)?.node;
138
+ return node !== undefined && inThreadsModule(node);
139
+ }));
140
+ }
@@ -3,7 +3,7 @@ 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
8
  import { isParameterProperty, nodeKey } from "./inventory.js";
9
9
  import { ambientGlobalInterfaces, augmentationReferences } from "./augmentations.js";
@@ -13,13 +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";
21
- import { hasOwnBody } from "./own-body.js";
22
- import { computedLoadsOf, isLoadCall } from "./unseen-loads.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";
23
25
  import { computedKeyNames, iterationReferences, keyNameOf, keyedUses, } from "./symbol-keys.js";
24
26
  import { handedOn, valuesOf } from "./value-flow.js";
25
27
  import { bytewise } from "./text/compare.js";
@@ -185,25 +187,23 @@ function isImportMetaURL(node) {
185
187
  }
186
188
  /** The module URL one node builds, where it builds one. */
187
189
  function moduleURLOf(node) {
188
- const relative = (argument) => {
190
+ const literal = (argument) => {
189
191
  const at = argument === undefined ? undefined : parenthesesRemoved(argument);
190
- return at !== undefined &&
191
- (isStringLiteral(at) || isNoSubstitutionTemplateLiteral(at)) &&
192
- /^\.\.?\//u.test(at.text)
193
- ? at.text
194
- : undefined;
192
+ return isLiteralText(at) ? at.text : undefined;
195
193
  };
196
- 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) {
197
196
  const [reference, base] = node.arguments ?? [];
198
- const specifier = relative(reference);
197
+ const specifier = literal(reference);
199
198
  return specifier !== undefined && base !== undefined && isImportMetaURL(base)
200
- ? { specifier, constructor: node.expression, at: node }
199
+ ? { specifier, constructor, at: node }
201
200
  : undefined;
202
201
  }
203
202
  if (isCallExpression(node)) {
204
203
  const callee = parenthesesRemoved(node.expression);
205
- const specifier = relative(node.arguments[0]);
204
+ const specifier = literal(node.arguments[0]);
206
205
  if (specifier !== undefined &&
206
+ /^\.\.?\//u.test(specifier) &&
207
207
  isPropertyAccessExpression(callee) &&
208
208
  isMetaProperty(callee.expression) &&
209
209
  callee.expression.keywordToken === SyntaxKind.ImportKeyword &&
@@ -213,6 +213,63 @@ function moduleURLOf(node) {
213
213
  }
214
214
  return undefined;
215
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
+ }
216
273
  /** The file of the target a relative specifier written in `file` names, by its source or output name. */
217
274
  function fileNamed(file, specifier, fileIdByName) {
218
275
  return specifierCandidates(dirnamePath(file.fileName), specifier)
@@ -226,10 +283,33 @@ function isLiteralText(node) {
226
283
  /**
227
284
  * The module specifier of the module one node evaluates, if it evaluates one: an import
228
285
  * declaration, an import-equals declaration of an external module, an export
229
- * declaration that names a module, and an `import()` call whose argument is literal
230
- * text. A type-only form evaluates nothing. An `import()` of a computed argument names
231
- * 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.
232
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
+ }
233
313
  function evaluatedBy(node) {
234
314
  if (isImportDeclaration(node)) {
235
315
  return node.importClause?.phaseModifier === SyntaxKind.TypeKeyword
@@ -338,6 +418,7 @@ function sitesOf(file, declarations, fileId, unread) {
338
418
  const evaluations = [];
339
419
  const wholes = [];
340
420
  const urls = [];
421
+ const workers = [];
341
422
  const loadCalls = [];
342
423
  const decorated = [];
343
424
  const parameterWrites = [];
@@ -414,14 +495,18 @@ function sitesOf(file, declarations, fileId, unread) {
414
495
  reads.push({ ...read, from: enclosing });
415
496
  }
416
497
  links.push(...linksOf(file, node, declarations, enclosing));
417
- const evaluated = evaluatedBy(node);
498
+ const evaluated = evaluationOf(node);
418
499
  if (evaluated !== undefined) {
419
- evaluations.push({ node: evaluated, from: enclosing });
500
+ evaluations.push({ ...evaluated, from: enclosing });
420
501
  }
421
502
  const url = moduleURLOf(node);
422
503
  if (url !== undefined) {
423
504
  urls.push({ ...url, from: enclosing });
424
505
  }
506
+ const worker = workerOf(node);
507
+ if (worker !== undefined) {
508
+ workers.push({ ...worker, from: enclosing });
509
+ }
425
510
  if (isLoadCall(node)) {
426
511
  loadCalls.push({ call: node, from: enclosing });
427
512
  }
@@ -457,6 +542,7 @@ function sitesOf(file, declarations, fileId, unread) {
457
542
  evaluations,
458
543
  wholes,
459
544
  urls,
545
+ workers,
460
546
  loadCalls,
461
547
  decorated,
462
548
  parameterWrites,
@@ -643,6 +729,10 @@ export function references(project, held, targetRoot, options) {
643
729
  let batched = 0;
644
730
  let fileBatches = 0;
645
731
  let residueFallbacks = 0;
732
+ let platformLookups = 0;
733
+ const asked = () => {
734
+ platformLookups += 1;
735
+ };
646
736
  let shorthandLookups = 0;
647
737
  const consumer = options.consumer;
648
738
  const root = consumer?.root ?? targetRoot;
@@ -678,6 +768,8 @@ export function references(project, held, targetRoot, options) {
678
768
  }
679
769
  return moduleBodies.get(moduleId) ?? [];
680
770
  };
771
+ let ownFiles;
772
+ const unseenLoads = [];
681
773
  const exportsCache = new Map();
682
774
  /**
683
775
  * Every declaration one module exports, each re-export walked along its chain. A module
@@ -791,6 +883,7 @@ export function references(project, held, targetRoot, options) {
791
883
  const loadable = walked
792
884
  .filter((one) => fileIds.has(one.fileId))
793
885
  .map(({ file, fileId }) => ({ file, fileId }));
886
+ const loadableById = new Map(loadable.map((one) => [one.fileId, one.file]));
794
887
  const contextual = contextualUses(project, held, chains, walked.flatMap((one) => one.literals), walked.flatMap((one) => one.flows), cap, tables);
795
888
  const writtenBy = new Map();
796
889
  for (const write of contextual.writes) {
@@ -870,9 +963,9 @@ export function references(project, held, targetRoot, options) {
870
963
  }
871
964
  }
872
965
  const keyNames = computedKeyNames(held.symbols);
873
- for (const { file, fileId, uses, reads, links, evaluations, wholes, urls, loadCalls, decorated, parameterWrites, keyed, } of walked) {
966
+ for (const { file, fileId, uses, reads, links, evaluations, wholes, urls, workers, loadCalls, decorated, parameterWrites, keyed, } of walked) {
874
967
  const test = testFile.get(file) === true;
875
- const loads = computedLoadsOf(file, loadCalls, loadable);
968
+ const loads = computedLoadsOf(file, loadCalls, loadable, (fileName) => options.directories.bundlerRootOf(fileName));
876
969
  // A component file's markup may use any binding its blocks declare at the top level.
877
970
  const markup = isComponentFile(file);
878
971
  const bindings = markup ? importBindings(file) : [];
@@ -909,9 +1002,15 @@ export function references(project, held, targetRoot, options) {
909
1002
  ...new Set([
910
1003
  ...uses.filter((site) => site.shorthand === undefined).map((site) => site.node),
911
1004
  ...links.map((link) => link.node),
912
- ...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
+ ]),
913
1011
  ...wholes.map((whole) => whole.specifier),
914
1012
  ...urls.flatMap((url) => url.constructor ?? []),
1013
+ ...workers.flatMap((worker) => worker.holder === undefined ? [worker.constructor] : [worker.constructor, worker.holder]),
915
1014
  ...bindings.map((binding) => binding.node),
916
1015
  ...loads.flatMap((load) => [
917
1016
  ...(load.require === undefined ? [] : [load.require]),
@@ -952,9 +1051,11 @@ export function references(project, held, targetRoot, options) {
952
1051
  });
953
1052
  }
954
1053
  }
1054
+ const loading = evaluations.filter((one) => one.require === undefined ||
1055
+ callsPlatform(project, "require", one.require, answered.get(one.require), asked));
955
1056
  // A script declares no module, so the checker names nothing for a specifier of one;
956
1057
  // the compiler's module resolution still names its file.
957
- const unnamed = evaluations.flatMap(({ node }) => answered.get(node) === undefined &&
1058
+ const unnamed = loading.flatMap(({ node }) => answered.get(node) === undefined &&
958
1059
  isStringLiteralLikeNode(node) &&
959
1060
  queriedFile(file, node, fileIdByName) === undefined
960
1061
  ? [node]
@@ -964,7 +1065,8 @@ export function references(project, held, targetRoot, options) {
964
1065
  const name = resolved[at];
965
1066
  return [node, name === undefined ? undefined : fileIdByName.get(name)];
966
1067
  }));
967
- for (const evaluation of evaluations) {
1068
+ const wholeReads = [];
1069
+ for (const evaluation of loading) {
968
1070
  const queried = queriedFile(file, evaluation.node, fileIdByName);
969
1071
  if (queried !== undefined) {
970
1072
  found.push({
@@ -977,7 +1079,18 @@ export function references(project, held, targetRoot, options) {
977
1079
  });
978
1080
  }
979
1081
  const module = answered.get(evaluation.node);
980
- const script = scripts.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
+ }
981
1094
  if (script !== undefined) {
982
1095
  found.push({
983
1096
  from: evaluation.from,
@@ -1007,12 +1120,34 @@ export function references(project, held, targetRoot, options) {
1007
1120
  }
1008
1121
  }
1009
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
+ });
1010
1144
  for (const url of urls) {
1011
- const constructor = url.constructor === undefined ? undefined : answered.get(url.constructor);
1012
- if (typeof constructor === "object" && hasOwnBody(project, constructor)) {
1145
+ if (url.constructor !== undefined &&
1146
+ !callsPlatform(project, "URL", url.constructor, answered.get(url.constructor), asked)) {
1013
1147
  continue;
1014
1148
  }
1015
- const loaded = fileNamed(file, url.specifier, fileIdByName);
1149
+ const path = relativeURLPath(dirnamePath(file.fileName), url.specifier);
1150
+ const loaded = path === undefined ? undefined : fileNamed(file, path, fileIdByName);
1016
1151
  if (loaded !== undefined) {
1017
1152
  found.push({
1018
1153
  from: url.from,
@@ -1024,15 +1159,48 @@ export function references(project, held, targetRoot, options) {
1024
1159
  });
1025
1160
  }
1026
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
+ }
1027
1192
  // A computed load imports each file it loads where the call is written, reading what
1028
- // the file exports unless it reads the file as text or a URL; hasOwnBody owns which
1029
- // `require` loads nothing.
1193
+ // the file exports unless it reads the file as text or a URL; callsPlatform owns which
1194
+ // call is a `require`.
1030
1195
  for (const load of loads) {
1031
- const callee = load.require === undefined ? undefined : answered.get(load.require);
1032
- if (typeof callee === "object" && hasOwnBody(project, callee)) {
1196
+ if (load.require !== undefined &&
1197
+ !callsPlatform(project, "require", load.require, answered.get(load.require), asked)) {
1033
1198
  continue;
1034
1199
  }
1035
1200
  const position = renderPosition(file, root, load.call.getStart());
1201
+ if (!load.resolved && !test && consumer === undefined && !file.isDeclarationFile) {
1202
+ unseenLoads.push(position);
1203
+ }
1036
1204
  for (const loaded of load.files) {
1037
1205
  found.push({
1038
1206
  from: load.eager ? fileId : load.from,
@@ -1248,12 +1416,14 @@ export function references(project, held, targetRoot, options) {
1248
1416
  ? found
1249
1417
  : found.map((reference) => ({ ...reference, consumer: consumer.id })),
1250
1418
  testFilePaths: tests.sort(bytewise),
1419
+ unseenLoads,
1251
1420
  cost: {
1252
1421
  batched,
1253
1422
  fileBatches,
1254
1423
  residueFallbacks,
1255
1424
  shorthandLookups,
1256
1425
  aliasSteps: chains.steps,
1426
+ platformLookups,
1257
1427
  patternBatches: destructured.cost.patternBatches,
1258
1428
  patternLookups: destructured.cost.patternLookups,
1259
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. */
@@ -1,13 +1,10 @@
1
1
  /**
2
- * Every entry-point convention this analyzer declines, each with the tool it belongs
3
- * to and why. A file only a declined convention enters is reported as never
4
- * imported, so a project that relies on one names the file in `ts.entry_files` or
5
- * `roots.patterns`, and this list is what tells it to.
6
- *
7
- * Every row carries a reason, because the list is read where the rows are shown
8
- * one at a time: beside a finding, or in a report's declared gaps.
2
+ * Every entry-point convention this analyzer declines, each with its tool and why. A file only
3
+ * a declined convention enters is reported as never imported, so a project relying on one names
4
+ * the file in `ts.entry_files` or `roots.patterns`, and this list tells it to. Every row carries
5
+ * a reason, since rows are shown one at a time, beside a finding or in a report's declared gaps.
9
6
  */
10
- import { bytewise } from "./text/compare.js";
7
+ import { bytewise } from "../text/compare.js";
11
8
  /** What is declined for a tool whose own conventions are entered only through its configuration files. */
12
9
  const EVERY_CONVENTION = "every file it loads by its own convention beyond its configuration files";
13
10
  /** Why a tool whose own conventions are not read declines them. */
@@ -4,10 +4,10 @@
4
4
  * shell running its command or one of its scripts. {@link readManifest} reads one
5
5
  * directory's manifest, {@link packageEntries} those of the packages below the target root.
6
6
  */
7
- import { isAbsolutePath, joinPath, relativePath, resolvePath } from "./paths.js";
8
- import { isRecord } from "./json-document.js";
9
- import { MANIFEST, manifestAt, readJSON } from "./fs/manifest.js";
10
- import { pathsNamed, readCommands } from "./commands/read.js";
7
+ import { isAbsolutePath, joinPath, relativePath, resolvePath } from "../paths.js";
8
+ import { isRecord } from "../json-document.js";
9
+ import { MANIFEST, manifestAt, readJSON } from "../fs/manifest.js";
10
+ import { pathsNamed, readCommands } from "../commands/read.js";
11
11
  /** The three members that name an entry point beside `exports` and `bin`. */
12
12
  const LEGACY_MEMBERS = ["main", "module", "types"];
13
13
  /**