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

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 (161) 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 +82 -91
  4. package/dist/src/augmentations.js +24 -25
  5. package/dist/src/bindings.js +382 -0
  6. package/dist/src/calls.js +279 -43
  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/contract.generated.js +2 -2
  14. package/dist/src/contract/report.generated.js +6 -2
  15. package/dist/src/dependencies.js +25 -29
  16. package/dist/src/destructuring.js +1 -19
  17. package/dist/src/element-access.js +179 -164
  18. package/dist/src/entries/build-scripts.js +2 -2
  19. package/dist/src/entries/configuration-files.js +3 -7
  20. package/dist/src/entries/conventions.js +2 -2
  21. package/dist/src/entries/html-entries.js +7 -10
  22. package/dist/src/entries/mdx-entries.js +2 -2
  23. package/dist/src/entries/static-module.js +2 -1
  24. package/dist/src/exempt.js +0 -26
  25. package/dist/src/exposure.js +400 -0
  26. package/dist/src/facts.js +4 -4
  27. package/dist/src/findings/completion.js +44 -21
  28. package/dist/src/findings/emitters.js +22 -33
  29. package/dist/src/{implementations.js → findings/implementations.js} +3 -3
  30. package/dist/src/{in-operands.js → findings/in-operands.js} +2 -2
  31. package/dist/src/findings/intra-function.js +3 -3
  32. package/dist/src/findings/reads-and-writes.js +71 -15
  33. package/dist/src/findings/unused-declarations.js +2 -22
  34. package/dist/src/findings/visibility-narrowing.js +14 -20
  35. package/dist/src/framework-lifecycle.js +41 -14
  36. package/dist/src/import-bindings.js +73 -28
  37. package/dist/src/index/sites.js +557 -0
  38. package/dist/src/indexed-access.js +523 -478
  39. package/dist/src/interface-satisfaction.js +1 -1
  40. package/dist/src/inventory.js +18 -0
  41. package/dist/src/keyof-comparisons.js +124 -103
  42. package/dist/src/{components.js → liveness/components.js} +1 -1
  43. package/dist/src/{graph.js → liveness/graph.js} +45 -20
  44. package/dist/src/liveness/matrix.js +99 -0
  45. package/dist/src/liveness/sweep.js +558 -0
  46. package/dist/src/load/expected-module.js +2 -2
  47. package/dist/src/load/specifiers.js +2 -4
  48. package/dist/src/load/synthesize.js +5 -0
  49. package/dist/src/load/workspace-resolution.js +2 -2
  50. package/dist/src/module-declarations.js +70 -85
  51. package/dist/src/overrides.js +134 -112
  52. package/dist/src/paths.js +46 -7
  53. package/dist/src/platform-calls.js +2 -2
  54. package/dist/src/platform-symbols.js +13 -19
  55. package/dist/src/query.js +46 -0
  56. package/dist/src/references/read-rule.js +1 -0
  57. package/dist/src/references/read-rules.js +48 -0
  58. package/dist/src/references/sink.js +237 -0
  59. package/dist/src/references/sources.js +450 -0
  60. package/dist/src/references.js +110 -1285
  61. package/dist/src/roots/configured-declarations.js +3 -0
  62. package/dist/src/roots/published-reach.js +4 -4
  63. package/dist/src/roots/root-set.js +1 -1
  64. package/dist/src/roots/unseen-loads.js +38 -10
  65. package/dist/src/serialization-contract.js +8 -3
  66. package/dist/src/stores.js +4 -2
  67. package/dist/src/symbol-keys.js +89 -83
  68. package/dist/src/tests/file-class.js +76 -0
  69. package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
  70. package/dist/src/{test-support.js → tests/support.js} +2 -11
  71. package/dist/src/type-parameter-reads.js +178 -139
  72. package/dist/src/unimplemented-callees.js +262 -242
  73. package/dist/src/verbs/analyze.js +3 -3
  74. package/dist/src/verbs/explain.js +75 -132
  75. package/dist/src/verbs/print-retained.js +3 -3
  76. package/jsr.json +1 -1
  77. package/package.json +2 -2
  78. package/src/alias-chain.ts +0 -7
  79. package/src/amd.ts +130 -0
  80. package/src/analysis.ts +125 -128
  81. package/src/augmentations.ts +42 -28
  82. package/src/bindings.ts +460 -0
  83. package/src/calls.ts +338 -46
  84. package/src/commands/tools.ts +5 -11
  85. package/src/component-files.ts +4 -2
  86. package/src/conformance-record.ts +1 -1
  87. package/src/consumers.ts +8 -11
  88. package/src/contextual-uses.ts +2 -22
  89. package/src/contract/config.generated.ts +4 -1
  90. package/src/contract/contract.generated.ts +2 -2
  91. package/src/contract/report.generated.ts +6 -2
  92. package/src/dependencies.ts +27 -28
  93. package/src/destructuring.ts +1 -31
  94. package/src/element-access.ts +188 -182
  95. package/src/entries/build-scripts.ts +2 -2
  96. package/src/entries/configuration-files.ts +11 -8
  97. package/src/entries/conventions.ts +2 -1
  98. package/src/entries/html-entries.ts +15 -14
  99. package/src/entries/mdx-entries.ts +2 -2
  100. package/src/entries/static-module.ts +2 -1
  101. package/src/exempt.ts +31 -46
  102. package/src/exposure.ts +492 -0
  103. package/src/facts.ts +5 -3
  104. package/src/finding.ts +1 -1
  105. package/src/findings/boundary.ts +9 -0
  106. package/src/findings/completion.ts +48 -23
  107. package/src/findings/emitter.ts +1 -1
  108. package/src/findings/emitters.ts +23 -41
  109. package/src/{implementations.ts → findings/implementations.ts} +4 -4
  110. package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
  111. package/src/findings/interfaces.ts +3 -3
  112. package/src/findings/intra-function.ts +3 -3
  113. package/src/findings/reads-and-writes.ts +99 -26
  114. package/src/findings/unused-declarations.ts +4 -28
  115. package/src/findings/visibility-narrowing.ts +25 -26
  116. package/src/framework-lifecycle.ts +42 -14
  117. package/src/import-bindings.ts +98 -29
  118. package/src/index/sites.ts +781 -0
  119. package/src/indexed-access.ts +570 -527
  120. package/src/interface-satisfaction.ts +2 -2
  121. package/src/inventory.ts +19 -0
  122. package/src/keyof-comparisons.ts +131 -116
  123. package/src/{components.ts → liveness/components.ts} +3 -3
  124. package/src/{graph.ts → liveness/graph.ts} +93 -27
  125. package/src/liveness/matrix.ts +162 -0
  126. package/src/liveness/sweep.ts +855 -0
  127. package/src/load/expected-module.ts +8 -2
  128. package/src/load/specifiers.ts +8 -5
  129. package/src/load/synthesize.ts +6 -0
  130. package/src/load/workspace-resolution.ts +9 -2
  131. package/src/module-declarations.ts +74 -98
  132. package/src/overrides.ts +162 -134
  133. package/src/paths.ts +54 -8
  134. package/src/platform-calls.ts +3 -3
  135. package/src/platform-symbols.ts +11 -27
  136. package/src/query.ts +81 -0
  137. package/src/references/read-rule.ts +60 -0
  138. package/src/references/read-rules.ts +55 -0
  139. package/src/references/sink.ts +314 -0
  140. package/src/references/sources.ts +632 -0
  141. package/src/references.ts +175 -1733
  142. package/src/report.ts +1 -1
  143. package/src/roots/configured-declarations.ts +3 -0
  144. package/src/roots/published-reach.ts +9 -4
  145. package/src/roots/root-set.ts +1 -1
  146. package/src/roots/unseen-loads.ts +49 -11
  147. package/src/serialization-contract.ts +8 -3
  148. package/src/stores.ts +15 -3
  149. package/src/symbol-keys.ts +102 -102
  150. package/src/tests/file-class.ts +135 -0
  151. package/src/{outside-tests.ts → tests/outside.ts} +16 -10
  152. package/src/{test-support.ts → tests/support.ts} +9 -20
  153. package/src/type-parameter-reads.ts +187 -158
  154. package/src/unimplemented-callees.ts +297 -280
  155. package/src/verbs/analyze.ts +3 -2
  156. package/src/verbs/explain.ts +83 -158
  157. package/src/verbs/print-retained.ts +3 -3
  158. package/dist/src/matrix.js +0 -273
  159. package/dist/src/sweep.js +0 -222
  160. package/src/matrix.ts +0 -463
  161. package/src/sweep.ts +0 -361
@@ -5,11 +5,12 @@
5
5
  * to the same module. One that names it by name or pattern also reads the members it
6
6
  * imports, whatever file the specifier resolves to, as a compiler reading no such file does.
7
7
  */
8
- import { isCallExpression, isExportDeclaration, isExternalModuleReference, isImportDeclaration, isImportEqualsDeclaration, isImportTypeNode, isLiteralTypeNode, isModuleDeclaration, isNamespaceExport, isNamespaceImport, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/ast";
8
+ import { isCallExpression, isExportDeclaration, isExternalModuleReference, isImportDeclaration, isImportEqualsDeclaration, isImportTypeNode, isLiteralTypeNode, isModuleDeclaration, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/ast";
9
+ import { namesImported } from "./import-bindings.js";
9
10
  import { nodeKey } from "./inventory.js";
10
- import { renderPosition } from "./position.js";
11
11
  import { UNANSWERED } from "./query.js";
12
12
  import { aliasFragment, nameComponent } from "./ref.js";
13
+ import { isPathSpecifier } from "./paths.js";
13
14
  /** Whether a node is a specifier's text: a string literal or a template with no substitution. */
14
15
  function literalText(node) {
15
16
  return node !== undefined && (isStringLiteral(node) || isNoSubstitutionTemplateLiteral(node));
@@ -36,32 +37,12 @@ function specifierOf(node) {
36
37
  }
37
38
  return undefined;
38
39
  }
39
- /**
40
- * The export names an import or a re-export reads from the module it names: none for a
41
- * namespace, a star or a bare form.
42
- */
43
- export function namesImported(node) {
44
- if (isImportDeclaration(node)) {
45
- const clause = node.importClause;
46
- const bindings = clause?.namedBindings;
47
- return [
48
- ...(clause?.name === undefined ? [] : ["default"]),
49
- ...(bindings === undefined || isNamespaceImport(bindings)
50
- ? []
51
- : bindings.elements.map((one) => (one.propertyName ?? one.name).text)),
52
- ];
53
- }
54
- const clause = isExportDeclaration(node) ? node.exportClause : undefined;
55
- return clause === undefined || isNamespaceExport(clause)
56
- ? []
57
- : clause.elements.map((one) => (one.propertyName ?? one.name).text);
58
- }
59
40
  /**
60
41
  * Whether a specifier matches a declaration's name, the name's one `*` standing for any run.
61
42
  * A relative name is resolved against its own file, so no specifier names it by its text.
62
43
  */
63
44
  function namesModule(specifier, name) {
64
- if (/^(?:\.{1,2}(?:\/|$)|\/)/u.test(name)) {
45
+ if (isPathSpecifier(name)) {
65
46
  return false;
66
47
  }
67
48
  const star = name.indexOf("*");
@@ -107,67 +88,71 @@ function namedBy(held) {
107
88
  });
108
89
  }
109
90
  /**
110
- * The references the imports of one project's own files make to the module declarations
111
- * they name, each at the import's specifier.
91
+ * The reads the imports of a side's files make of the module declarations of the target they
92
+ * name, each at the import's specifier. A specifier the checker leaves unanswered still names
93
+ * a declaration by its name or pattern.
112
94
  */
113
- export function moduleDeclarationReferences(project, held, root, testFiles) {
114
- const imports = [];
115
- const declared = [];
116
- for (const file of project.ownSourceFiles()) {
117
- const fileId = held.declarations.get(nodeKey(file, file));
118
- if (fileId === undefined) {
119
- continue;
120
- }
121
- const visit = (node, from) => {
122
- const own = held.declarations.get(nodeKey(file, node));
123
- if (isModuleDeclaration(node) && literalText(node.name) && own !== undefined) {
124
- declared.push({ id: own, name: node.name });
125
- }
126
- const specifier = specifierOf(node);
127
- if (specifier !== undefined) {
128
- imports.push({ file, from, specifier, names: namesImported(node) });
129
- }
130
- node.forEachChild((child) => {
131
- visit(child, own ?? from);
132
- });
133
- };
134
- file.forEachChild((child) => {
135
- visit(child, fileId);
136
- });
137
- }
138
- if (declared.length === 0 || imports.length === 0) {
139
- return [];
140
- }
141
- const asked = [...declared.map((one) => one.name), ...imports.map((one) => one.specifier)];
142
- const answers = project.symbolsAt(asked.map((node) => project.handle(node)));
143
- const moduleOf = (at) => {
144
- const symbol = answers[at];
145
- return symbol === undefined || symbol === UNANSWERED ? undefined : symbol.id;
146
- };
147
- const named = namedBy(held);
148
- const found = [];
149
- imports.forEach((one, index) => {
150
- const resolved = moduleOf(declared.length + index);
151
- const position = renderPosition(one.file, root, one.specifier.getStart());
152
- const read = (to, resolution, test) => {
153
- found.push({ from: one.from, to, position, use: "read", resolution, test });
154
- };
155
- const test = testFiles.has(position.path);
156
- const same = new Set(declared
157
- .filter((_, at) => resolved !== undefined && resolved === moduleOf(at))
158
- .map((declaration) => declaration.id));
159
- for (const id of same) {
160
- read(id, "batch", test);
161
- }
162
- for (const declaration of named(one.specifier.text, one.names)) {
163
- // A wildcard declaration is how the compiler types the file a matching specifier
164
- // names, so the import uses it whoever writes it, a test included.
165
- if (declaration.wildcard || !same.has(declaration.id)) {
166
- for (const to of [declaration.id, ...declaration.members]) {
167
- read(to, "syntax", test && !declaration.wildcard);
95
+ export const moduleDeclarations = {
96
+ start() {
97
+ const imports = [];
98
+ return {
99
+ visit(node, { file, from }) {
100
+ const specifier = specifierOf(node);
101
+ if (specifier !== undefined) {
102
+ imports.push({ file, from, specifier, names: namesImported(node) });
168
103
  }
169
- }
170
- }
171
- });
172
- return found;
173
- }
104
+ },
105
+ reads({ project, held }) {
106
+ const found = [];
107
+ // A module declaration whose name is a string is written at a file's top level.
108
+ const declared = project.ownSourceFiles().flatMap((file) => file.statements.flatMap((statement) => {
109
+ const id = held.declarations.get(nodeKey(file, statement));
110
+ return isModuleDeclaration(statement) && literalText(statement.name) && id !== undefined
111
+ ? [{ id, name: statement.name }]
112
+ : [];
113
+ }));
114
+ if (declared.length === 0 || imports.length === 0) {
115
+ return found;
116
+ }
117
+ const asked = [...declared.map((one) => one.name), ...imports.map((one) => one.specifier)];
118
+ const answers = project.symbolsAt(asked.map((node) => project.handle(node)));
119
+ const moduleOf = (at) => {
120
+ const symbol = answers[at];
121
+ return symbol === undefined || symbol === UNANSWERED ? undefined : symbol.id;
122
+ };
123
+ const named = namedBy(held);
124
+ imports.forEach((one, index) => {
125
+ const resolved = moduleOf(declared.length + index);
126
+ const read = (to, via, production) => {
127
+ found.push({
128
+ kind: "named",
129
+ from: one.from,
130
+ to,
131
+ file: one.file,
132
+ at: one.specifier,
133
+ use: "read",
134
+ via,
135
+ ...(production ? { production: true } : {}),
136
+ });
137
+ };
138
+ const same = new Set(declared
139
+ .filter((_, at) => resolved !== undefined && resolved === moduleOf(at))
140
+ .map((declaration) => declaration.id));
141
+ for (const id of same) {
142
+ read(id, "batch", false);
143
+ }
144
+ for (const declaration of named(one.specifier.text, one.names)) {
145
+ // A wildcard declaration is how the compiler types the file a matching specifier
146
+ // names, so the import uses it whoever writes it, a test included.
147
+ if (declaration.wildcard || !same.has(declaration.id)) {
148
+ for (const to of [declaration.id, ...declaration.members]) {
149
+ read(to, "syntax", declaration.wildcard);
150
+ }
151
+ }
152
+ }
153
+ });
154
+ return found;
155
+ },
156
+ };
157
+ },
158
+ };
@@ -24,121 +24,136 @@ function nameOf(entry) {
24
24
  }
25
25
  return isQualifiedName(entry.typeName) ? entry.typeName.right : entry.typeName;
26
26
  }
27
- /** Each class of the target whose `extends` clause names a class, with the name node. */
28
- function extending(project, held) {
29
- const found = [];
30
- for (const file of project.ownSourceFiles()) {
31
- const visit = (node) => {
32
- if (isClassDeclaration(node) || isClassExpression(node)) {
33
- const id = held.declarations.get(nodeKey(file, node));
34
- const clause = node.heritageClauses?.find((one) => one.token === SyntaxKind.ExtendsKeyword);
35
- const base = clause?.types[0];
36
- if (id !== undefined && base !== undefined) {
37
- found.push({ id, base: nameOf(base) });
38
- }
39
- }
40
- node.forEachChild(visit);
41
- };
42
- file.forEachChild(visit);
43
- }
44
- return found;
45
- }
46
27
  /**
47
- * The references each base class member makes to its overrides, for the classes of one
48
- * project that extend another class of the target, and each class makes to its members
49
- * that override a member of a class declared outside the target. A member a nearer base
50
- * already overrides is reached through that override. A private member is not
51
- * overridden. Each reference is placed at the override and made by its file.
28
+ * The reads each base class member makes of its overrides, for the classes of the target that
29
+ * extend another class of the target, and each class makes of its members that override a
30
+ * member of a class declared outside the target. A member a nearer base already overrides is
31
+ * reached through that override. A private member is not overridden. Each read is placed at
32
+ * the override. A class whose base the checker leaves unanswered holds every member it could
33
+ * override.
52
34
  */
53
- export function overrideReferences(project, held, chains, testFiles) {
54
- const classes = extending(project, held);
55
- if (classes.length === 0) {
56
- return [];
57
- }
58
- const byId = new Map(held.symbols.map((symbol) => [symbol.id, symbol]));
59
- const members = new Map();
60
- for (const symbol of held.symbols) {
61
- const component = memberComponent(symbol, byId);
62
- if (component === undefined ||
63
- !OVERRIDABLE.has(symbol.kind) ||
64
- symbol.visibility === "private" ||
65
- symbol.visibility === "private-name") {
66
- continue;
67
- }
68
- const own = members.get(symbol.parent) ?? new Map();
69
- own.set(component, symbol);
70
- members.set(symbol.parent, own);
71
- }
72
- const bases = new Map();
73
- const outsideBases = new Map();
74
- const own = project.ownPaths();
75
- const symbols = project.symbolsAt(classes.map((one) => project.handle(one.base)));
76
- classes.forEach(({ id }, index) => {
77
- const symbol = symbols[index];
78
- if (symbol !== undefined && symbol !== UNANSWERED) {
79
- const inTarget = chains
80
- .chainOf(symbol)
81
- .map((target) => target.id)
82
- .filter((target) => byId.get(target)?.kind === "class" && target !== id);
83
- bases.set(id, inTarget);
84
- const declared = (symbol.flags & SymbolFlags.Alias) === 0 ? symbol : project.queries.aliased(symbol);
85
- if (inTarget.length === 0 &&
86
- declared !== UNANSWERED &&
87
- (((declared.flags & SymbolFlags.Class) !== 0 &&
88
- // The target's augmentation of an outside class merges an interface into it,
89
- // so only a class declaration says where the class is.
90
- declared.declarations.some((handle) => CLASS_DECLARATIONS.has(handle.kind) && !own.has(handle.path))) ||
91
- ((declared.flags & SymbolFlags.Interface) !== 0 &&
92
- declared.declarations.some((handle) => handle.kind === SyntaxKind.VariableDeclaration && !own.has(handle.path))))) {
93
- outsideBases.set(id, declared);
94
- }
95
- }
96
- });
97
- /** The nearest member of one component the class or a class it extends declares. */
98
- const declaredAbove = (classId, component, seen) => {
99
- if (seen.has(classId)) {
100
- return [];
101
- }
102
- seen.add(classId);
103
- const own = members.get(classId)?.get(component);
104
- if (own !== undefined) {
105
- return [own.id];
106
- }
107
- return (bases.get(classId) ?? []).flatMap((base) => declaredAbove(base, component, seen));
108
- };
109
- const found = [];
110
- const outsideNames = outsideMemberNames(project, bases, outsideBases);
111
- for (const [classId, names] of outsideNames) {
112
- for (const [component, member] of members.get(classId) ?? []) {
113
- if (names.has(component)) {
114
- found.push({
115
- from: classId,
116
- to: member.id,
117
- position: member.position,
118
- use: "read",
119
- resolution: "override",
120
- test: testFiles.has(member.position.path),
35
+ export const overrides = {
36
+ start() {
37
+ const extending = [];
38
+ return {
39
+ visit(node, { file }) {
40
+ if (isClassDeclaration(node) || isClassExpression(node)) {
41
+ const clause = node.heritageClauses?.find((one) => one.token === SyntaxKind.ExtendsKeyword);
42
+ const base = clause?.types[0];
43
+ if (base !== undefined) {
44
+ extending.push({ node, file, base: nameOf(base) });
45
+ }
46
+ }
47
+ },
48
+ reads({ project, held, chains }) {
49
+ const found = [];
50
+ const classes = extending.flatMap((one) => {
51
+ const id = held.declarations.get(nodeKey(one.file, one.node));
52
+ return id === undefined ? [] : [{ ...one, id }];
121
53
  });
122
- }
123
- }
124
- }
125
- for (const [classId, baseIds] of bases) {
126
- for (const [component, member] of members.get(classId) ?? []) {
127
- const seen = new Set([classId]);
128
- for (const base of baseIds.flatMap((one) => declaredAbove(one, component, seen))) {
129
- found.push({
130
- from: base,
131
- to: member.id,
132
- position: member.position,
133
- use: "read",
134
- resolution: "override",
135
- test: testFiles.has(member.position.path),
54
+ if (classes.length === 0) {
55
+ return found;
56
+ }
57
+ const byId = new Map(held.symbols.map((symbol) => [symbol.id, symbol]));
58
+ const members = new Map();
59
+ for (const symbol of held.symbols) {
60
+ const component = memberComponent(symbol, byId);
61
+ if (component === undefined ||
62
+ !OVERRIDABLE.has(symbol.kind) ||
63
+ symbol.visibility === "private" ||
64
+ symbol.visibility === "private-name") {
65
+ continue;
66
+ }
67
+ const own = members.get(symbol.parent) ?? new Map();
68
+ own.set(component, symbol);
69
+ members.set(symbol.parent, own);
70
+ }
71
+ const bases = new Map();
72
+ const outsideBases = new Map();
73
+ const fileOf = new Map(classes.map((one) => [one.id, one.file]));
74
+ const own = project.ownPaths();
75
+ const unanswered = new Set();
76
+ const symbols = project.symbolsAt(classes.map((one) => project.handle(one.base)));
77
+ classes.forEach(({ id }, index) => {
78
+ const symbol = symbols[index];
79
+ if (symbol === UNANSWERED) {
80
+ unanswered.add(id);
81
+ return;
82
+ }
83
+ if (symbol === undefined) {
84
+ return;
85
+ }
86
+ const inTarget = chains
87
+ .chainOf(symbol)
88
+ .map((target) => target.id)
89
+ .filter((target) => byId.get(target)?.kind === "class" && target !== id);
90
+ bases.set(id, inTarget);
91
+ const declared = (symbol.flags & SymbolFlags.Alias) === 0 ? symbol : project.queries.aliased(symbol);
92
+ if (declared === UNANSWERED) {
93
+ unanswered.add(id);
94
+ return;
95
+ }
96
+ if (inTarget.length === 0 &&
97
+ (((declared.flags & SymbolFlags.Class) !== 0 &&
98
+ // The target's augmentation of an outside class merges an interface into it,
99
+ // so only a class declaration says where the class is.
100
+ declared.declarations.some((handle) => CLASS_DECLARATIONS.has(handle.kind) && !own.has(handle.path))) ||
101
+ ((declared.flags & SymbolFlags.Interface) !== 0 &&
102
+ declared.declarations.some((handle) => handle.kind === SyntaxKind.VariableDeclaration && !own.has(handle.path))))) {
103
+ outsideBases.set(id, declared);
104
+ }
136
105
  });
137
- }
138
- }
139
- }
140
- return found;
141
- }
106
+ /** The nearest member of one component the class or a class it extends declares. */
107
+ const declaredAbove = (classId, component, seen) => {
108
+ if (seen.has(classId)) {
109
+ return [];
110
+ }
111
+ seen.add(classId);
112
+ const member = members.get(classId)?.get(component);
113
+ if (member !== undefined) {
114
+ return [member.id];
115
+ }
116
+ return (bases.get(classId) ?? []).flatMap((base) => declaredAbove(base, component, seen));
117
+ };
118
+ const read = (classId, from, member) => {
119
+ const file = fileOf.get(classId);
120
+ if (file !== undefined) {
121
+ found.push({
122
+ kind: "named",
123
+ from,
124
+ to: member.id,
125
+ file,
126
+ at: member.position,
127
+ use: "read",
128
+ via: "override",
129
+ });
130
+ }
131
+ };
132
+ const outside = outsideMemberNames(project, bases, outsideBases);
133
+ for (const classId of [...unanswered, ...outside.unanswered]) {
134
+ const ids = [...(members.get(classId)?.values() ?? [])].map((member) => member.id);
135
+ found.push({ kind: "held", from: classId, ids });
136
+ }
137
+ for (const [classId, names] of outside.names) {
138
+ for (const [component, member] of members.get(classId) ?? []) {
139
+ if (names.has(component)) {
140
+ read(classId, classId, member);
141
+ }
142
+ }
143
+ }
144
+ for (const [classId, baseIds] of bases) {
145
+ for (const [component, member] of members.get(classId) ?? []) {
146
+ const seen = new Set([classId]);
147
+ for (const base of baseIds.flatMap((one) => declaredAbove(one, component, seen))) {
148
+ read(classId, base, member);
149
+ }
150
+ }
151
+ }
152
+ return found;
153
+ },
154
+ };
155
+ },
156
+ };
142
157
  /**
143
158
  * Per class of the target with a class declared outside the target in its `extends`
144
159
  * chain, the member components that outside class's instances and constructor carry, a
@@ -146,10 +161,17 @@ export function overrideReferences(project, held, chains, testFiles) {
146
161
  */
147
162
  function outsideMemberNames(project, bases, outsideBases) {
148
163
  const direct = new Map();
164
+ const unanswered = [];
149
165
  const error = errorType(project);
150
166
  for (const [classId, base] of outsideBases) {
151
167
  const instance = project.queries.declaredTypeOf(base);
152
168
  const statics = project.queries.typeOfSymbol(base);
169
+ if (instance === UNANSWERED ||
170
+ statics === UNANSWERED ||
171
+ project.queries.propertiesOf(instance) === UNANSWERED ||
172
+ (statics !== undefined && project.queries.propertiesOf(statics) === UNANSWERED)) {
173
+ unanswered.push(classId);
174
+ }
153
175
  const components = [
154
176
  ...namesOf(project, instance === UNANSWERED ? undefined : instance).map(nameComponent),
155
177
  ...(instance !== UNANSWERED && reaches(project, instance, error) ? ["toString"] : []),
@@ -176,7 +198,7 @@ function outsideMemberNames(project, bases, outsideBases) {
176
198
  names.set(classId, found);
177
199
  }
178
200
  }
179
- return names;
201
+ return { names, unanswered };
180
202
  }
181
203
  /** The global `Error` instance type's identifier, where the program's library declares one. */
182
204
  function errorType(project) {
package/dist/src/paths.js CHANGED
@@ -8,6 +8,50 @@ const SEPARATOR = "/";
8
8
  export function isAbsolutePath(path) {
9
9
  return path.startsWith(SEPARATOR);
10
10
  }
11
+ /** Whether a specifier starts a path relative to the file that writes it: `./` or `../`. */
12
+ export function startsRelativePath(specifier) {
13
+ return specifier.startsWith("./") || specifier.startsWith("../");
14
+ }
15
+ /** Whether a specifier names a path relative to the file that writes it: `.`, `..`, `./` or `../` first. */
16
+ export function isRelativeSpecifier(specifier) {
17
+ return specifier === "." || specifier === ".." || startsRelativePath(specifier);
18
+ }
19
+ /**
20
+ * Whether a module specifier names a path rather than a package, as the compiler reads one:
21
+ * a relative specifier or an absolute path.
22
+ */
23
+ export function isPathSpecifier(specifier) {
24
+ return isRelativeSpecifier(specifier) || isAbsolutePath(specifier);
25
+ }
26
+ /**
27
+ * Whether a string starts with `./`, `../` or `/`, the specifiers a browser reads as a URL
28
+ * rather than a bare name, so a bare `.` or `..` stays a request:
29
+ * https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-url-like-module-specifier
30
+ */
31
+ export function isExplicitPath(text) {
32
+ return startsRelativePath(text) || isAbsolutePath(text);
33
+ }
34
+ /**
35
+ * Whether a string starts with `.` or `/`, which no npm package name does, so a reader that
36
+ * takes any other string as a package's takes it as a path:
37
+ * https://docs.npmjs.com/cli/v11/configuring-npm/package-json#name
38
+ */
39
+ export function cannotStartPackageName(text) {
40
+ return text.startsWith(".") || isAbsolutePath(text);
41
+ }
42
+ /** A URL scheme, `file:` and `node:` among them: https://url.spec.whatwg.org/#url-scheme-string */
43
+ const URL_SCHEME = /^[a-z][a-z\d+.-]*:/iu;
44
+ /** Whether a string starts with a URL scheme. */
45
+ export function hasURLScheme(text) {
46
+ return URL_SCHEME.test(text);
47
+ }
48
+ /**
49
+ * Whether a URL reference is a relative one that does not start with a solidus, the bare
50
+ * name included: https://url.spec.whatwg.org/#relative-url-string
51
+ */
52
+ export function isPathRelativeURL(reference) {
53
+ return reference !== "" && !reference.startsWith(SEPARATOR) && !hasURLScheme(reference);
54
+ }
11
55
  /**
12
56
  * One path with `.` removed, `..` applied to the segment before it, and repeated
13
57
  * separators collapsed. A `..` that walks past the root of an absolute path is
@@ -88,17 +132,12 @@ export function extensionOf(path) {
88
132
  export function resolvePath(against, path) {
89
133
  return isAbsolutePath(path) ? normalizePath(path) : joinPath(against, path);
90
134
  }
91
- /**
92
- * A URL reference's scheme. A reference with none that does not start with a solidus is
93
- * relative, the bare name included: https://url.spec.whatwg.org/#relative-url-string
94
- */
95
- const URL_SCHEME = /^[a-z][a-z\d+.-]*:/iu;
96
135
  /**
97
136
  * The path a relative URL reference names against `dir`, its query and its fragment
98
137
  * removed, which name no part of the file; undefined where the reference is not relative.
99
138
  */
100
139
  export function relativeURLPath(dir, reference) {
101
- if (reference === "" || reference.startsWith("/") || URL_SCHEME.test(reference)) {
140
+ if (!isPathRelativeURL(reference)) {
102
141
  return undefined;
103
142
  }
104
143
  const cut = reference.search(/[?#]/u);
@@ -112,7 +151,7 @@ export function relativeURLPath(dir, reference) {
112
151
  */
113
152
  export function queriedModulePath(specifier) {
114
153
  const query = specifier.indexOf("?");
115
- if (query < 0 || !(specifier.startsWith("./") || specifier.startsWith("../"))) {
154
+ if (query < 0 || !startsRelativePath(specifier)) {
116
155
  return undefined;
117
156
  }
118
157
  const raw = specifier
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Which calls are the platform's `require` and which constructions its `URL` (roots.23,
3
- * references-ts.1), decided once over the bindings of the names a callee is written with: the
2
+ * Which calls are the platform's `require` or AMD's `define` and which constructions its `URL`
3
+ * (roots.23, references-ts.37, references-ts.1), decided once over the bindings of the names a callee is written with: the
4
4
  * checker's in a program, the file's own scopes in a module read outside every program.
5
5
  */
6
6
  import { isBindingElement, isCallExpression, isExportSpecifier, isIdentifier, isImportSpecifier, isPropertyAccessExpression, isStringLiteral, isVariableDeclaration, } from "@typescript/native/ast";
@@ -11,11 +11,10 @@ import { isAmbient } from "./inventory.js";
11
11
  import { callsPlatformBy, carriedNames } from "./platform-calls.js";
12
12
  import { unwrapped } from "./unwrap.js";
13
13
  /** The symbol one symbol names through its aliases, or the symbol itself where it is none. */
14
- function dealiased(project, symbol, asked) {
14
+ function dealiased(project, symbol) {
15
15
  if ((symbol.flags & SymbolFlags.Alias) === 0) {
16
16
  return symbol;
17
17
  }
18
- asked();
19
18
  return project.queries.aliased(symbol);
20
19
  }
21
20
  /** Each declaration of `symbol` in a file of the target, read in this project's program. */
@@ -50,7 +49,7 @@ function writesBody(node) {
50
49
  * prove it the target's own where one {@link writesBody}. A chain that ends at no
51
50
  * declaration, or that the checker leaves unanswered, reaches none.
52
51
  */
53
- function boundBy(project, symbol, asked) {
52
+ function boundBy(project, symbol) {
54
53
  if (typeof symbol !== "object") {
55
54
  return undefined;
56
55
  }
@@ -66,28 +65,23 @@ function boundBy(project, symbol, asked) {
66
65
  if ((at.flags & SymbolFlags.Alias) === 0) {
67
66
  return { names, own, provenOwn: own.some(writesBody) };
68
67
  }
69
- asked();
70
68
  const next = project.aliasStepOf(at);
71
69
  at = typeof next === "object" ? next : undefined;
72
70
  }
73
71
  return { names, own: [], provenOwn: false };
74
72
  }
75
73
  /** 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
- };
74
+ function checkerBinding(project) {
75
+ return (written) => boundBy(project, project.symbolsAt([project.handle(written)])[0]);
81
76
  }
82
77
  /**
83
78
  * 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`.
79
+ * is one of the platform's `require`, AMD's `define` or `URL` (roots.23, references-ts.37,
80
+ * references-ts.1), as {@link callsPlatformBy} decides it over this project's checker.
87
81
  */
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));
82
+ export function callsPlatform(project, name, callee, symbol) {
83
+ const others = checkerBinding(project);
84
+ return callsPlatformBy(name, callee, (written) => written === callee ? boundBy(project, symbol) : others(written));
91
85
  }
92
86
  /**
93
87
  * Whether a worker's constructor or `navigator`, the batch's answer for which is `symbol`, is
@@ -122,16 +116,16 @@ function inThreadsModule(node) {
122
116
  * (references-ts.2): its symbol resolves to a declaration of `worker_threads`, or one of its
123
117
  * own declarations binds the module's `Worker`, or `holder`, the namespace it is read
124
118
  * 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`.
119
+ * decides where the module has no declaration.
126
120
  */
127
- export function startsThread(project, symbol, holder, asked) {
128
- const bindingOf = checkerBinding(project, asked);
121
+ export function startsThread(project, symbol, holder) {
122
+ const bindingOf = checkerBinding(project);
129
123
  const declared = (one, member) => typeof one === "object" &&
130
124
  ownDeclarations(project, one).some((node) => boundExports(node, (specifier) => THREADS.has(specifier), bindingOf).includes(member));
131
125
  if (declared(symbol, "Worker") || declared(holder, NAMESPACE)) {
132
126
  return true;
133
127
  }
134
- const named = typeof symbol === "object" ? dealiased(project, symbol, asked) : undefined;
128
+ const named = typeof symbol === "object" ? dealiased(project, symbol) : undefined;
135
129
  return (typeof named === "object" &&
136
130
  named.declarations.some((handle) => {
137
131
  const node = project.declarationAt(handle)?.node;