@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
package/src/overrides.ts CHANGED
@@ -15,14 +15,14 @@ import {
15
15
  SyntaxKind,
16
16
  type HeritageClauseElement,
17
17
  type Node,
18
+ type SourceFile,
18
19
  } from "@typescript/native/ast";
19
- import type { AliasChains } from "./alias-chain.ts";
20
20
  import { memberComponent } from "./class-members.ts";
21
- import { nodeKey, type Inventory, type InventorySymbol } from "./inventory.ts";
21
+ import { nodeKey, type InventorySymbol } from "./inventory.ts";
22
22
  import { SymbolFlags, type Symbol as TSSymbol, type Type } from "@typescript/native/sync";
23
23
  import { UNANSWERED } from "./query.ts";
24
24
  import { nameComponent } from "./ref.ts";
25
- import type { Reference } from "./references.ts";
25
+ import type { Read, ReadRule } from "./references/read-rule.ts";
26
26
  import type { ProjectView } from "./session.ts";
27
27
 
28
28
  const CLASS_DECLARATIONS: ReadonlySet<SyntaxKind> = new Set([
@@ -41,141 +41,157 @@ function nameOf(entry: HeritageClauseElement): Node {
41
41
  return isQualifiedName(entry.typeName) ? entry.typeName.right : entry.typeName;
42
42
  }
43
43
 
44
- /** Each class of the target whose `extends` clause names a class, with the name node. */
45
- function extending<Brand>(
46
- project: ProjectView<Brand>,
47
- held: Inventory,
48
- ): readonly { readonly id: string; readonly base: Node }[] {
49
- const found: { id: string; base: Node }[] = [];
50
- for (const file of project.ownSourceFiles()) {
51
- const visit = (node: Node): void => {
52
- if (isClassDeclaration(node) || isClassExpression(node)) {
53
- const id = held.declarations.get(nodeKey(file, node));
54
- const clause = node.heritageClauses?.find((one) => one.token === SyntaxKind.ExtendsKeyword);
55
- const base = clause?.types[0];
56
- if (id !== undefined && base !== undefined) {
57
- found.push({ id, base: nameOf(base) });
58
- }
59
- }
60
- node.forEachChild(visit);
61
- };
62
- file.forEachChild(visit);
63
- }
64
- return found;
44
+ /** One class whose `extends` clause names a class, with the name node and its file. */
45
+ interface Extending {
46
+ readonly node: Node;
47
+ readonly file: SourceFile;
48
+ readonly base: Node;
65
49
  }
66
50
 
67
51
  /**
68
- * The references each base class member makes to its overrides, for the classes of one
69
- * project that extend another class of the target, and each class makes to its members
70
- * that override a member of a class declared outside the target. A member a nearer base
71
- * already overrides is reached through that override. A private member is not
72
- * overridden. Each reference is placed at the override and made by its file.
52
+ * The reads each base class member makes of its overrides, for the classes of the target that
53
+ * extend another class of the target, and each class makes of its members that override a
54
+ * member of a class declared outside the target. A member a nearer base already overrides is
55
+ * reached through that override. A private member is not overridden. Each read is placed at
56
+ * the override. A class whose base the checker leaves unanswered holds every member it could
57
+ * override.
73
58
  */
74
- export function overrideReferences<Brand>(
75
- project: ProjectView<Brand>,
76
- held: Inventory,
77
- chains: AliasChains,
78
- testFiles: ReadonlySet<string>,
79
- ): readonly Reference[] {
80
- const classes = extending(project, held);
81
- if (classes.length === 0) {
82
- return [];
83
- }
84
- const byId = new Map(held.symbols.map((symbol) => [symbol.id, symbol]));
85
- const members = new Map<string, Map<string, InventorySymbol>>();
86
- for (const symbol of held.symbols) {
87
- const component = memberComponent(symbol, byId);
88
- if (
89
- component === undefined ||
90
- !OVERRIDABLE.has(symbol.kind) ||
91
- symbol.visibility === "private" ||
92
- symbol.visibility === "private-name"
93
- ) {
94
- continue;
95
- }
96
- const own = members.get(symbol.parent) ?? new Map<string, InventorySymbol>();
97
- own.set(component, symbol);
98
- members.set(symbol.parent, own);
99
- }
100
- const bases = new Map<string, readonly string[]>();
101
- const outsideBases = new Map<string, TSSymbol>();
102
- const own = project.ownPaths();
103
- const symbols = project.symbolsAt(classes.map((one) => project.handle(one.base)));
104
- classes.forEach(({ id }, index) => {
105
- const symbol = symbols[index];
106
- if (symbol !== undefined && symbol !== UNANSWERED) {
107
- const inTarget = chains
108
- .chainOf(symbol)
109
- .map((target) => target.id)
110
- .filter((target) => byId.get(target)?.kind === "class" && target !== id);
111
- bases.set(id, inTarget);
112
- const declared =
113
- (symbol.flags & SymbolFlags.Alias) === 0 ? symbol : project.queries.aliased(symbol);
114
- if (
115
- inTarget.length === 0 &&
116
- declared !== UNANSWERED &&
117
- (((declared.flags & SymbolFlags.Class) !== 0 &&
118
- // The target's augmentation of an outside class merges an interface into it,
119
- // so only a class declaration says where the class is.
120
- declared.declarations.some(
121
- (handle) => CLASS_DECLARATIONS.has(handle.kind) && !own.has(handle.path),
122
- )) ||
123
- ((declared.flags & SymbolFlags.Interface) !== 0 &&
124
- declared.declarations.some(
125
- (handle) => handle.kind === SyntaxKind.VariableDeclaration && !own.has(handle.path),
126
- )))
127
- ) {
128
- outsideBases.set(id, declared);
129
- }
130
- }
131
- });
132
-
133
- /** The nearest member of one component the class or a class it extends declares. */
134
- const declaredAbove = (classId: string, component: string, seen: Set<string>): string[] => {
135
- if (seen.has(classId)) {
136
- return [];
137
- }
138
- seen.add(classId);
139
- const own = members.get(classId)?.get(component);
140
- if (own !== undefined) {
141
- return [own.id];
142
- }
143
- return (bases.get(classId) ?? []).flatMap((base) => declaredAbove(base, component, seen));
144
- };
145
-
146
- const found: Reference[] = [];
147
- const outsideNames = outsideMemberNames(project, bases, outsideBases);
148
- for (const [classId, names] of outsideNames) {
149
- for (const [component, member] of members.get(classId) ?? []) {
150
- if (names.has(component)) {
151
- found.push({
152
- from: classId,
153
- to: member.id,
154
- position: member.position,
155
- use: "read",
156
- resolution: "override",
157
- test: testFiles.has(member.position.path),
59
+ export const overrides: ReadRule = {
60
+ start() {
61
+ const extending: Extending[] = [];
62
+ return {
63
+ visit(node, { file }) {
64
+ if (isClassDeclaration(node) || isClassExpression(node)) {
65
+ const clause = node.heritageClauses?.find(
66
+ (one) => one.token === SyntaxKind.ExtendsKeyword,
67
+ );
68
+ const base = clause?.types[0];
69
+ if (base !== undefined) {
70
+ extending.push({ node, file, base: nameOf(base) });
71
+ }
72
+ }
73
+ },
74
+ reads({ project, held, chains }) {
75
+ const found: Read[] = [];
76
+ const classes = extending.flatMap((one) => {
77
+ const id = held.declarations.get(nodeKey(one.file, one.node));
78
+ return id === undefined ? [] : [{ ...one, id }];
158
79
  });
159
- }
160
- }
161
- }
162
- for (const [classId, baseIds] of bases) {
163
- for (const [component, member] of members.get(classId) ?? []) {
164
- const seen = new Set([classId]);
165
- for (const base of baseIds.flatMap((one) => declaredAbove(one, component, seen))) {
166
- found.push({
167
- from: base,
168
- to: member.id,
169
- position: member.position,
170
- use: "read",
171
- resolution: "override",
172
- test: testFiles.has(member.position.path),
80
+ if (classes.length === 0) {
81
+ return found;
82
+ }
83
+ const byId = new Map(held.symbols.map((symbol) => [symbol.id, symbol]));
84
+ const members = new Map<string, Map<string, InventorySymbol>>();
85
+ for (const symbol of held.symbols) {
86
+ const component = memberComponent(symbol, byId);
87
+ if (
88
+ component === undefined ||
89
+ !OVERRIDABLE.has(symbol.kind) ||
90
+ symbol.visibility === "private" ||
91
+ symbol.visibility === "private-name"
92
+ ) {
93
+ continue;
94
+ }
95
+ const own = members.get(symbol.parent) ?? new Map<string, InventorySymbol>();
96
+ own.set(component, symbol);
97
+ members.set(symbol.parent, own);
98
+ }
99
+ const bases = new Map<string, readonly string[]>();
100
+ const outsideBases = new Map<string, TSSymbol>();
101
+ const fileOf = new Map(classes.map((one) => [one.id, one.file]));
102
+ const own = project.ownPaths();
103
+ const unanswered = new Set<string>();
104
+ const symbols = project.symbolsAt(classes.map((one) => project.handle(one.base)));
105
+ classes.forEach(({ id }, index) => {
106
+ const symbol = symbols[index];
107
+ if (symbol === UNANSWERED) {
108
+ unanswered.add(id);
109
+ return;
110
+ }
111
+ if (symbol === undefined) {
112
+ return;
113
+ }
114
+ const inTarget = chains
115
+ .chainOf(symbol)
116
+ .map((target) => target.id)
117
+ .filter((target) => byId.get(target)?.kind === "class" && target !== id);
118
+ bases.set(id, inTarget);
119
+ const declared =
120
+ (symbol.flags & SymbolFlags.Alias) === 0 ? symbol : project.queries.aliased(symbol);
121
+ if (declared === UNANSWERED) {
122
+ unanswered.add(id);
123
+ return;
124
+ }
125
+ if (
126
+ inTarget.length === 0 &&
127
+ (((declared.flags & SymbolFlags.Class) !== 0 &&
128
+ // The target's augmentation of an outside class merges an interface into it,
129
+ // so only a class declaration says where the class is.
130
+ declared.declarations.some(
131
+ (handle) => CLASS_DECLARATIONS.has(handle.kind) && !own.has(handle.path),
132
+ )) ||
133
+ ((declared.flags & SymbolFlags.Interface) !== 0 &&
134
+ declared.declarations.some(
135
+ (handle) =>
136
+ handle.kind === SyntaxKind.VariableDeclaration && !own.has(handle.path),
137
+ )))
138
+ ) {
139
+ outsideBases.set(id, declared);
140
+ }
173
141
  });
174
- }
175
- }
176
- }
177
- return found;
178
- }
142
+
143
+ /** The nearest member of one component the class or a class it extends declares. */
144
+ const declaredAbove = (classId: string, component: string, seen: Set<string>): string[] => {
145
+ if (seen.has(classId)) {
146
+ return [];
147
+ }
148
+ seen.add(classId);
149
+ const member = members.get(classId)?.get(component);
150
+ if (member !== undefined) {
151
+ return [member.id];
152
+ }
153
+ return (bases.get(classId) ?? []).flatMap((base) => declaredAbove(base, component, seen));
154
+ };
155
+
156
+ const read = (classId: string, from: string, member: InventorySymbol): void => {
157
+ const file = fileOf.get(classId);
158
+ if (file !== undefined) {
159
+ found.push({
160
+ kind: "named",
161
+ from,
162
+ to: member.id,
163
+ file,
164
+ at: member.position,
165
+ use: "read",
166
+ via: "override",
167
+ });
168
+ }
169
+ };
170
+ const outside = outsideMemberNames(project, bases, outsideBases);
171
+ for (const classId of [...unanswered, ...outside.unanswered]) {
172
+ const ids = [...(members.get(classId)?.values() ?? [])].map((member) => member.id);
173
+ found.push({ kind: "held", from: classId, ids });
174
+ }
175
+ for (const [classId, names] of outside.names) {
176
+ for (const [component, member] of members.get(classId) ?? []) {
177
+ if (names.has(component)) {
178
+ read(classId, classId, member);
179
+ }
180
+ }
181
+ }
182
+ for (const [classId, baseIds] of bases) {
183
+ for (const [component, member] of members.get(classId) ?? []) {
184
+ const seen = new Set([classId]);
185
+ for (const base of baseIds.flatMap((one) => declaredAbove(one, component, seen))) {
186
+ read(classId, base, member);
187
+ }
188
+ }
189
+ }
190
+ return found;
191
+ },
192
+ };
193
+ },
194
+ };
179
195
 
180
196
  /**
181
197
  * Per class of the target with a class declared outside the target in its `extends`
@@ -186,12 +202,24 @@ function outsideMemberNames<Brand>(
186
202
  project: ProjectView<Brand>,
187
203
  bases: ReadonlyMap<string, readonly string[]>,
188
204
  outsideBases: ReadonlyMap<string, TSSymbol>,
189
- ): ReadonlyMap<string, ReadonlySet<string>> {
205
+ ): {
206
+ readonly names: ReadonlyMap<string, ReadonlySet<string>>;
207
+ readonly unanswered: readonly string[];
208
+ } {
190
209
  const direct = new Map<string, ReadonlySet<string>>();
210
+ const unanswered: string[] = [];
191
211
  const error = errorType(project);
192
212
  for (const [classId, base] of outsideBases) {
193
213
  const instance = project.queries.declaredTypeOf(base);
194
214
  const statics = project.queries.typeOfSymbol(base);
215
+ if (
216
+ instance === UNANSWERED ||
217
+ statics === UNANSWERED ||
218
+ project.queries.propertiesOf(instance) === UNANSWERED ||
219
+ (statics !== undefined && project.queries.propertiesOf(statics) === UNANSWERED)
220
+ ) {
221
+ unanswered.push(classId);
222
+ }
195
223
  const components = [
196
224
  ...namesOf(project, instance === UNANSWERED ? undefined : instance).map(nameComponent),
197
225
  ...(instance !== UNANSWERED && reaches(project, instance, error) ? ["toString"] : []),
@@ -220,7 +248,7 @@ function outsideMemberNames<Brand>(
220
248
  names.set(classId, found);
221
249
  }
222
250
  }
223
- return names;
251
+ return { names, unanswered };
224
252
  }
225
253
 
226
254
  /** The global `Error` instance type's identifier, where the program's library declares one. */
package/src/paths.ts CHANGED
@@ -11,6 +11,58 @@ export function isAbsolutePath(path: string): boolean {
11
11
  return path.startsWith(SEPARATOR);
12
12
  }
13
13
 
14
+ /** Whether a specifier starts a path relative to the file that writes it: `./` or `../`. */
15
+ export function startsRelativePath(specifier: string): boolean {
16
+ return specifier.startsWith("./") || specifier.startsWith("../");
17
+ }
18
+
19
+ /** Whether a specifier names a path relative to the file that writes it: `.`, `..`, `./` or `../` first. */
20
+ export function isRelativeSpecifier(specifier: string): boolean {
21
+ return specifier === "." || specifier === ".." || startsRelativePath(specifier);
22
+ }
23
+
24
+ /**
25
+ * Whether a module specifier names a path rather than a package, as the compiler reads one:
26
+ * a relative specifier or an absolute path.
27
+ */
28
+ export function isPathSpecifier(specifier: string): boolean {
29
+ return isRelativeSpecifier(specifier) || isAbsolutePath(specifier);
30
+ }
31
+
32
+ /**
33
+ * Whether a string starts with `./`, `../` or `/`, the specifiers a browser reads as a URL
34
+ * rather than a bare name, so a bare `.` or `..` stays a request:
35
+ * https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-url-like-module-specifier
36
+ */
37
+ export function isExplicitPath(text: string): boolean {
38
+ return startsRelativePath(text) || isAbsolutePath(text);
39
+ }
40
+
41
+ /**
42
+ * Whether a string starts with `.` or `/`, which no npm package name does, so a reader that
43
+ * takes any other string as a package's takes it as a path:
44
+ * https://docs.npmjs.com/cli/v11/configuring-npm/package-json#name
45
+ */
46
+ export function cannotStartPackageName(text: string): boolean {
47
+ return text.startsWith(".") || isAbsolutePath(text);
48
+ }
49
+
50
+ /** A URL scheme, `file:` and `node:` among them: https://url.spec.whatwg.org/#url-scheme-string */
51
+ const URL_SCHEME = /^[a-z][a-z\d+.-]*:/iu;
52
+
53
+ /** Whether a string starts with a URL scheme. */
54
+ export function hasURLScheme(text: string): boolean {
55
+ return URL_SCHEME.test(text);
56
+ }
57
+
58
+ /**
59
+ * Whether a URL reference is a relative one that does not start with a solidus, the bare
60
+ * name included: https://url.spec.whatwg.org/#relative-url-string
61
+ */
62
+ export function isPathRelativeURL(reference: string): boolean {
63
+ return reference !== "" && !reference.startsWith(SEPARATOR) && !hasURLScheme(reference);
64
+ }
65
+
14
66
  /**
15
67
  * One path with `.` removed, `..` applied to the segment before it, and repeated
16
68
  * separators collapsed. A `..` that walks past the root of an absolute path is
@@ -97,18 +149,12 @@ export function resolvePath(against: string, path: string): string {
97
149
  return isAbsolutePath(path) ? normalizePath(path) : joinPath(against, path);
98
150
  }
99
151
 
100
- /**
101
- * A URL reference's scheme. A reference with none that does not start with a solidus is
102
- * relative, the bare name included: https://url.spec.whatwg.org/#relative-url-string
103
- */
104
- const URL_SCHEME = /^[a-z][a-z\d+.-]*:/iu;
105
-
106
152
  /**
107
153
  * The path a relative URL reference names against `dir`, its query and its fragment
108
154
  * removed, which name no part of the file; undefined where the reference is not relative.
109
155
  */
110
156
  export function relativeURLPath(dir: string, reference: string): string | undefined {
111
- if (reference === "" || reference.startsWith("/") || URL_SCHEME.test(reference)) {
157
+ if (!isPathRelativeURL(reference)) {
112
158
  return undefined;
113
159
  }
114
160
  const cut = reference.search(/[?#]/u);
@@ -123,7 +169,7 @@ export function relativeURLPath(dir: string, reference: string): string | undefi
123
169
  */
124
170
  export function queriedModulePath(specifier: string): string | undefined {
125
171
  const query = specifier.indexOf("?");
126
- if (query < 0 || !(specifier.startsWith("./") || specifier.startsWith("../"))) {
172
+ if (query < 0 || !startsRelativePath(specifier)) {
127
173
  return undefined;
128
174
  }
129
175
  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
 
@@ -19,7 +19,7 @@ import {
19
19
  import { parenthesesRemoved, unwrapped } from "./unwrap.ts";
20
20
 
21
21
  /** A name of the platform the analysis reads calls of. */
22
- type PlatformName = "require" | "createRequire" | "URL";
22
+ type PlatformName = "require" | "createRequire" | "define" | "URL";
23
23
 
24
24
  /** What one written name is bound to, as far as its reader can tell. */
25
25
  export interface Bound {
@@ -28,19 +28,11 @@ import { isAmbient } from "./inventory.ts";
28
28
  import { callsPlatformBy, carriedNames, type BindingOf, type Bound } from "./platform-calls.ts";
29
29
  import { unwrapped } from "./unwrap.ts";
30
30
 
31
- /** Counts one request a platform decision puts to the checker. */
32
- type Asked = () => void;
33
-
34
31
  /** The symbol one symbol names through its aliases, or the symbol itself where it is none. */
35
- function dealiased<Brand>(
36
- project: ProjectView<Brand>,
37
- symbol: TSSymbol,
38
- asked: Asked,
39
- ): Answer<TSSymbol> {
32
+ function dealiased<Brand>(project: ProjectView<Brand>, symbol: TSSymbol): Answer<TSSymbol> {
40
33
  if ((symbol.flags & SymbolFlags.Alias) === 0) {
41
34
  return symbol;
42
35
  }
43
- asked();
44
36
  return project.queries.aliased(symbol);
45
37
  }
46
38
 
@@ -84,7 +76,6 @@ function writesBody(node: Node): boolean {
84
76
  function boundBy<Brand>(
85
77
  project: ProjectView<Brand>,
86
78
  symbol: Answer<TSSymbol> | undefined,
87
- asked: Asked,
88
79
  ): Bound | undefined {
89
80
  if (typeof symbol !== "object") {
90
81
  return undefined;
@@ -101,7 +92,6 @@ function boundBy<Brand>(
101
92
  if ((at.flags & SymbolFlags.Alias) === 0) {
102
93
  return { names, own, provenOwn: own.some(writesBody) };
103
94
  }
104
- asked();
105
95
  const next = project.aliasStepOf(at);
106
96
  at = typeof next === "object" ? next : undefined;
107
97
  }
@@ -109,29 +99,24 @@ function boundBy<Brand>(
109
99
  }
110
100
 
111
101
  /** Each written name's binding, as this project's checker answers it. */
112
- function checkerBinding<Brand>(project: ProjectView<Brand>, asked: Asked): BindingOf {
113
- return (written) => {
114
- asked();
115
- return boundBy(project, project.symbolsAt([project.handle(written)])[0], asked);
116
- };
102
+ function checkerBinding<Brand>(project: ProjectView<Brand>): BindingOf {
103
+ return (written) => boundBy(project, project.symbolsAt([project.handle(written)])[0]);
117
104
  }
118
105
 
119
106
  /**
120
107
  * Whether a call or construction through `callee`, the batch's answer for which is `symbol`,
121
- * is one of the platform's `require` or `URL` (roots.23, references-ts.1), as
122
- * {@link callsPlatformBy} decides it over this project's checker, each request it puts
123
- * counted by `asked`.
108
+ * is one of the platform's `require`, AMD's `define` or `URL` (roots.23, references-ts.37,
109
+ * references-ts.1), as {@link callsPlatformBy} decides it over this project's checker.
124
110
  */
125
111
  export function callsPlatform<Brand>(
126
112
  project: ProjectView<Brand>,
127
- name: "require" | "URL",
113
+ name: "require" | "define" | "URL",
128
114
  callee: Identifier,
129
115
  symbol: Answer<TSSymbol> | undefined,
130
- asked: Asked,
131
116
  ): boolean {
132
- const others = checkerBinding(project, asked);
117
+ const others = checkerBinding(project);
133
118
  return callsPlatformBy(name, callee, (written) =>
134
- written === callee ? boundBy(project, symbol, asked) : others(written),
119
+ written === callee ? boundBy(project, symbol) : others(written),
135
120
  );
136
121
  }
137
122
 
@@ -177,15 +162,14 @@ function inThreadsModule(node: Node): boolean {
177
162
  * (references-ts.2): its symbol resolves to a declaration of `worker_threads`, or one of its
178
163
  * own declarations binds the module's `Worker`, or `holder`, the namespace it is read
179
164
  * through, binds the module, each binding read as {@link boundExports} reads one, which
180
- * decides where the module has no declaration. Each request it puts is counted by `asked`.
165
+ * decides where the module has no declaration.
181
166
  */
182
167
  export function startsThread<Brand>(
183
168
  project: ProjectView<Brand>,
184
169
  symbol: Answer<TSSymbol> | undefined,
185
170
  holder: Answer<TSSymbol> | undefined,
186
- asked: Asked,
187
171
  ): boolean {
188
- const bindingOf = checkerBinding(project, asked);
172
+ const bindingOf = checkerBinding(project);
189
173
  const declared = (one: Answer<TSSymbol> | undefined, member: string): boolean =>
190
174
  typeof one === "object" &&
191
175
  ownDeclarations(project, one).some((node) =>
@@ -194,7 +178,7 @@ export function startsThread<Brand>(
194
178
  if (declared(symbol, "Worker") || declared(holder, NAMESPACE)) {
195
179
  return true;
196
180
  }
197
- const named = typeof symbol === "object" ? dealiased(project, symbol, asked) : undefined;
181
+ const named = typeof symbol === "object" ? dealiased(project, symbol) : undefined;
198
182
  return (
199
183
  typeof named === "object" &&
200
184
  named.declarations.some((handle) => {
@@ -0,0 +1,60 @@
1
+ import type { Node, SourceFile } from "@typescript/native/ast";
2
+ import type { Type } from "@typescript/native/sync";
3
+ import type { AliasChains } from "../alias-chain.ts";
4
+ import type { Inventory } from "../inventory.ts";
5
+ import type { Position } from "../position.ts";
6
+ import type { Resolution } from "../references.ts";
7
+ import type { ProjectView } from "../session.ts";
8
+
9
+ /** Where the walk offered a read rule one node: the file, and the declaration enclosing the node. */
10
+ interface ReadSite {
11
+ readonly file: SourceFile;
12
+ readonly from: string;
13
+ }
14
+
15
+ /** What a read rule reads its answers through, after the walk of every file of the side. */
16
+ export interface ReadContext<Brand> {
17
+ readonly project: ProjectView<Brand>;
18
+ readonly held: Inventory;
19
+ readonly chains: AliasChains;
20
+ }
21
+
22
+ /**
23
+ * One read a rule makes: a use of the declaration `to` by `from` at a node of a walked file,
24
+ * or, where a question the read needed went unanswered, a hold. A hold names what the answer
25
+ * could have made live (program.33): the declarations `ids`, each declaration bearing one of
26
+ * `names`, each member of one of `types`, or with `members` every member the inventory holds;
27
+ * the declaration making the read is held as well. A named read sits at a node of `file`, or
28
+ * at a declaration's own position in it.
29
+ */
30
+ export type Read =
31
+ | {
32
+ readonly kind: "named";
33
+ readonly from: string;
34
+ readonly to: string;
35
+ readonly file: SourceFile;
36
+ readonly at: Node | Position;
37
+ readonly use: "read" | "write";
38
+ readonly via: Resolution;
39
+ /** A read a rule counts as production code's whoever writes it. */
40
+ readonly production?: true;
41
+ }
42
+ | {
43
+ readonly kind: "held";
44
+ readonly from: string;
45
+ readonly ids?: readonly string[];
46
+ readonly names?: readonly string[];
47
+ readonly types?: readonly Type[];
48
+ readonly members?: true;
49
+ };
50
+
51
+ /** One pass of one read rule over the files of one side: it collects while the walk offers nodes, then reads. */
52
+ interface ReadRuleRun {
53
+ visit(node: Node, site: ReadSite): void;
54
+ reads<Brand>(context: ReadContext<Brand>): readonly Read[];
55
+ }
56
+
57
+ /** One rule of the references pages that reads declarations no name node names. */
58
+ export interface ReadRule {
59
+ start(): ReadRuleRun;
60
+ }
@@ -0,0 +1,55 @@
1
+ import { elementAccess } from "../element-access.ts";
2
+ import { indexedAccess } from "../indexed-access.ts";
3
+ import { keyofComparisons } from "../keyof-comparisons.ts";
4
+ import { moduleDeclarations } from "../module-declarations.ts";
5
+ import { overrides } from "../overrides.ts";
6
+ import { isAnswered } from "../query.ts";
7
+ import { iteration } from "../symbol-keys.ts";
8
+ import { typeParameterReads } from "../type-parameter-reads.ts";
9
+ import { unimplementedCallees } from "../unimplemented-callees.ts";
10
+ import type { Read, ReadContext, ReadRule } from "./read-rule.ts";
11
+
12
+ /** The member kinds a hold of every member names. */
13
+ const MEMBER_KINDS: ReadonlySet<string> = new Set([
14
+ "method",
15
+ "class-member",
16
+ "interface-method",
17
+ "type-member",
18
+ "enum-member",
19
+ ]);
20
+
21
+ /**
22
+ * Every read rule, each run over every side's walked files with the reference sources:
23
+ * a consumer's read of a target member is a reference as the target's own is.
24
+ */
25
+ export const READ_RULES: readonly ReadRule[] = [
26
+ overrides,
27
+ indexedAccess,
28
+ elementAccess,
29
+ keyofComparisons,
30
+ iteration,
31
+ typeParameterReads,
32
+ unimplementedCallees,
33
+ moduleDeclarations,
34
+ ];
35
+
36
+ /** The declarations one hold names, with the declaration whose read it is. */
37
+ export function heldBy<Brand>(
38
+ read: Extract<Read, { kind: "held" }>,
39
+ { project, held, chains }: ReadContext<Brand>,
40
+ ): readonly string[] {
41
+ const ids = [...(read.from === "" ? [] : [read.from]), ...(read.ids ?? [])];
42
+ for (const name of read.names ?? []) {
43
+ ids.push(...(held.byName.get(name) ?? []));
44
+ }
45
+ for (const type of read.types ?? []) {
46
+ const properties = project.queries.propertiesOf(type);
47
+ for (const property of isAnswered(properties) ? properties : []) {
48
+ ids.push(...chains.declarationsOf(property));
49
+ }
50
+ }
51
+ if (read.members === true) {
52
+ ids.push(...held.symbols.filter((one) => MEMBER_KINDS.has(one.kind)).map((one) => one.id));
53
+ }
54
+ return ids;
55
+ }