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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/dist/src/alias-chain.js +2 -2
  2. package/dist/src/analysis.js +43 -38
  3. package/dist/src/commands/read.js +6 -7
  4. package/dist/src/conformance-record.js +1 -1
  5. package/dist/src/consumers.js +4 -2
  6. package/dist/src/contract/config.generated.js +1 -1
  7. package/dist/src/contract/contract.generated.js +120 -2
  8. package/dist/src/dependencies.js +81 -49
  9. package/dist/src/entries/build-scripts.js +222 -0
  10. package/dist/src/{configuration-files.js → entries/configuration-files.js} +20 -58
  11. package/dist/src/{convention-rows.js → entries/convention-rows.js} +120 -27
  12. package/dist/src/entries/conventions.js +739 -0
  13. package/dist/src/{html-entries.js → entries/html-entries.js} +3 -3
  14. package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +2 -2
  15. package/dist/src/entries/run-directories.js +77 -0
  16. package/dist/src/entries/sources.js +144 -0
  17. package/dist/src/entries/static-module.js +669 -0
  18. package/dist/src/entries/test-runners.js +684 -0
  19. package/dist/src/{workflow-steps.js → entries/workflow-steps.js} +18 -8
  20. package/dist/src/extensions.js +37 -0
  21. package/dist/src/facts.js +38 -0
  22. package/dist/src/file-facts.js +1 -1
  23. package/dist/src/findings/emitters.js +5 -8
  24. package/dist/src/findings/self-check.js +36 -20
  25. package/dist/src/findings-pass.js +5 -6
  26. package/dist/src/framework-lifecycle.js +1 -1
  27. package/dist/src/fs/manifest.js +15 -20
  28. package/dist/src/generated-file.js +172 -29
  29. package/dist/src/graph.js +2 -5
  30. package/dist/src/index.js +1 -1
  31. package/dist/src/injection-container.js +1 -1
  32. package/dist/src/inventory/commonjs.js +507 -0
  33. package/dist/src/inventory/enumerate.js +167 -0
  34. package/dist/src/inventory/syntax.js +236 -0
  35. package/dist/src/inventory.js +305 -705
  36. package/dist/src/javascript-file.js +26 -11
  37. package/dist/src/load/classify.js +28 -9
  38. package/dist/src/load/diagnostics.js +8 -1
  39. package/dist/src/load/discover.js +16 -8
  40. package/dist/src/load/package-scope.js +56 -0
  41. package/dist/src/load/plan.js +2 -2
  42. package/dist/src/load/synthesize.js +5 -5
  43. package/dist/src/load/workspace-manifests.js +1 -1
  44. package/dist/src/matrix.js +6 -13
  45. package/dist/src/outside-tests.js +1 -1
  46. package/dist/src/paths.js +16 -0
  47. package/dist/src/platform-calls.js +78 -0
  48. package/dist/src/platform-symbols.js +140 -0
  49. package/dist/src/position.js +41 -0
  50. package/dist/src/ref.js +11 -20
  51. package/dist/src/references.js +281 -50
  52. package/dist/src/resolve.js +1 -1
  53. package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +4 -4
  54. package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +5 -8
  55. package/dist/src/{manifest.js → roots/manifest.js} +4 -4
  56. package/dist/src/roots/published-reach.js +412 -0
  57. package/dist/src/roots/root-set.js +489 -0
  58. package/dist/src/roots/unseen-loads.js +208 -0
  59. package/dist/src/serialization-contract.js +1 -1
  60. package/dist/src/session.js +3 -0
  61. package/dist/src/source-files.js +5 -9
  62. package/dist/src/suppress-inline.js +33 -81
  63. package/dist/src/suppress.js +30 -32
  64. package/dist/src/test-support.js +4 -2
  65. package/dist/src/verbs/analyze.js +13 -1
  66. package/dist/src/verbs/describe.js +5 -3
  67. package/jsr.json +1 -1
  68. package/package.json +3 -3
  69. package/src/alias-chain.ts +2 -2
  70. package/src/analysis.ts +77 -53
  71. package/src/automatic-types.ts +1 -1
  72. package/src/commands/read.ts +6 -8
  73. package/src/conformance-record.ts +1 -1
  74. package/src/consumers.ts +5 -1
  75. package/src/contract/config.generated.ts +1 -1
  76. package/src/contract/contract.generated.ts +139 -2
  77. package/src/dependencies.ts +98 -50
  78. package/src/entries/build-scripts.ts +292 -0
  79. package/src/{configuration-files.ts → entries/configuration-files.ts} +23 -75
  80. package/src/{convention-rows.ts → entries/convention-rows.ts} +167 -28
  81. package/src/{conventions.ts → entries/conventions.ts} +347 -522
  82. package/src/{html-entries.ts → entries/html-entries.ts} +5 -5
  83. package/src/{mdx-entries.ts → entries/mdx-entries.ts} +4 -4
  84. package/src/entries/run-directories.ts +108 -0
  85. package/src/entries/sources.ts +266 -0
  86. package/src/entries/static-module.ts +857 -0
  87. package/src/entries/test-runners.ts +868 -0
  88. package/src/{workflow-steps.ts → entries/workflow-steps.ts} +29 -11
  89. package/src/exempt.ts +3 -2
  90. package/src/extensions.ts +45 -0
  91. package/src/facts.ts +44 -0
  92. package/src/file-facts.ts +2 -2
  93. package/src/findings/emitters.ts +14 -10
  94. package/src/findings/self-check.ts +41 -22
  95. package/src/findings/visibility-narrowing.ts +1 -1
  96. package/src/findings-pass.ts +5 -12
  97. package/src/framework-lifecycle.ts +1 -1
  98. package/src/fs/manifest.ts +25 -21
  99. package/src/generated-file.ts +240 -36
  100. package/src/graph.ts +3 -6
  101. package/src/index.ts +1 -1
  102. package/src/injection-container.ts +1 -1
  103. package/src/inventory/commonjs.ts +696 -0
  104. package/src/inventory/enumerate.ts +203 -0
  105. package/src/inventory/syntax.ts +311 -0
  106. package/src/inventory.ts +424 -878
  107. package/src/javascript-file.ts +29 -12
  108. package/src/load/classify.ts +49 -13
  109. package/src/load/diagnostics.ts +11 -1
  110. package/src/load/discover.ts +28 -12
  111. package/src/load/package-scope.ts +74 -0
  112. package/src/load/plan.ts +11 -2
  113. package/src/load/synthesize.ts +6 -6
  114. package/src/load/test-globals.ts +1 -1
  115. package/src/load/workspace-manifests.ts +1 -1
  116. package/src/load/workspace-run.ts +1 -1
  117. package/src/matrix.ts +7 -14
  118. package/src/outside-tests.ts +1 -1
  119. package/src/paths.ts +18 -0
  120. package/src/platform-calls.ts +119 -0
  121. package/src/platform-symbols.ts +205 -0
  122. package/src/position.ts +42 -0
  123. package/src/ref.ts +11 -20
  124. package/src/references.ts +383 -73
  125. package/src/report.ts +1 -1
  126. package/src/resolve.ts +1 -1
  127. package/src/{configured-declarations.ts → roots/configured-declarations.ts} +7 -7
  128. package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +5 -8
  129. package/src/{manifest.ts → roots/manifest.ts} +5 -5
  130. package/src/roots/published-reach.ts +481 -0
  131. package/src/{roots.ts → roots/root-set.ts} +203 -630
  132. package/src/roots/unseen-loads.ts +307 -0
  133. package/src/serialization-contract.ts +1 -1
  134. package/src/session.ts +17 -1
  135. package/src/source-files.ts +5 -10
  136. package/src/suppress-inline.ts +53 -89
  137. package/src/suppress.ts +48 -33
  138. package/src/test-support.ts +8 -3
  139. package/src/verbs/analyze.ts +17 -4
  140. package/src/verbs/describe.ts +5 -2
  141. package/src/verbs/explain.ts +1 -1
  142. package/dist/src/build-scripts.js +0 -418
  143. package/dist/src/conventions.js +0 -907
  144. package/dist/src/entry-points.js +0 -513
  145. package/dist/src/roots.js +0 -865
  146. package/dist/src/unseen-loads.js +0 -100
  147. package/src/build-scripts.ts +0 -536
  148. package/src/entry-points.ts +0 -661
  149. package/src/unseen-loads.ts +0 -134
@@ -1,683 +1,123 @@
1
- import { isArrayBindingPattern, isBindingElement, isClassDeclaration, isComputedPropertyName, isConstructorDeclaration, isEnumDeclaration, isEnumMember, isExportAssignment, isExportDeclaration, isFunctionDeclaration, isGetAccessorDeclaration, isIdentifier, isInterfaceDeclaration, isIntersectionTypeNode, isMethodDeclaration, isMethodSignatureDeclaration, isModuleBlock, isModuleDeclaration, isNamespaceExport, isObjectBindingPattern, isParameterDeclaration, isParenthesizedTypeNode, isPrivateIdentifier, isPropertyDeclaration, isPropertySignatureDeclaration, isSetAccessorDeclaration, isStringLiteral, isTypeAliasDeclaration, isTypeLiteralNode, isUnionTypeNode, isVariableStatement, ModifierFlags, } from "@typescript/native/ast";
2
- import { dirnamePath, joinPath, relativePath } from "./paths.js";
3
- import { byPosition, positionKey, renderPosition, writtenLength, } from "./position.js";
4
- import { computedComponent, nameComponent, renderRef } from "./ref.js";
5
- import { isAnswered, UNANSWERED } from "./query.js";
6
- import { manifestAt } from "./fs/manifest.js";
1
+ import { isClassDeclaration, isComputedPropertyName, isConstructorDeclaration, isEnumMember, isFunctionDeclaration, isGetAccessorDeclaration, isIdentifier, isInterfaceDeclaration, isMethodDeclaration, isMethodSignatureDeclaration, isParameterDeclaration, isPrivateIdentifier, isPropertyDeclaration, isPropertySignatureDeclaration, isSetAccessorDeclaration, isTypeAliasDeclaration, ModifierFlags, SyntaxKind, } from "@typescript/native/ast";
2
+ import { byPosition, positionKey, renderPosition, startLine } from "./position.js";
3
+ import { renderRef, spell } from "./ref.js";
4
+ import { signatureOf } from "./signatures.js";
7
5
  /**
8
- * Resolves each file's package scope: the name of the nearest manifest at or above
9
- * its directory up to the target root, and the file's path below that manifest.
10
- * Where no manifest up to the root carries a name, the package is a single dot and
11
- * the path is read from the root, which cannot collide, because a package name never
12
- * starts with a dot.
13
- *
14
- * One directory is read at most once, because a package holds many files and a
15
- * manifest read is a filesystem call.
16
- */
17
- export function packageScope(host, root) {
18
- const named = new Map();
19
- const manifestName = (dir) => {
20
- const name = manifestAt(host, joinPath(root, dir))?.["name"];
21
- return typeof name === "string" && name !== "" ? name : undefined;
22
- };
23
- const nearest = (dir) => {
24
- if (named.has(dir)) {
25
- return named.get(dir);
26
- }
27
- const name = manifestName(dir);
28
- const found = name !== undefined ? { name, at: dir } : dir === "" ? undefined : nearest(parentOf(dir));
29
- named.set(dir, found);
30
- return found;
31
- };
32
- return (path) => {
33
- const found = nearest(parentOf(path));
34
- if (found === undefined) {
35
- return { package: ".", path };
36
- }
37
- return { package: found.name, path: found.at === "" ? path : path.slice(found.at.length + 1) };
38
- };
39
- }
40
- /** The directory one path below the root sits in, and the root itself as the empty path. */
41
- function parentOf(path) {
42
- const dir = dirnamePath(path);
43
- return dir === "." ? "" : dir;
44
- }
45
- /** The modifier flags one node carries, and zero for a node that carries none. */
46
- function modifiersOf(node) {
47
- return node.modifierFlags ?? 0;
48
- }
49
- /** Whether a declaration is ambient: in a declaration file, or under a `declare` modifier. */
50
- function isAmbient(file, node) {
51
- if (file.isDeclarationFile) {
52
- return true;
53
- }
54
- for (let at = node; at !== undefined; at = at.parent) {
55
- if ((modifiersOf(at) & ModifierFlags.Ambient) !== 0) {
56
- return true;
57
- }
58
- }
59
- return false;
60
- }
61
- /** The type parameters one node declares, and none for a node that declares none. */
62
- function typeParametersOf(node) {
63
- if (isFunctionDeclaration(node) ||
64
- isMethodDeclaration(node) ||
65
- isMethodSignatureDeclaration(node) ||
66
- isClassDeclaration(node) ||
67
- isInterfaceDeclaration(node) ||
68
- isTypeAliasDeclaration(node)) {
69
- return node.typeParameters ?? [];
70
- }
71
- return [];
72
- }
73
- /**
74
- * The reference component one declaration name node spells, and undefined for a
75
- * name the grammar does not represent: a binding pattern, and a computed key whose
76
- * source text carries a closing bracket.
77
- */
78
- function componentOf(name) {
79
- if (name === undefined) {
80
- return undefined;
81
- }
82
- if (isComputedPropertyName(name)) {
83
- const text = name.expression.getText();
84
- return text.includes("]") ? undefined : { text, computed: true };
85
- }
86
- if (isIdentifier(name) || isPrivateIdentifier(name)) {
87
- return { text: name.getText(), computed: false };
88
- }
89
- if (isStringOrNumericName(name)) {
90
- // A string or numeric key's own text carries its quotes and its numeric
91
- // spelling; the reference carries the value, which the component then quotes.
92
- return { text: literalTextOf(name), computed: false };
93
- }
94
- return undefined;
95
- }
96
- function isStringOrNumericName(name) {
97
- const text = name.getText();
98
- return (text.startsWith('"') || text.startsWith("'") || text.startsWith("`") || /^[0-9]/u.test(text));
99
- }
100
- /** One member's identity inside its container: its component, and its side of a class. */
101
- function memberKey(component, isStatic) {
102
- return `${component.computed ? "[" : ""}${component.text}${isStatic ? ":static" : ""}`;
103
- }
104
- /** The type one node is, through however many parentheses are written around it. */
105
- function unparenthesizedType(type) {
106
- let held = type;
107
- while (isParenthesizedTypeNode(held)) {
108
- held = held.type;
109
- }
110
- return held;
111
- }
112
- /**
113
- * The direct constituents of one type. Parentheses are syntax and are not a
114
- * constituent, so a parenthesized type contributes the constituents it holds: the
115
- * constituents of `(A | B) & C` are `A`, `B` and `C`. A union or an intersection
116
- * written without them stays one constituent.
117
- */
118
- function constituentsOf(type) {
119
- const held = unparenthesizedType(type);
120
- if (!isUnionTypeNode(held) && !isIntersectionTypeNode(held)) {
121
- return [held];
122
- }
123
- return held.types.flatMap((member) => isParenthesizedTypeNode(member) ? constituentsOf(member) : [member]);
124
- }
125
- /**
126
- * The members one type alias declares: the members of the object type it writes, and
127
- * of every object type that is a direct constituent of a union or an intersection it
128
- * writes. A constituent naming another type declares nothing here, because that
129
- * declaration carries its own members.
130
- *
131
- * A name two constituents both declare is left out of all of them. The alias stands
132
- * for such a member, because the two declarations are one member of the type and
133
- * neither of them is the one a reference to it would name.
134
- */
135
- function aliasMembers(type) {
136
- const literals = constituentsOf(type).filter(isTypeLiteralNode);
137
- if (literals.length < 2) {
138
- return literals[0]?.members ?? [];
139
- }
140
- const declaring = new Map();
141
- for (const literal of literals) {
142
- for (const key of new Set(literal.members.map(keyOf))) {
143
- if (key !== undefined) {
144
- declaring.set(key, (declaring.get(key) ?? 0) + 1);
145
- }
146
- }
147
- }
148
- return literals.flatMap((literal) => literal.members.filter((member) => {
149
- const key = keyOf(member);
150
- return key !== undefined && declaring.get(key) === 1;
151
- }));
152
- }
153
- /** One type member's identity, and undefined for a member the grammar cannot name. */
154
- function keyOf(member) {
155
- const component = componentOf(nameNodeOf(member));
156
- return component === undefined ? undefined : memberKey(component, false);
157
- }
158
- /** The value one string or numeric key denotes, which is what the reference names. */
159
- function literalTextOf(name) {
160
- const text = name.getText();
161
- if (text.startsWith('"') || text.startsWith("'") || text.startsWith("`")) {
162
- return text.slice(1, -1);
163
- }
164
- return text;
165
- }
166
- /**
167
- * The component one declaration's reference carries, and its declared name where the
168
- * two differ.
169
- *
170
- * A declaration its module exports as the default is named `default`, because that is
171
- * its export name and a module has at most one, so the spelling is unambiguous and it
172
- * holds for an anonymous declaration too. Its declared name is a fact about the source
173
- * rather than about the export, so it travels beside the component as the display name.
174
- */
175
- function exportedAs(node, name) {
176
- const declared = componentOf(name);
177
- if ((modifiersOf(node) & ModifierFlags.Default) === 0) {
178
- return { component: declared, localName: undefined };
179
- }
180
- return { component: { text: "default", computed: false }, localName: declared?.text };
181
- }
182
- /** The visibility one member declaration's modifiers and name give it. */
183
- function visibilityOf(node, name) {
184
- if (name !== undefined && isPrivateIdentifier(name)) {
185
- return "private-name";
186
- }
187
- const flags = modifiersOf(node);
188
- if ((flags & ModifierFlags.Private) !== 0) {
189
- return "private";
190
- }
191
- if ((flags & ModifierFlags.Protected) !== 0) {
192
- return "protected";
193
- }
194
- return "public";
195
- }
196
- /**
197
- * The binding elements of a destructuring declaration that each declare a name of their
198
- * own, at any depth, but one with a default, whose removal skips the default's
199
- * evaluation, and one beside a rest element of its object pattern, whose removal
200
- * changes what the rest holds.
201
- */
202
- function patternElements(name) {
203
- if (!isObjectBindingPattern(name) && !isArrayBindingPattern(name)) {
204
- return [];
205
- }
206
- const rest = isObjectBindingPattern(name) &&
207
- name.elements.some((element) => element.dotDotDotToken !== undefined);
208
- return name.elements.flatMap((element) => {
209
- if (!isBindingElement(element) ||
210
- element.initializer !== undefined ||
211
- (rest && element.dotDotDotToken === undefined)) {
212
- return [];
213
- }
214
- const bound = element.name;
215
- if (bound === undefined) {
216
- return [];
217
- }
218
- return isIdentifier(bound) ? [{ element, name: bound }] : patternElements(bound);
219
- });
220
- }
221
- /** The name node of one declaration, for the kinds that carry one. */
222
- function nameNodeOf(node) {
223
- return node.name;
224
- }
225
- /**
226
- * The fragment component one declaration's name spells in a reference, as the inventory
227
- * spells it, or undefined for a name the grammar does not represent.
228
- */
229
- export function declaredComponent(node) {
230
- const component = componentOf(nameNodeOf(node));
231
- if (component === undefined) {
232
- return undefined;
233
- }
234
- return component.computed ? computedComponent(component.text) : nameComponent(component.text);
235
- }
236
- /**
237
- * One node's identity inside one program, which is how two readings of that program
238
- * name one declaration.
239
- *
240
- * The two mechanisms behind the member set resolve a member separately and meet here;
241
- * so does the reference pass, which starts from a resolved symbol's declaration and
242
- * needs the declaration this enumeration made for it.
6
+ * One node's identity inside one program, which is how two readings of that program name
7
+ * one declaration: the binder's table and the tree meet here, and so does the reference
8
+ * pass, which starts from a resolved symbol's declaration.
243
9
  */
244
10
  export function nodeKey(file, node) {
245
11
  return `${file.fileName}:${String(node.pos)}:${String(node.end)}:${String(node.kind)}`;
246
12
  }
247
- /** Whether `node` is written inside `declaration`, compared by file and range. */
248
- function writtenInside(declaration, node) {
249
- return (node.getSourceFile().fileName === declaration.getSourceFile().fileName &&
250
- declaration.pos <= node.pos &&
251
- node.end <= declaration.end);
252
- }
253
- /** `export default`, up to the keyword that stands where an unnamed default export's name would. */
254
- const DEFAULT_EXPORT = /^export\s+(?=default\b)/u;
255
- /**
256
- * Where an unnamed declaration is placed: at `default` when it is the default export, so it
257
- * never shares the file's own position, else at its first token.
258
- */
259
- function unnamedStart(node) {
260
- const start = node.getStart();
261
- const prefix = DEFAULT_EXPORT.exec(node.getText());
262
- return prefix === null ? start : start + prefix[0].length;
263
- }
264
13
  /**
265
14
  * Whether one node is a parameter property: a constructor parameter carrying an
266
- * accessibility, `readonly` or `override` modifier, which also declares an instance
267
- * member of the class, with the parameter's node as the member's declaration.
15
+ * accessibility, `readonly` or `override` modifier, which also declares an instance member
16
+ * of the class, with the parameter's node as the member's declaration.
268
17
  */
269
18
  export function isParameterProperty(node) {
270
19
  return (isParameterDeclaration(node) &&
271
20
  isConstructorDeclaration(node.parent) &&
272
21
  (modifiersOf(node) & ModifierFlags.ParameterPropertyModifier) !== 0);
273
22
  }
274
- /** The parameter properties every constructor of one class declares. */
275
- function parameterPropertiesOf(node) {
276
- return node.members.flatMap((member) => isConstructorDeclaration(member) ? member.parameters.filter(isParameterProperty) : []);
277
- }
278
- /** The kind a class element takes: a method, or a property or accessor. */
279
- function classMemberKind(node) {
280
- return isMethodDeclaration(node) ? "method" : "class-member";
281
- }
282
- /** The kind a type element takes: a method of an interface, or any other member. */
283
- function typeMemberKind(node, inInterface) {
284
- return inInterface && isMethodSignatureDeclaration(node) ? "interface-method" : "type-member";
23
+ /**
24
+ * The fragment component one declaration's name spells in a reference, as the inventory
25
+ * spells it, or undefined for a name the grammar does not represent.
26
+ */
27
+ export function declaredComponent(node) {
28
+ const component = componentOf(nameNodeOf(node));
29
+ return component === undefined ? undefined : spell(component);
285
30
  }
286
31
  /**
287
- * Every declaration one project's own files hold.
288
- *
289
- * `targetRoot` is the absolute path every position is rendered against; a file the
290
- * program holds that is not below it ends the enumeration rather than being passed
291
- * over. `host` is the filesystem the manifests that name each file's package are
292
- * read through, and nothing else is read from it.
293
- *
294
- * The order is by rendered position, so two enumerations of one project return the
295
- * same list whatever order the program reported its files in.
32
+ * Collects one project's declarations, from every {@link DeclarationSource} and from the
33
+ * binder's tables, and renders them. A member is kept once per container, by its
34
+ * component and its side of a class, whichever mechanism names it first.
296
35
  */
297
- export function inventory(project, host, targetRoot) {
298
- const files = project.ownSourceFiles();
299
- const ownFiles = new Set(files.map((file) => file.fileName));
300
- const moduleOf = packageScope(host, targetRoot);
301
- const building = [];
302
- const byNode = new Map();
303
- const exportScopes = [];
304
- const containers = [];
305
- const modules = [];
306
- const keep = (record) => {
307
- building.push(record);
308
- byNode.set(nodeKey(record.file, record.node), record);
309
- return record;
310
- };
311
- for (const file of files) {
312
- const path = relativePath(targetRoot, file.fileName);
313
- if (path === undefined) {
314
- // renderPosition states the refusal, with the root it was rendered against.
315
- renderPosition(file, targetRoot, 0);
316
- continue;
317
- }
318
- const module = moduleOf(path);
319
- const fileRecord = keep({
320
- file,
321
- node: file,
36
+ export class Builder {
37
+ #targetRoot;
38
+ #records = [];
39
+ #byNode = new Map();
40
+ #exportScopes = [];
41
+ #containers = [];
42
+ #modules = [];
43
+ #unansweredOwners = new Set();
44
+ #openTables = new Set();
45
+ constructor(targetRoot) {
46
+ this.#targetRoot = targetRoot;
47
+ }
48
+ get exportScopes() {
49
+ return this.#exportScopes;
50
+ }
51
+ get containers() {
52
+ return this.#containers;
53
+ }
54
+ /** One file's own record, which is also the export scope of its module. */
55
+ file(file, module, path) {
56
+ const record = this.#keep(file, file, 0, {
322
57
  module,
323
- position: renderPosition(file, targetRoot, 0),
324
- endLine: renderPosition(file, targetRoot, Math.max(file.end - 1, 0)).line,
325
58
  kind: "file",
326
59
  parentChain: [],
327
- // A file's reference names its module, so no component of it is ever spelled;
328
- // the path is what a text line renders for it.
60
+ // A file's reference names its module, so no component of it is ever spelled; the
61
+ // path is what a text line renders for it.
329
62
  component: { text: path, computed: false },
330
63
  localName: path,
331
64
  parent: undefined,
332
- visibility: "public",
333
- static: false,
334
- exported: false,
335
- typeParameter: undefined,
336
65
  });
337
- exportScopes.push({ owner: fileRecord, nameNode: file });
338
- // A statement a component file's module appends is not written in the file.
339
- const written = writtenLength(file);
340
- walkStatements(file.statements.filter((statement) => statement.end <= written), fileRecord);
341
- }
342
- /** One declaration of a module, a namespace or a container body. */
343
- function walkStatements(statements, owner) {
344
- for (const statement of statements) {
345
- walkStatement(statement, owner);
346
- }
66
+ this.#exportScopes.push({ owner: record, nameNode: file });
67
+ return record;
347
68
  }
348
- function declare(node, owner, kind, name, named) {
349
- const record = keep({
350
- file: owner.file,
351
- node,
69
+ /** One declaration written under `owner`, placed at its name, and its type parameters. */
70
+ declare(node, owner, kind, name, named) {
71
+ const record = this.#keep(owner.file, node, name?.getStart() ?? unnamedStart(node), {
352
72
  module: owner.module,
353
- position: renderPosition(owner.file, targetRoot, name?.getStart() ?? unnamedStart(node)),
354
- endLine: renderPosition(owner.file, targetRoot, Math.max(node.end - 1, 0)).line,
355
73
  kind,
356
74
  parentChain: owner.kind === "file" ? [] : chainOf(owner),
357
- component: named.component,
358
- localName: named.localName,
75
+ ...named,
359
76
  parent: owner,
360
77
  visibility: visibilityOf(node, name),
361
78
  static: (modifiersOf(node) & ModifierFlags.Static) !== 0,
362
- exported: false,
363
- typeParameter: undefined,
364
79
  });
365
- declareTypeParameters(node, record);
80
+ this.#declareTypeParameters(node, record);
366
81
  return record;
367
82
  }
368
- function declareTypeParameters(node, owner) {
369
- for (const parameter of typeParametersOf(node)) {
370
- const name = nameNodeOf(parameter);
371
- if (name === undefined) {
372
- continue;
373
- }
374
- keep({
375
- file: owner.file,
376
- node: parameter,
377
- module: owner.module,
378
- position: renderPosition(owner.file, targetRoot, name.getStart()),
379
- endLine: renderPosition(owner.file, targetRoot, Math.max(parameter.end - 1, 0)).line,
380
- kind: "type-parameter",
381
- parentChain: owner.parentChain,
382
- component: owner.component,
383
- localName: undefined,
384
- parent: owner,
385
- visibility: "public",
386
- static: owner.static,
387
- exported: false,
388
- typeParameter: name.getText(),
389
- });
390
- }
391
- }
392
- function walkStatement(node, owner) {
393
- if (isFunctionDeclaration(node)) {
394
- const named = exportedAs(node, node.name);
395
- if (named.component !== undefined) {
396
- declare(node, owner, "function", node.name, { ...named, component: named.component });
397
- }
398
- return;
399
- }
400
- if (isClassDeclaration(node)) {
401
- const named = exportedAs(node, node.name);
402
- if (named.component === undefined) {
403
- return;
404
- }
405
- const record = declare(node, owner, "class", node.name, {
406
- ...named,
407
- component: named.component,
408
- });
409
- containers.push({
410
- owner: record,
411
- declaration: node,
412
- nameNode: node.name,
413
- members: [...node.members, ...parameterPropertiesOf(node)],
414
- memberKind: "class-member",
415
- isClass: true,
416
- membersAreExports: false,
417
- });
418
- return;
419
- }
420
- if (isInterfaceDeclaration(node)) {
421
- const named = exportedAs(node, node.name);
422
- if (named.component === undefined) {
423
- return;
424
- }
425
- const record = declare(node, owner, "interface", node.name, {
426
- ...named,
427
- component: named.component,
428
- });
429
- containers.push({
430
- owner: record,
431
- declaration: node,
432
- nameNode: node.name,
433
- members: node.members,
434
- memberKind: "interface-method",
435
- isClass: false,
436
- membersAreExports: false,
437
- });
438
- return;
439
- }
440
- if (isTypeAliasDeclaration(node)) {
441
- const named = exportedAs(node, node.name);
442
- if (named.component === undefined) {
443
- return;
444
- }
445
- const record = declare(node, owner, "type", node.name, {
446
- ...named,
447
- component: named.component,
448
- });
449
- // A type alias keeps no member table of its own: the members belong to the
450
- // object types its declaration writes, so the declaration is what carries them.
451
- const members = aliasMembers(node.type);
452
- if (members.length > 0) {
453
- containers.push({
454
- owner: record,
455
- declaration: node,
456
- nameNode: undefined,
457
- members,
458
- memberKind: "type-member",
459
- isClass: false,
460
- membersAreExports: false,
461
- });
462
- }
463
- return;
464
- }
465
- if (isEnumDeclaration(node)) {
466
- const named = exportedAs(node, node.name);
467
- if (named.component === undefined) {
468
- return;
469
- }
470
- const record = declare(node, owner, "enum", node.name, {
471
- ...named,
472
- component: named.component,
473
- });
474
- containers.push({
475
- owner: record,
476
- declaration: node,
477
- nameNode: node.name,
478
- members: node.members,
479
- memberKind: "enum-member",
480
- isClass: false,
481
- membersAreExports: true,
482
- });
83
+ /** A container of members; a class's constructors are where its parts are bound. */
84
+ container(container) {
85
+ this.#containers.push(container);
86
+ if (container.memberKind !== "class-member") {
483
87
  return;
484
88
  }
485
- if (isModuleDeclaration(node)) {
486
- const component = componentOf(node.name);
487
- if (component === undefined) {
488
- return;
489
- }
490
- const record = declare(node, owner, "namespace", node.name, {
491
- component,
492
- localName: undefined,
493
- });
494
- exportScopes.push({ owner: record, nameNode: node.name });
495
- if (isStringLiteral(node.name)) {
496
- modules.push([record, node.name.text]);
497
- }
498
- const body = node.body;
499
- if (body === undefined) {
500
- return;
89
+ for (const member of container.members.filter(isConstructorDeclaration)) {
90
+ for (const at of [member, ...member.parameters.filter((one) => !isParameterProperty(one))]) {
91
+ container.owner.partLines.add(startLine(container.owner.file, at.pos));
501
92
  }
502
- if (isModuleBlock(body)) {
503
- walkStatements(body.statements, record);
504
- return;
505
- }
506
- walkStatement(body, record);
507
- return;
508
- }
509
- if (isVariableStatement(node)) {
510
- for (const declaration of node.declarationList.declarations) {
511
- const component = componentOf(declaration.name);
512
- if (component !== undefined) {
513
- declare(declaration, owner, "variable", declaration.name, {
514
- component,
515
- localName: undefined,
516
- });
517
- continue;
518
- }
519
- for (const { element, name } of patternElements(declaration.name)) {
520
- declare(element, owner, "variable", name, {
521
- component: { text: name.text, computed: false },
522
- localName: undefined,
523
- });
524
- }
525
- }
526
- return;
527
- }
528
- if (isExportDeclaration(node)) {
529
- walkExportDeclaration(node, owner);
530
- return;
531
- }
532
- if (isExportAssignment(node) && !node.isExportEquals) {
533
- declare(node, owner, "export-alias", undefined, {
534
- component: { text: "default", computed: false },
535
- localName: undefined,
536
- });
537
93
  }
538
94
  }
539
- function walkExportDeclaration(node, owner) {
540
- const clause = node.exportClause;
541
- // A bare star re-export declares nothing: the names it carries are the named module's.
542
- if (clause === undefined) {
543
- return;
544
- }
545
- if (isNamespaceExport(clause)) {
546
- const component = componentOf(clause.name);
547
- if (component !== undefined) {
548
- declare(clause, owner, "export-alias", clause.name, { component, localName: undefined });
549
- }
550
- return;
551
- }
552
- for (const element of clause.elements ?? []) {
553
- const name = nameNodeOf(element);
554
- const component = componentOf(name);
555
- if (component !== undefined) {
556
- declare(element, owner, "export-alias", name, { component, localName: undefined });
557
- }
558
- }
95
+ /** A namespace whose export table says which of its declarations it exports. */
96
+ exportScope(owner, nameNode) {
97
+ this.#exportScopes.push({ owner, nameNode });
559
98
  }
560
- // One batch for every module and every container the walk found, because a
561
- // checker accessor with an array overload costs one round trip for the batch and
562
- // one per node without it.
563
- const batched = [
564
- ...exportScopes.map((scope) => scope.nameNode),
565
- ...containers.map((container) => container.nameNode).filter((node) => node !== undefined),
566
- ];
567
- const handles = batched.map((node) => project.handle(node));
568
- const resolved = project.symbolsAt(handles);
569
- const symbolOf = new Map();
570
- // A scope or a container whose symbol or table went unanswered keeps what its syntax
571
- // declares, and everything under it is recorded unanswered.
572
- const unansweredOwners = new Set();
573
- batched.forEach((node, index) => {
574
- const symbol = resolved[index];
575
- if (typeof symbol === "object") {
576
- symbolOf.set(node, symbol);
577
- }
578
- });
579
- resolved.slice(0, exportScopes.length).forEach((symbol, index) => {
580
- const scope = exportScopes[index];
581
- if (symbol === UNANSWERED && scope !== undefined) {
582
- unansweredOwners.add(scope.owner);
583
- }
584
- });
585
- containers
586
- .filter((container) => container.nameNode !== undefined)
587
- .forEach((container, index) => {
588
- if (resolved[exportScopes.length + index] === UNANSWERED) {
589
- unansweredOwners.add(container.owner);
590
- }
591
- });
592
- let exportTables = 0;
593
- for (const scope of exportScopes) {
594
- const symbol = symbolOf.get(scope.nameNode);
595
- if (symbol === undefined) {
596
- continue;
597
- }
598
- exportTables += 1;
599
- const table = project.queries.exportsOf(symbol);
600
- if (!isAnswered(table)) {
601
- unansweredOwners.add(scope.owner);
602
- }
603
- for (const [, exported] of isAnswered(table) ? table : []) {
604
- for (const declaration of exported.declarations) {
605
- const held = project.declarationAt(declaration);
606
- if (held === undefined) {
607
- continue;
608
- }
609
- const record = byNode.get(nodeKey(held.node.getSourceFile(), held.node));
610
- if (record === undefined) {
611
- continue;
612
- }
613
- record.exported = true;
614
- }
615
- }
99
+ /** A `declare module` declaration named by a string literal. */
100
+ module(record, name) {
101
+ this.#modules.push([record, name]);
616
102
  }
617
- let memberTables = 0;
618
- for (const container of containers) {
619
- const symbol = container.nameNode === undefined ? undefined : symbolOf.get(container.nameNode);
620
- const seen = new Map();
621
- if (symbol !== undefined) {
622
- if (!container.membersAreExports) {
623
- memberTables += 1;
624
- const table = project.queries.membersOf(symbol);
625
- if (isAnswered(table)) {
626
- readTable(container, table, seen, false);
627
- }
628
- else {
629
- unansweredOwners.add(container.owner);
630
- }
631
- }
632
- if (container.membersAreExports || container.isClass) {
633
- memberTables += 1;
634
- const table = project.queries.exportsOf(symbol);
635
- if (isAnswered(table)) {
636
- readTable(container, table, seen, container.isClass);
637
- }
638
- else {
639
- unansweredOwners.add(container.owner);
640
- }
641
- }
642
- }
643
- completeFromTree(container, seen);
103
+ /** The declaration enumerated at one node, where one is. */
104
+ recordAt(node) {
105
+ return this.#byNode.get(nodeKey(node.getSourceFile(), node));
644
106
  }
645
- /**
646
- * One container's members as its symbol table holds them, each positioned at the
647
- * declaration the table names. A member with no declaration is the language's own
648
- * (a class's `prototype`), and a member declared outside the target's own files
649
- * belongs to another program; neither is a declaration of the target.
650
- */
651
- function readTable(container, table, seen, areStatic) {
652
- for (const [, member] of table) {
653
- const declarations = member.declarations
654
- .map((handle) => project.declarationAt(handle))
655
- .filter((held) => held !== undefined);
656
- if (declarations.length === 0) {
657
- continue;
658
- }
659
- const own = declarations.filter((held) => ownFiles.has(held.node.getSourceFile().fileName));
660
- if (own.length === 0) {
661
- continue;
662
- }
663
- // A merged symbol's table holds the members every one of its declarations writes,
664
- // and each member is kept once, by the declaration that writes it.
665
- for (const held of own.filter((one) => writtenInside(container.declaration, one.node))) {
666
- keepMember(container, held.node, seen, areStatic);
667
- }
668
- }
107
+ /** Records that a table of `owner` is unknown, which leaves everything under it unknown. */
108
+ unanswered(owner) {
109
+ this.#unansweredOwners.add(owner);
110
+ }
111
+ /** Records that one file writes its exports in a form its table does not model. */
112
+ openTable(file) {
113
+ this.#openTables.add(file);
669
114
  }
670
115
  /**
671
- * One container's members as its own declaration writes them, for the members its
672
- * symbol table does not hold: a member whose key is computed is bound to the
673
- * container's type later than the table is built.
116
+ * One member of a container. A getter and a setter of one name are one symbol and one
117
+ * reference, so the second declaration widens the first record rather than making a
118
+ * second, and both nodes name that record.
674
119
  */
675
- function completeFromTree(container, seen) {
676
- for (const member of container.members) {
677
- keepMember(container, member, seen, (modifiersOf(member) & ModifierFlags.Static) !== 0);
678
- }
679
- }
680
- function keepMember(container, node, seen, areStatic) {
120
+ member(container, node, seen, areStatic) {
681
121
  const kind = memberKindOf(container, node);
682
122
  if (kind === undefined) {
683
123
  return;
@@ -690,61 +130,234 @@ export function inventory(project, host, targetRoot) {
690
130
  const file = node.getSourceFile();
691
131
  const key = memberKey(component, areStatic);
692
132
  const held = seen.get(key);
693
- const endLine = renderPosition(file, targetRoot, Math.max(node.end - 1, 0)).line;
133
+ const endLine = this.#endOf(file, node);
694
134
  if (held !== undefined) {
695
- // A getter and a setter of one name are one symbol and one reference, so the
696
- // second declaration widens the first record rather than making a second. Both
697
- // nodes name that record, so a reading that starts from either one finds it.
698
135
  held.endLine = Math.max(held.endLine, endLine);
699
- byNode.set(nodeKey(file, node), held);
136
+ this.#byNode.set(nodeKey(file, node), held);
137
+ placeAt(held, node);
700
138
  return;
701
139
  }
702
- const at = name ?? node;
703
- const record = keep({
704
- file,
705
- node,
140
+ const record = this.#keep(file, node, (name ?? node).getStart(), {
706
141
  module: container.owner.module,
707
- position: renderPosition(file, targetRoot, at.getStart()),
708
- endLine,
709
142
  kind,
710
143
  parentChain: chainOf(container.owner),
711
144
  component,
712
- localName: undefined,
713
145
  parent: container.owner,
714
146
  visibility: visibilityOf(node, name),
715
147
  static: areStatic,
716
- exported: false,
717
- typeParameter: undefined,
148
+ holder: isParameterProperty(node) ? container.owner : undefined,
718
149
  });
719
150
  seen.set(key, record);
720
- declareTypeParameters(node, record);
721
- }
722
- function memberKindOf(container, node) {
723
- if (container.isClass) {
724
- if (isPropertyDeclaration(node) ||
725
- isMethodDeclaration(node) ||
726
- isGetAccessorDeclaration(node) ||
727
- isSetAccessorDeclaration(node) ||
728
- isParameterProperty(node)) {
729
- return classMemberKind(node);
151
+ this.#declareTypeParameters(node, record);
152
+ }
153
+ /** The inventory of everything collected. */
154
+ build(cost) {
155
+ const byId = new Map(this.#records.map((record) => [positionKey(record.position), record]));
156
+ const symbols = this.#records.map(render).sort((a, b) => byPosition(a.position, b.position));
157
+ return {
158
+ symbols,
159
+ declarations: new Map([...this.#byNode].map(([key, record]) => [key, positionKey(record.position)])),
160
+ cost,
161
+ unanswered: symbols
162
+ .filter((symbol) => underUnanswered(byId.get(symbol.id), this.#unansweredOwners))
163
+ .map((symbol) => symbol.id),
164
+ byName: namesOf(this.#records),
165
+ modules: new Map(this.#modules.map(([record, name]) => [positionKey(record.position), name])),
166
+ ambient: new Set(this.#records
167
+ .filter((record) => isAmbient(record.file, record.node))
168
+ .map((record) => positionKey(record.position))),
169
+ dependents: dependentsOf(this.#containers, (node) => this.recordAt(node)),
170
+ placement: placementOf(this.#records),
171
+ openTables: new Set([...this.#openTables].map((file) => positionKey(file.position))),
172
+ };
173
+ }
174
+ #keep(file, node, at, fields) {
175
+ const kept = {
176
+ file,
177
+ node,
178
+ position: renderPosition(file, this.#targetRoot, at),
179
+ endLine: this.#endOf(file, node),
180
+ localName: undefined,
181
+ visibility: "public",
182
+ static: false,
183
+ exported: false,
184
+ typeParameter: undefined,
185
+ holder: undefined,
186
+ ...fields,
187
+ startLines: new Set(),
188
+ partLines: new Set(),
189
+ };
190
+ this.#records.push(kept);
191
+ this.#byNode.set(nodeKey(kept.file, kept.node), kept);
192
+ if (kept.kind !== "file") {
193
+ placeAt(kept, kept.node);
194
+ }
195
+ return kept;
196
+ }
197
+ #declareTypeParameters(node, owner) {
198
+ for (const parameter of typeParametersOf(node)) {
199
+ const name = nameNodeOf(parameter);
200
+ if (name === undefined) {
201
+ continue;
730
202
  }
731
- return undefined;
732
- }
733
- if (container.memberKind === "enum-member") {
734
- return isEnumMember(node) ? "enum-member" : undefined;
203
+ this.#keep(owner.file, parameter, name.getStart(), {
204
+ module: owner.module,
205
+ kind: "type-parameter",
206
+ parentChain: owner.parentChain,
207
+ component: owner.component,
208
+ parent: owner,
209
+ static: owner.static,
210
+ typeParameter: name.getText(),
211
+ });
735
212
  }
736
- if (isPropertySignatureDeclaration(node) || isMethodSignatureDeclaration(node)) {
737
- return typeMemberKind(node, container.memberKind === "interface-method");
213
+ }
214
+ #endOf(file, node) {
215
+ return renderPosition(file, this.#targetRoot, Math.max(node.end - 1, 0)).line;
216
+ }
217
+ }
218
+ /** The lines one declaring node binds its declaration on. */
219
+ function placeAt(record, node) {
220
+ const file = record.file;
221
+ record.startLines.add(startLine(file, node.pos));
222
+ for (const parameter of signatureOf(node)?.parameters ?? []) {
223
+ record.startLines.add(startLine(file, parameter.pos));
224
+ }
225
+ const list = node.parent;
226
+ if (node.kind === SyntaxKind.VariableDeclaration &&
227
+ list?.kind === SyntaxKind.VariableDeclarationList &&
228
+ list.parent.kind === SyntaxKind.VariableStatement) {
229
+ record.startLines.add(startLine(file, list.parent.pos));
230
+ }
231
+ }
232
+ /** The modifier flags one node carries, and zero for a node that carries none. */
233
+ export function modifiersOf(node) {
234
+ return node.modifierFlags ?? 0;
235
+ }
236
+ /** The name node of one declaration, for the kinds that carry one. */
237
+ export function nameNodeOf(node) {
238
+ return node.name;
239
+ }
240
+ /**
241
+ * The reference component one declaration name node spells, and undefined for a name the
242
+ * grammar does not represent: a binding pattern, and a computed key whose source text
243
+ * carries a closing bracket.
244
+ */
245
+ export function componentOf(name) {
246
+ if (name === undefined) {
247
+ return undefined;
248
+ }
249
+ if (isComputedPropertyName(name)) {
250
+ const text = name.expression.getText();
251
+ return text.includes("]") ? undefined : { text, computed: true };
252
+ }
253
+ if (isIdentifier(name) || isPrivateIdentifier(name)) {
254
+ return { text: name.getText(), computed: false };
255
+ }
256
+ const text = name.getText();
257
+ if (text.startsWith('"') || text.startsWith("'") || text.startsWith("`")) {
258
+ // A string key's own text carries its quotes; the reference carries the value.
259
+ return { text: text.slice(1, -1), computed: false };
260
+ }
261
+ return /^[0-9]/u.test(text) ? { text, computed: false } : undefined;
262
+ }
263
+ /**
264
+ * The component one declaration's reference carries, and its declared name where the two
265
+ * differ. A declaration its module exports as the default is named `default`, its export
266
+ * name, which a module has at most one of and an anonymous declaration has too.
267
+ */
268
+ export function exportedAs(node, name) {
269
+ const declared = componentOf(name);
270
+ if ((modifiersOf(node) & ModifierFlags.Default) === 0) {
271
+ return { component: declared, localName: undefined };
272
+ }
273
+ return { component: { text: "default", computed: false }, localName: declared?.text };
274
+ }
275
+ /** One member's identity inside its container: its component, and its side of a class. */
276
+ export function memberKey(component, isStatic) {
277
+ return `${component.computed ? "[" : ""}${component.text}${isStatic ? ":static" : ""}`;
278
+ }
279
+ /** The visibility one member declaration's modifiers and name give it. */
280
+ function visibilityOf(node, name) {
281
+ if (name !== undefined && isPrivateIdentifier(name)) {
282
+ return "private-name";
283
+ }
284
+ const flags = modifiersOf(node);
285
+ if ((flags & ModifierFlags.Private) !== 0) {
286
+ return "private";
287
+ }
288
+ if ((flags & ModifierFlags.Protected) !== 0) {
289
+ return "protected";
290
+ }
291
+ return "public";
292
+ }
293
+ /** Whether a declaration is ambient: in a declaration file, or under a `declare` modifier. */
294
+ export function isAmbient(file, node) {
295
+ if (file.isDeclarationFile) {
296
+ return true;
297
+ }
298
+ for (let at = node; at !== undefined; at = at.parent) {
299
+ if ((modifiersOf(at) & ModifierFlags.Ambient) !== 0) {
300
+ return true;
738
301
  }
739
- if (isGetAccessorDeclaration(node) || isSetAccessorDeclaration(node)) {
740
- return "type-member";
302
+ }
303
+ return false;
304
+ }
305
+ /** The type parameters one node declares, and none for a node that declares none. */
306
+ function typeParametersOf(node) {
307
+ if (isFunctionDeclaration(node) ||
308
+ isMethodDeclaration(node) ||
309
+ isMethodSignatureDeclaration(node) ||
310
+ isClassDeclaration(node) ||
311
+ isInterfaceDeclaration(node) ||
312
+ isTypeAliasDeclaration(node)) {
313
+ return node.typeParameters ?? [];
314
+ }
315
+ return [];
316
+ }
317
+ /** `export default`, up to the keyword that stands where an unnamed default export's name would. */
318
+ const DEFAULT_EXPORT = /^export\s+(?=default\b)/u;
319
+ /**
320
+ * Where an unnamed declaration is placed: at `default` when it is the default export, so it
321
+ * never shares the file's own position, else at its first token.
322
+ */
323
+ function unnamedStart(node) {
324
+ const start = node.getStart();
325
+ const prefix = DEFAULT_EXPORT.exec(node.getText());
326
+ return prefix === null ? start : start + prefix[0].length;
327
+ }
328
+ /** The kind one member of a container takes, and undefined for a node that is no member. */
329
+ function memberKindOf(container, node) {
330
+ if (container.memberKind === "class-member") {
331
+ if (isPropertyDeclaration(node) ||
332
+ isGetAccessorDeclaration(node) ||
333
+ isSetAccessorDeclaration(node) ||
334
+ isParameterProperty(node)) {
335
+ return "class-member";
741
336
  }
742
- return undefined;
337
+ return isMethodDeclaration(node) ? "method" : undefined;
338
+ }
339
+ if (container.memberKind === "enum-member") {
340
+ return isEnumMember(node) ? "enum-member" : undefined;
743
341
  }
342
+ if (isMethodSignatureDeclaration(node)) {
343
+ return container.memberKind === "interface-method" ? "interface-method" : "type-member";
344
+ }
345
+ if (isPropertySignatureDeclaration(node) ||
346
+ isGetAccessorDeclaration(node) ||
347
+ isSetAccessorDeclaration(node)) {
348
+ return "type-member";
349
+ }
350
+ return undefined;
351
+ }
352
+ /**
353
+ * Each enum member's dependents: the later members of its declaration with no initializer,
354
+ * up to the first one with an initializer.
355
+ */
356
+ function dependentsOf(containers, recordAt) {
744
357
  const dependents = new Map();
745
358
  for (const container of containers.filter((one) => one.memberKind === "enum-member")) {
746
359
  const ids = container.members.map((member) => {
747
- const record = byNode.get(nodeKey(member.getSourceFile(), member));
360
+ const record = recordAt(member);
748
361
  return record === undefined ? undefined : positionKey(record.position);
749
362
  });
750
363
  container.members.forEach((_member, index) => {
@@ -765,22 +378,21 @@ export function inventory(project, host, targetRoot) {
765
378
  }
766
379
  });
767
380
  }
768
- const byId = new Map(building.map((record) => [positionKey(record.position), record]));
769
- const symbols = building.map(render).sort((a, b) => byPosition(a.position, b.position));
770
- return {
771
- symbols,
772
- declarations: new Map([...byNode].map(([key, record]) => [key, positionKey(record.position)])),
773
- cost: { batches: 1, exportTables, memberTables },
774
- unanswered: symbols
775
- .filter((symbol) => underUnanswered(byId.get(symbol.id), unansweredOwners))
776
- .map((symbol) => symbol.id),
777
- byName: namesOf(building),
778
- modules: new Map(modules.map(([record, name]) => [positionKey(record.position), name])),
779
- ambient: new Set(building
780
- .filter((record) => isAmbient(record.file, record.node))
781
- .map((record) => positionKey(record.position))),
782
- dependents,
783
- };
381
+ return dependents;
382
+ }
383
+ /** Each declaration but a file to where a directive binds it. */
384
+ function placementOf(records) {
385
+ const ascending = (lines) => [...lines].sort((a, b) => a - b);
386
+ return new Map(records
387
+ .filter((record) => record.kind !== "file")
388
+ .map((record) => [
389
+ positionKey(record.position),
390
+ {
391
+ startLines: ascending(record.startLines),
392
+ holder: positionKey((record.holder ?? record).position),
393
+ partLines: ascending(record.partLines),
394
+ },
395
+ ]));
784
396
  }
785
397
  /** Whether one declaration is, or is declared under, a scope or container left unanswered. */
786
398
  function underUnanswered(record, owners) {
@@ -792,9 +404,9 @@ function underUnanswered(record, owners) {
792
404
  return false;
793
405
  }
794
406
  /** Every declaration but a file, by each name a use may spell it with. */
795
- function namesOf(building) {
407
+ function namesOf(records) {
796
408
  const byName = new Map();
797
- for (const record of building) {
409
+ for (const record of records) {
798
410
  if (record.kind === "file") {
799
411
  continue;
800
412
  }
@@ -823,21 +435,17 @@ function chainOf(record) {
823
435
  }
824
436
  /**
825
437
  * One declaration as the inventory reports it. The display name is the chain in the
826
- * spelling a reference gives it, except where the declaration carries a name of its
827
- * own the reference does not: a file, whose reference names its module, and a default
828
- * export, whose reference carries its export name.
438
+ * spelling a reference gives it, except where the declaration carries a name of its own
439
+ * the reference does not: a file, whose reference names its module, and a default export.
829
440
  */
830
441
  function render(record) {
831
442
  const chain = chainOf(record);
832
- const spelled = chain
833
- .map((component) => component.computed ? computedComponent(component.text) : nameComponent(component.text))
834
- .join(".");
835
- const name = record.localName ??
836
- (record.typeParameter === undefined ? spelled : `${spelled}<${record.typeParameter}>`);
443
+ const spelled = chain.map(spell).join(".");
837
444
  return {
838
445
  id: positionKey(record.position),
839
446
  ref: refOf(record, chain),
840
- name,
447
+ name: record.localName ??
448
+ (record.typeParameter === undefined ? spelled : `${spelled}<${record.typeParameter}>`),
841
449
  kind: record.kind,
842
450
  position: record.position,
843
451
  endLine: record.endLine,
@@ -866,11 +474,3 @@ function refOf(record, chain) {
866
474
  ...(record.typeParameter === undefined ? {} : { typeParameter: record.typeParameter }),
867
475
  });
868
476
  }
869
- /**
870
- * Every declaration of an inventory other than a file, by each name a use may spell it
871
- * with. It is how a reference the checker could not resolve is still counted, as a
872
- * reference to every declaration that bears the name it spells.
873
- */
874
- export function declarationsByName(held) {
875
- return held.byName;
876
- }