@cplieger/deadset-ts 8.0.0-dev.16 → 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 (131) 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 +58 -22
  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/tools.js +5 -9
  8. package/dist/src/component-files.js +3 -2
  9. package/dist/src/conformance-record.js +1 -1
  10. package/dist/src/consumers.js +6 -9
  11. package/dist/src/contextual-uses.js +1 -17
  12. package/dist/src/contract/config.generated.js +4 -1
  13. package/dist/src/contract/report.generated.js +6 -2
  14. package/dist/src/dependencies.js +25 -29
  15. package/dist/src/destructuring.js +1 -19
  16. package/dist/src/element-access.js +179 -164
  17. package/dist/src/entries/build-scripts.js +2 -2
  18. package/dist/src/entries/configuration-files.js +3 -7
  19. package/dist/src/entries/conventions.js +2 -2
  20. package/dist/src/entries/html-entries.js +7 -10
  21. package/dist/src/entries/mdx-entries.js +2 -2
  22. package/dist/src/entries/static-module.js +2 -1
  23. package/dist/src/facts.js +4 -4
  24. package/dist/src/findings/completion.js +40 -4
  25. package/dist/src/{implementations.js → findings/implementations.js} +3 -3
  26. package/dist/src/{in-operands.js → findings/in-operands.js} +2 -2
  27. package/dist/src/framework-lifecycle.js +41 -14
  28. package/dist/src/graph.js +12 -4
  29. package/dist/src/import-bindings.js +73 -28
  30. package/dist/src/index/sites.js +557 -0
  31. package/dist/src/indexed-access.js +523 -478
  32. package/dist/src/interface-satisfaction.js +1 -1
  33. package/dist/src/keyof-comparisons.js +124 -103
  34. package/dist/src/load/expected-module.js +2 -2
  35. package/dist/src/load/specifiers.js +2 -4
  36. package/dist/src/load/synthesize.js +5 -0
  37. package/dist/src/load/workspace-resolution.js +2 -2
  38. package/dist/src/matrix.js +6 -4
  39. package/dist/src/module-declarations.js +70 -85
  40. package/dist/src/overrides.js +134 -112
  41. package/dist/src/paths.js +46 -7
  42. package/dist/src/platform-calls.js +2 -2
  43. package/dist/src/platform-symbols.js +13 -19
  44. package/dist/src/references/read-rule.js +1 -0
  45. package/dist/src/references/read-rules.js +48 -0
  46. package/dist/src/references/sink.js +151 -0
  47. package/dist/src/references/sources.js +450 -0
  48. package/dist/src/references.js +110 -1285
  49. package/dist/src/roots/configured-declarations.js +3 -0
  50. package/dist/src/roots/root-set.js +1 -1
  51. package/dist/src/roots/unseen-loads.js +38 -10
  52. package/dist/src/serialization-contract.js +8 -3
  53. package/dist/src/stores.js +4 -2
  54. package/dist/src/sweep.js +25 -13
  55. package/dist/src/symbol-keys.js +89 -83
  56. package/dist/src/tests/file-class.js +76 -0
  57. package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
  58. package/dist/src/{test-support.js → tests/support.js} +2 -11
  59. package/dist/src/type-parameter-reads.js +178 -139
  60. package/dist/src/unimplemented-callees.js +262 -242
  61. package/dist/src/verbs/analyze.js +3 -3
  62. package/dist/src/verbs/explain.js +50 -28
  63. package/dist/src/verbs/print-retained.js +2 -2
  64. package/jsr.json +1 -1
  65. package/package.json +2 -2
  66. package/src/alias-chain.ts +0 -7
  67. package/src/amd.ts +130 -0
  68. package/src/analysis.ts +86 -23
  69. package/src/augmentations.ts +41 -27
  70. package/src/bindings.ts +460 -0
  71. package/src/calls.ts +25 -7
  72. package/src/commands/tools.ts +5 -11
  73. package/src/component-files.ts +4 -2
  74. package/src/conformance-record.ts +1 -1
  75. package/src/consumers.ts +8 -11
  76. package/src/contextual-uses.ts +2 -22
  77. package/src/contract/config.generated.ts +4 -1
  78. package/src/contract/report.generated.ts +6 -2
  79. package/src/dependencies.ts +27 -28
  80. package/src/destructuring.ts +1 -31
  81. package/src/element-access.ts +188 -182
  82. package/src/entries/build-scripts.ts +2 -2
  83. package/src/entries/configuration-files.ts +11 -8
  84. package/src/entries/conventions.ts +2 -1
  85. package/src/entries/html-entries.ts +15 -14
  86. package/src/entries/mdx-entries.ts +2 -2
  87. package/src/entries/static-module.ts +2 -1
  88. package/src/facts.ts +5 -3
  89. package/src/findings/boundary.ts +2 -0
  90. package/src/findings/completion.ts +43 -4
  91. package/src/findings/emitter.ts +1 -1
  92. package/src/{implementations.ts → findings/implementations.ts} +4 -4
  93. package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
  94. package/src/framework-lifecycle.ts +42 -14
  95. package/src/graph.ts +24 -6
  96. package/src/import-bindings.ts +98 -29
  97. package/src/index/sites.ts +781 -0
  98. package/src/indexed-access.ts +570 -527
  99. package/src/interface-satisfaction.ts +1 -1
  100. package/src/keyof-comparisons.ts +131 -116
  101. package/src/load/expected-module.ts +8 -2
  102. package/src/load/specifiers.ts +8 -5
  103. package/src/load/synthesize.ts +6 -0
  104. package/src/load/workspace-resolution.ts +9 -2
  105. package/src/matrix.ts +6 -3
  106. package/src/module-declarations.ts +74 -98
  107. package/src/overrides.ts +162 -134
  108. package/src/paths.ts +54 -8
  109. package/src/platform-calls.ts +3 -3
  110. package/src/platform-symbols.ts +11 -27
  111. package/src/references/read-rule.ts +60 -0
  112. package/src/references/read-rules.ts +55 -0
  113. package/src/references/sink.ts +213 -0
  114. package/src/references/sources.ts +632 -0
  115. package/src/references.ts +168 -1733
  116. package/src/report.ts +1 -1
  117. package/src/roots/configured-declarations.ts +3 -0
  118. package/src/roots/root-set.ts +1 -1
  119. package/src/roots/unseen-loads.ts +49 -11
  120. package/src/serialization-contract.ts +8 -3
  121. package/src/stores.ts +13 -2
  122. package/src/sweep.ts +37 -14
  123. package/src/symbol-keys.ts +102 -102
  124. package/src/tests/file-class.ts +118 -0
  125. package/src/{outside-tests.ts → tests/outside.ts} +16 -10
  126. package/src/{test-support.ts → tests/support.ts} +9 -20
  127. package/src/type-parameter-reads.ts +187 -158
  128. package/src/unimplemented-callees.ts +297 -280
  129. package/src/verbs/analyze.ts +3 -2
  130. package/src/verbs/explain.ts +74 -31
  131. package/src/verbs/print-retained.ts +2 -2
@@ -1,11 +1,12 @@
1
1
  import { EXIT_CLEAN, EXIT_USAGE } from "../contract/contract.generated.js";
2
- import { runAnalysis } from "../analysis.js";
2
+ import { productionMode, runAnalysis } from "../analysis.js";
3
3
  import { listing } from "../components.js";
4
4
  import { OUTSIDE, swept as judged } from "../graph.js";
5
5
  import { referenceKey } from "../matrix.js";
6
6
  import { partialNotes } from "../partial.js";
7
7
  import { resolvePath } from "../paths.js";
8
8
  import { positionKey } from "../position.js";
9
+ import { madeByTestCode, withheldBy } from "../tests/file-class.js";
9
10
  import { resolve } from "../resolve.js";
10
11
  import {} from "./verb.js";
11
12
  import { UsageError } from "../usage.js";
@@ -107,24 +108,32 @@ function rootsOf(configured) {
107
108
  });
108
109
  }
109
110
  /**
110
- * The shortest path from a root or a loaded consumer to one declaration, walked breadth first
111
- * from every root at once in the order the run names them, a consumer's reference being the
112
- * first hop of a path of its own, and following the references the sweep follows: a
111
+ * The shortest path from a root, a loaded consumer or a test file no project holds to one
112
+ * declaration, walked breadth first from every root at once in the order the run names them,
113
+ * a reference of a consumer or of such a file being the first hop of a path of its own, as
114
+ * the sweep seeds them, and following the references the sweep follows: a
113
115
  * reference that reads a module reaches what the module exports, and an evaluation or a root
114
116
  * reaches the module alone. Under `production` no reference a test file made is followed,
115
117
  * which is the set the report's sweep counts.
116
118
  */
117
- function shortestPath(analysis, to, production) {
119
+ function shortestPath(analysis, to, mode, support) {
118
120
  const graph = analysis.swept.matrix.union;
119
121
  const out = new Map();
120
122
  const consumed = [];
121
123
  for (const reference of referencesOf(analysis.configured)) {
122
- if (production && reference.test) {
124
+ if (withheldBy(mode, reference, support)) {
123
125
  continue;
124
126
  }
125
127
  const { consumer } = reference;
126
128
  if (consumer !== undefined) {
127
- consumed.push({ ...reference, consumer });
129
+ consumed.push({ reference, origin: { k: "consumer", consumer } });
130
+ continue;
131
+ }
132
+ if (reference.unheld === true) {
133
+ consumed.push({
134
+ reference,
135
+ origin: { k: "unheld", file: reference.from.replace(/#$/u, "") },
136
+ });
128
137
  continue;
129
138
  }
130
139
  const held = out.get(reference.from);
@@ -161,13 +170,12 @@ function shortestPath(analysis, to, production) {
161
170
  for (const root of rootsOf(analysis.configured)) {
162
171
  enter({ at: root.id, hop: undefined, previous: -1, origin: { k: "root", root } });
163
172
  }
164
- // A consumer's reference is one hop long, so it joins the walk where the roots' first
165
- // hops do.
173
+ // A consumed reference is one hop long, so it joins the walk where the roots' first hops do.
166
174
  const firstHops = steps.length;
167
175
  for (let head = 0;; head += 1) {
168
176
  if (head === firstHops) {
169
- for (const reference of consumed) {
170
- follow(reference, -1, { k: "consumer", consumer: reference.consumer });
177
+ for (const { reference, origin } of consumed) {
178
+ follow(reference, -1, origin);
171
179
  }
172
180
  }
173
181
  const step = steps[head];
@@ -187,15 +195,27 @@ function shortestPath(analysis, to, production) {
187
195
  }
188
196
  return undefined;
189
197
  }
198
+ /** Where a path starts, as a line names it. */
199
+ function originText(origin) {
200
+ switch (origin.k) {
201
+ case "root":
202
+ return `root ${origin.root.kind} ${origin.root.id}`;
203
+ case "consumer":
204
+ return `consumer ${origin.consumer}`;
205
+ case "unheld":
206
+ return `test file no project holds ${origin.file}`;
207
+ }
208
+ }
190
209
  /** What made a reference: a declaration of the target, or a loaded consumer. */
191
210
  function madeBy(reference) {
192
211
  return reference.consumer === undefined ? reference.from : `consumer ${reference.consumer}`;
193
212
  }
194
- function referenceLine(reference, tail) {
195
- return ` reference: ${positionKey(reference.position)}\t${reference.use}\t${tail}${reference.test ? "\tfrom a test file" : ""}\n`;
213
+ /** One reference of an answer, marked where test code made it, a test-support file's included. */
214
+ function referenceLine(reference, tail, support) {
215
+ return ` reference: ${positionKey(reference.position)}\t${reference.use}\t${tail}${madeByTestCode(reference, support) ? "\tfrom a test file" : ""}\n`;
196
216
  }
197
217
  /** Why a declaration no finding names and no exemption held back is live, or how it fell. */
198
- function liveness(analysis, subject, cascade) {
218
+ function liveness(analysis, subject, cascade, mode) {
199
219
  const { sweep } = analysis.swept;
200
220
  const candidate = sweep.candidates.find((one) => one.id === subject.id);
201
221
  if (candidate === undefined && !judged(subject.kind)) {
@@ -206,12 +226,14 @@ function liveness(analysis, subject, cascade) {
206
226
  for (const root of rootsOf(analysis.configured).filter((one) => one.id === subject.id)) {
207
227
  text += ` root: ${root.kind}${root.source === "" ? "" : `\tnamed by ${root.source}`}\n`;
208
228
  }
209
- const found = shortestPath(analysis, subject.id, true) ?? shortestPath(analysis, subject.id, false);
229
+ const support = new Set(analysis.configured.flatMap((one) => one.supportFiles ?? []));
230
+ const found = shortestPath(analysis, subject.id, mode, support) ??
231
+ shortestPath(analysis, subject.id, { production: false }, support);
210
232
  if (found !== undefined && found.path.length > 0) {
211
233
  const { origin } = found;
212
- text += ` reached from: ${origin.k === "root" ? `root ${origin.root.kind} ${origin.root.id}` : `consumer ${origin.consumer}`}\n`;
234
+ text += ` reached from: ${originText(origin)}\n`;
213
235
  for (const hop of found.path) {
214
- text += referenceLine(hop.reference, `${madeBy(hop.reference)} -> ${hop.reference.to}`);
236
+ text += referenceLine(hop.reference, `${madeBy(hop.reference)} -> ${hop.reference.to}`, support);
215
237
  if (hop.k === "export") {
216
238
  text += ` export: ${hop.reference.to} -> ${hop.to}\n`;
217
239
  }
@@ -219,10 +241,11 @@ function liveness(analysis, subject, cascade) {
219
241
  }
220
242
  else if (found === undefined) {
221
243
  const references = referencesOf(analysis.configured).filter((one) => one.to === subject.id);
222
- text += " unreachable: no path of references reaches it from a root or a loaded consumer\n";
244
+ text +=
245
+ " unreachable: no path of references reaches it from a root, a loaded consumer or a test file no project holds\n";
223
246
  text += ` references: ${String(references.length)}\n`;
224
247
  for (const reference of references) {
225
- text += referenceLine(reference, `from ${madeBy(reference)}`);
248
+ text += referenceLine(reference, `from ${madeBy(reference)}`, support);
226
249
  }
227
250
  }
228
251
  if (candidate !== undefined) {
@@ -271,7 +294,7 @@ function namedElsewhere(findings, subject) {
271
294
  .join("");
272
295
  }
273
296
  /** The one explanation that applies to a declaration, built whole before it is written. */
274
- function explanation(analysis, subject, cascade) {
297
+ function explanation(analysis, subject, cascade, mode) {
275
298
  const graph = analysis.swept.matrix.union;
276
299
  const at = graph.at(subject.id);
277
300
  let text = `symbol: ${subject.ref}\n`;
@@ -294,7 +317,7 @@ function explanation(analysis, subject, cascade) {
294
317
  "answer: held\n held by: a question the checker did not answer, which could have kept it live, so no finding names it\n";
295
318
  }
296
319
  else {
297
- text += liveness(analysis, subject, cascade);
320
+ text += liveness(analysis, subject, cascade, mode);
298
321
  }
299
322
  return text + namedElsewhere(analysis.result.findings, subject);
300
323
  }
@@ -302,17 +325,16 @@ function explanation(analysis, subject, cascade) {
302
325
  * Explains one symbol from the analysis the report is built from: why a reported symbol is
303
326
  * reported, which exemption classes held a retained one back, that an unanswered question held
304
327
  * a held one, and for any other declaration the relations that hold it live and the shortest
305
- * path from a root or a loaded consumer, or the references it has where no path reaches it. A
306
- * request naming no one declaration exits with the usage code and names what partially
307
- * matches it. Where the analysis was partial, the error stream says so.
328
+ * path from a root, a loaded consumer or a test file no project holds, or the references it
329
+ * has where no path reaches it. A request naming no one declaration exits with the usage
330
+ * code and names what partially matches it. Where the analysis was partial, the error stream says so.
308
331
  */
309
332
  export const explainVerb = ({ out, err, host, inputs, scope, openClient, option }) => {
310
333
  const named = namedSymbol(option);
311
334
  const { config, provenance } = resolve(inputs());
312
335
  const scoped = scope();
313
- const analysis = runAnalysis(openClient(config), host, scoped, config, provenance, {
314
- production: true,
315
- });
336
+ const mode = productionMode(config);
337
+ const analysis = runAnalysis(openClient(config), host, scoped, config, provenance, mode);
316
338
  const { subject, candidates } = subjectOf(analysis.swept.matrix.union.symbols, named);
317
339
  const notes = partialNotes({
318
340
  notBuilt: analysis.run.notBuilt,
@@ -329,7 +351,7 @@ export const explainVerb = ({ out, err, host, inputs, scope, openClient, option
329
351
  }
330
352
  }
331
353
  else {
332
- out.write(explanation(analysis, subject, config.reporters.cascade));
354
+ out.write(explanation(analysis, subject, config.reporters.cascade, mode));
333
355
  }
334
356
  for (const note of notes) {
335
357
  diagnose(err, note);
@@ -1,5 +1,5 @@
1
1
  import { EXIT_CLEAN } from "../contract/contract.generated.js";
2
- import { runSweep } from "../analysis.js";
2
+ import { productionMode, runSweep } from "../analysis.js";
3
3
  import { retainedLines } from "../exempt.js";
4
4
  import { partialNotes } from "../partial.js";
5
5
  import { resolvePath } from "../paths.js";
@@ -18,7 +18,7 @@ export const printRetainedVerb = ({ out, err, host, inputs, scope, openClient })
18
18
  const scoped = scope();
19
19
  const swept = runSweep(openClient(config), host, scoped, config, {
20
20
  marked: [],
21
- mode: { production: true },
21
+ mode: productionMode(config),
22
22
  });
23
23
  for (const line of retainedLines(swept.matrix.union.symbols, swept.retained)) {
24
24
  out.write(`${line}\n`);
package/jsr.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/deadset-ts",
3
- "version": "8.0.0-dev.16",
3
+ "version": "8.0.0-dev.17",
4
4
  "license": "GPL-3.0-or-later",
5
5
  "exports": {
6
6
  ".": "./src/index.ts"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/deadset-ts",
3
- "version": "8.0.0-dev.16",
3
+ "version": "8.0.0-dev.17",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Deterministic dead-code analysis for TypeScript, class and type members included",
@@ -48,7 +48,7 @@
48
48
  "prettier": "3.9.10",
49
49
  "typescript": "npm:@typescript/typescript6@6.0.2",
50
50
  "typescript-eslint": "8.72.1",
51
- "vitest": "5.0.5"
51
+ "vitest": "5.1.0"
52
52
  },
53
53
  "publishConfig": {
54
54
  "access": "public"
@@ -49,8 +49,6 @@ export interface AliasChains {
49
49
  * link is declared.
50
50
  */
51
51
  nextDeclared(symbol: TSSymbol): readonly ChainTarget[];
52
- /** How many steps the compiler was asked for: one per alias, whatever the chains it heads. */
53
- readonly steps: number;
54
52
  }
55
53
 
56
54
  /**
@@ -65,7 +63,6 @@ export function aliasChains<Brand>(project: ProjectView<Brand>, held: Inventory)
65
63
  const ownFiles = project.ownPaths();
66
64
  const steps = new Map<number, Answer<TSSymbol | undefined>>();
67
65
  const chains = new Map<number, readonly ChainTarget[]>();
68
- let asked = 0;
69
66
 
70
67
  const declarationsOf = (symbol: TSSymbol): string[] => {
71
68
  const ids: string[] = [];
@@ -102,7 +99,6 @@ export function aliasChains<Brand>(project: ProjectView<Brand>, held: Inventory)
102
99
  return steps.get(symbol.id);
103
100
  }
104
101
  const next = project.aliasStepOf(symbol);
105
- asked += 1;
106
102
  steps.set(symbol.id, next);
107
103
  return next;
108
104
  };
@@ -197,8 +193,5 @@ export function aliasChains<Brand>(project: ProjectView<Brand>, held: Inventory)
197
193
  declarationsOf,
198
194
  chainOf,
199
195
  nextDeclared,
200
- get steps() {
201
- return asked;
202
- },
203
196
  };
204
197
  }
package/src/amd.ts ADDED
@@ -0,0 +1,130 @@
1
+ /**
2
+ * AMD's module forms (references-ts.37), read from the syntax because the binder declares
3
+ * none of them: `define([<ids>], factory)`, `define("<name>", [<ids>], factory)` and
4
+ * `require([<ids>], callback)` (https://github.com/amdjs/amdjs-api/blob/master/AMD.md).
5
+ */
6
+
7
+ import {
8
+ isArrayLiteralExpression,
9
+ isArrowFunction,
10
+ isFunctionExpression,
11
+ isCallExpression,
12
+ isIdentifier,
13
+ isNoSubstitutionTemplateLiteral,
14
+ isStringLiteral,
15
+ type Identifier,
16
+ type Node,
17
+ type StringLiteralLikeNode,
18
+ } from "@typescript/native/ast";
19
+ import { hasURLScheme, isAbsolutePath, resolvePath, startsRelativePath } from "./paths.ts";
20
+ import { parenthesesRemoved } from "./unwrap.ts";
21
+
22
+ /** The platform function an AMD call is written as. */
23
+ type AmdPlatform = "define" | "require";
24
+
25
+ /** One AMD call's dependency list. */
26
+ export interface AmdLoad {
27
+ /** The name the call is written with; {@link callsPlatform} owns whether it is AMD's. */
28
+ readonly callee: Identifier;
29
+ /** Each dependency id written as literal text, AMD's own `require`, `exports` and `module` left out. */
30
+ readonly ids: readonly AmdDependency[];
31
+ /** Whether the list, or an element of it, is computed, so the call may load any module. */
32
+ readonly computed: boolean;
33
+ }
34
+
35
+ /** One dependency id of an AMD call. */
36
+ interface AmdDependency {
37
+ readonly id: StringLiteralLikeNode;
38
+ /**
39
+ * Whether the factory may receive the module's value: it declares a parameter at the id's
40
+ * place, a rest parameter included, or it is written as anything but a function.
41
+ */
42
+ readonly received: boolean;
43
+ }
44
+
45
+ /** The ids AMD binds to the factory's CommonJS parameters, which load no module. */
46
+ const FREE_VARIABLES: ReadonlySet<string> = new Set(["require", "exports", "module"]);
47
+
48
+ /** The platform function `callee` is written as. */
49
+ export function amdPlatform(callee: Identifier): AmdPlatform {
50
+ return callee.text === "define" ? "define" : "require";
51
+ }
52
+
53
+ /**
54
+ * The dependency list one call writes, where it is a call of a bare `define` or `require` in
55
+ * one of AMD's forms. A `define` with no list, of a factory or a value alone, loads nothing;
56
+ * a `require` whose first argument is no array literal is CommonJS's.
57
+ */
58
+ export function amdLoadOf(node: Node): AmdLoad | undefined {
59
+ if (!isCallExpression(node)) {
60
+ return undefined;
61
+ }
62
+ const callee = parenthesesRemoved(node.expression);
63
+ if (!isIdentifier(callee) || (callee.text !== "define" && callee.text !== "require")) {
64
+ return undefined;
65
+ }
66
+ const written = node.arguments.map((one) => parenthesesRemoved(one));
67
+ const [first] = written;
68
+ if (first === undefined) {
69
+ return undefined;
70
+ }
71
+ const define = callee.text === "define";
72
+ const named = define && isLiteralText(first);
73
+ const list = written[named ? 1 : 0];
74
+ if (list !== undefined && isArrayLiteralExpression(list)) {
75
+ const elements = list.elements.map((one) => parenthesesRemoved(one));
76
+ const factory = written[named ? 2 : 1];
77
+ const ids = elements.flatMap((one, at) =>
78
+ isLiteralText(one) && !FREE_VARIABLES.has(one.text)
79
+ ? [{ id: one, received: receives(factory, at) }]
80
+ : [],
81
+ );
82
+ return { callee, ids, computed: elements.some((one) => !isLiteralText(one)) };
83
+ }
84
+ if (!define) {
85
+ return undefined;
86
+ }
87
+ // A factory after the list's place makes what stands there a list, here a computed one.
88
+ return { callee, ids: [], computed: written.length > (named ? 2 : 1) };
89
+ }
90
+
91
+ function isLiteralText(node: Node): node is StringLiteralLikeNode {
92
+ return isStringLiteral(node) || isNoSubstitutionTemplateLiteral(node);
93
+ }
94
+
95
+ /** Whether a factory may receive the dependency at place `at` of its list. */
96
+ function receives(factory: Node | undefined, at: number): boolean {
97
+ if (factory === undefined) {
98
+ return false;
99
+ }
100
+ if (!isFunctionExpression(factory) && !isArrowFunction(factory)) {
101
+ return true;
102
+ }
103
+ const { parameters } = factory;
104
+ return at < parameters.length || parameters.some((one) => one.dotDotDotToken !== undefined);
105
+ }
106
+
107
+ /**
108
+ * The path a dependency id names against the calling file's directory: a relative module id,
109
+ * `./` or `../` with no `.js` ending, with `.js` appended. RequireJS reads any other id
110
+ * through its configuration or as a URL against the page, which the analysis does not know.
111
+ */
112
+ export function amdPath(dir: string, id: string): string | undefined {
113
+ return startsRelativePath(id) && !id.endsWith(".js") ? resolvePath(dir, `${id}.js`) : undefined;
114
+ }
115
+
116
+ /**
117
+ * Whether one call may load a file the analysis cannot name: a computed list, or an id that
118
+ * RequireJS reads as a URL, one ending in `.js`, starting with `/` or carrying a scheme. Any
119
+ * other id {@link amdPath} reads no path from is a package's.
120
+ */
121
+ export function amdUnresolved(load: AmdLoad): boolean {
122
+ return (
123
+ load.computed ||
124
+ load.ids.some(
125
+ ({ id }) =>
126
+ amdPath("/", id.text) === undefined &&
127
+ (id.text.endsWith(".js") || isAbsolutePath(id.text) || hasURLScheme(id.text)),
128
+ )
129
+ );
130
+ }