@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.
- package/dist/src/alias-chain.js +2 -2
- package/dist/src/analysis.js +43 -38
- package/dist/src/commands/read.js +6 -7
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/consumers.js +4 -2
- package/dist/src/contract/config.generated.js +1 -1
- package/dist/src/contract/contract.generated.js +120 -2
- package/dist/src/dependencies.js +81 -49
- package/dist/src/entries/build-scripts.js +222 -0
- package/dist/src/{configuration-files.js → entries/configuration-files.js} +20 -58
- package/dist/src/{convention-rows.js → entries/convention-rows.js} +120 -27
- package/dist/src/entries/conventions.js +739 -0
- package/dist/src/{html-entries.js → entries/html-entries.js} +3 -3
- package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +2 -2
- package/dist/src/entries/run-directories.js +77 -0
- package/dist/src/entries/sources.js +144 -0
- package/dist/src/entries/static-module.js +669 -0
- package/dist/src/entries/test-runners.js +684 -0
- package/dist/src/{workflow-steps.js → entries/workflow-steps.js} +18 -8
- package/dist/src/extensions.js +37 -0
- package/dist/src/facts.js +38 -0
- package/dist/src/file-facts.js +1 -1
- package/dist/src/findings/emitters.js +5 -8
- package/dist/src/findings/self-check.js +36 -20
- package/dist/src/findings-pass.js +5 -6
- package/dist/src/framework-lifecycle.js +1 -1
- package/dist/src/fs/manifest.js +15 -20
- package/dist/src/generated-file.js +172 -29
- package/dist/src/graph.js +2 -5
- package/dist/src/index.js +1 -1
- package/dist/src/injection-container.js +1 -1
- package/dist/src/inventory/commonjs.js +507 -0
- package/dist/src/inventory/enumerate.js +167 -0
- package/dist/src/inventory/syntax.js +236 -0
- package/dist/src/inventory.js +305 -705
- package/dist/src/javascript-file.js +26 -11
- package/dist/src/load/classify.js +28 -9
- package/dist/src/load/diagnostics.js +8 -1
- package/dist/src/load/discover.js +16 -8
- package/dist/src/load/package-scope.js +56 -0
- package/dist/src/load/plan.js +2 -2
- package/dist/src/load/synthesize.js +5 -5
- package/dist/src/load/workspace-manifests.js +1 -1
- package/dist/src/matrix.js +6 -13
- package/dist/src/outside-tests.js +1 -1
- package/dist/src/paths.js +16 -0
- package/dist/src/platform-calls.js +78 -0
- package/dist/src/platform-symbols.js +140 -0
- package/dist/src/position.js +41 -0
- package/dist/src/ref.js +11 -20
- package/dist/src/references.js +281 -50
- package/dist/src/resolve.js +1 -1
- package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +4 -4
- package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +5 -8
- package/dist/src/{manifest.js → roots/manifest.js} +4 -4
- package/dist/src/roots/published-reach.js +412 -0
- package/dist/src/roots/root-set.js +489 -0
- package/dist/src/roots/unseen-loads.js +208 -0
- package/dist/src/serialization-contract.js +1 -1
- package/dist/src/session.js +3 -0
- package/dist/src/source-files.js +5 -9
- package/dist/src/suppress-inline.js +33 -81
- package/dist/src/suppress.js +30 -32
- package/dist/src/test-support.js +4 -2
- package/dist/src/verbs/analyze.js +13 -1
- package/dist/src/verbs/describe.js +5 -3
- package/jsr.json +1 -1
- package/package.json +3 -3
- package/src/alias-chain.ts +2 -2
- package/src/analysis.ts +77 -53
- package/src/automatic-types.ts +1 -1
- package/src/commands/read.ts +6 -8
- package/src/conformance-record.ts +1 -1
- package/src/consumers.ts +5 -1
- package/src/contract/config.generated.ts +1 -1
- package/src/contract/contract.generated.ts +139 -2
- package/src/dependencies.ts +98 -50
- package/src/entries/build-scripts.ts +292 -0
- package/src/{configuration-files.ts → entries/configuration-files.ts} +23 -75
- package/src/{convention-rows.ts → entries/convention-rows.ts} +167 -28
- package/src/{conventions.ts → entries/conventions.ts} +347 -522
- package/src/{html-entries.ts → entries/html-entries.ts} +5 -5
- package/src/{mdx-entries.ts → entries/mdx-entries.ts} +4 -4
- package/src/entries/run-directories.ts +108 -0
- package/src/entries/sources.ts +266 -0
- package/src/entries/static-module.ts +857 -0
- package/src/entries/test-runners.ts +868 -0
- package/src/{workflow-steps.ts → entries/workflow-steps.ts} +29 -11
- package/src/exempt.ts +3 -2
- package/src/extensions.ts +45 -0
- package/src/facts.ts +44 -0
- package/src/file-facts.ts +2 -2
- package/src/findings/emitters.ts +14 -10
- package/src/findings/self-check.ts +41 -22
- package/src/findings/visibility-narrowing.ts +1 -1
- package/src/findings-pass.ts +5 -12
- package/src/framework-lifecycle.ts +1 -1
- package/src/fs/manifest.ts +25 -21
- package/src/generated-file.ts +240 -36
- package/src/graph.ts +3 -6
- package/src/index.ts +1 -1
- package/src/injection-container.ts +1 -1
- package/src/inventory/commonjs.ts +696 -0
- package/src/inventory/enumerate.ts +203 -0
- package/src/inventory/syntax.ts +311 -0
- package/src/inventory.ts +424 -878
- package/src/javascript-file.ts +29 -12
- package/src/load/classify.ts +49 -13
- package/src/load/diagnostics.ts +11 -1
- package/src/load/discover.ts +28 -12
- package/src/load/package-scope.ts +74 -0
- package/src/load/plan.ts +11 -2
- package/src/load/synthesize.ts +6 -6
- package/src/load/test-globals.ts +1 -1
- package/src/load/workspace-manifests.ts +1 -1
- package/src/load/workspace-run.ts +1 -1
- package/src/matrix.ts +7 -14
- package/src/outside-tests.ts +1 -1
- package/src/paths.ts +18 -0
- package/src/platform-calls.ts +119 -0
- package/src/platform-symbols.ts +205 -0
- package/src/position.ts +42 -0
- package/src/ref.ts +11 -20
- package/src/references.ts +383 -73
- package/src/report.ts +1 -1
- package/src/resolve.ts +1 -1
- package/src/{configured-declarations.ts → roots/configured-declarations.ts} +7 -7
- package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +5 -8
- package/src/{manifest.ts → roots/manifest.ts} +5 -5
- package/src/roots/published-reach.ts +481 -0
- package/src/{roots.ts → roots/root-set.ts} +203 -630
- package/src/roots/unseen-loads.ts +307 -0
- package/src/serialization-contract.ts +1 -1
- package/src/session.ts +17 -1
- package/src/source-files.ts +5 -10
- package/src/suppress-inline.ts +53 -89
- package/src/suppress.ts +48 -33
- package/src/test-support.ts +8 -3
- package/src/verbs/analyze.ts +17 -4
- package/src/verbs/describe.ts +5 -2
- package/src/verbs/explain.ts +1 -1
- package/dist/src/build-scripts.js +0 -418
- package/dist/src/conventions.js +0 -907
- package/dist/src/entry-points.js +0 -513
- package/dist/src/roots.js +0 -865
- package/dist/src/unseen-loads.js +0 -100
- package/src/build-scripts.ts +0 -536
- package/src/entry-points.ts +0 -661
- package/src/unseen-loads.ts +0 -134
package/dist/src/inventory.js
CHANGED
|
@@ -1,683 +1,123 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
-
*
|
|
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
|
-
/**
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
return
|
|
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
|
-
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
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
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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
|
-
//
|
|
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:
|
|
338
|
-
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
|
|
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
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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
|
-
|
|
486
|
-
const
|
|
487
|
-
|
|
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
|
-
|
|
540
|
-
|
|
541
|
-
|
|
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
|
-
|
|
561
|
-
|
|
562
|
-
|
|
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
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
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
|
|
672
|
-
*
|
|
673
|
-
*
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
717
|
-
typeParameter: undefined,
|
|
148
|
+
holder: isParameterProperty(node) ? container.owner : undefined,
|
|
718
149
|
});
|
|
719
150
|
seen.set(key, record);
|
|
720
|
-
declareTypeParameters(node, record);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
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
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
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
|
-
|
|
737
|
-
|
|
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
|
-
|
|
740
|
-
|
|
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 =
|
|
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
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
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(
|
|
407
|
+
function namesOf(records) {
|
|
796
408
|
const byName = new Map();
|
|
797
|
-
for (const record of
|
|
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
|
-
*
|
|
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
|
-
}
|