@crouton-kit/tsym 0.1.0
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/LICENSE +21 -0
- package/README.md +51 -0
- package/bin/tsym.js +2 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +89 -0
- package/dist/commands/handlers/check.d.ts +3 -0
- package/dist/commands/handlers/check.js +94 -0
- package/dist/commands/handlers/find.d.ts +3 -0
- package/dist/commands/handlers/find.js +56 -0
- package/dist/commands/handlers/inspect.d.ts +3 -0
- package/dist/commands/handlers/inspect.js +81 -0
- package/dist/commands/handlers/outline.d.ts +3 -0
- package/dist/commands/handlers/outline.js +101 -0
- package/dist/commands/handlers/query.d.ts +3 -0
- package/dist/commands/handlers/query.js +196 -0
- package/dist/commands/handlers/rename.d.ts +3 -0
- package/dist/commands/handlers/rename.js +53 -0
- package/dist/commands/handlers/search.d.ts +5 -0
- package/dist/commands/handlers/search.js +32 -0
- package/dist/commands/handlers/server-reload.d.ts +3 -0
- package/dist/commands/handlers/server-reload.js +7 -0
- package/dist/commands/handlers/server-status.d.ts +3 -0
- package/dist/commands/handlers/server-status.js +7 -0
- package/dist/commands/handlers/server-stop.d.ts +3 -0
- package/dist/commands/handlers/server-stop.js +7 -0
- package/dist/commands/handlers/show.d.ts +3 -0
- package/dist/commands/handlers/show.js +63 -0
- package/dist/commands/handlers/type.d.ts +3 -0
- package/dist/commands/handlers/type.js +45 -0
- package/dist/commands/handlers.d.ts +12 -0
- package/dist/commands/handlers.js +44 -0
- package/dist/commands/help.d.ts +2 -0
- package/dist/commands/help.js +27 -0
- package/dist/commands/rename-apply.d.ts +37 -0
- package/dist/commands/rename-apply.js +115 -0
- package/dist/commands/tree.d.ts +9 -0
- package/dist/commands/tree.js +277 -0
- package/dist/commands/types.d.ts +80 -0
- package/dist/commands/types.js +1 -0
- package/dist/core/declarations.d.ts +48 -0
- package/dist/core/declarations.js +153 -0
- package/dist/core/receipt.d.ts +7 -0
- package/dist/core/receipt.js +9 -0
- package/dist/core/search.d.ts +29 -0
- package/dist/core/search.js +47 -0
- package/dist/core/source.d.ts +32 -0
- package/dist/core/source.js +96 -0
- package/dist/core/workspace.d.ts +28 -0
- package/dist/core/workspace.js +137 -0
- package/dist/index/build.d.ts +22 -0
- package/dist/index/build.js +79 -0
- package/dist/index/classify.d.ts +20 -0
- package/dist/index/classify.js +60 -0
- package/dist/index/commits.d.ts +15 -0
- package/dist/index/commits.js +201 -0
- package/dist/index/descriptor.d.ts +19 -0
- package/dist/index/descriptor.js +295 -0
- package/dist/index/graph.d.ts +82 -0
- package/dist/index/graph.js +1 -0
- package/dist/index/heritage.d.ts +15 -0
- package/dist/index/heritage.js +122 -0
- package/dist/index/refresh.d.ts +28 -0
- package/dist/index/refresh.js +110 -0
- package/dist/index/scope.d.ts +4 -0
- package/dist/index/scope.js +21 -0
- package/dist/index/state.d.ts +22 -0
- package/dist/index/state.js +63 -0
- package/dist/index/walk.d.ts +23 -0
- package/dist/index/walk.js +550 -0
- package/dist/index/watch.d.ts +18 -0
- package/dist/index/watch.js +175 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/output.d.ts +12 -0
- package/dist/output.js +54 -0
- package/dist/read/read.d.ts +32 -0
- package/dist/read/read.js +89 -0
- package/dist/relations/graph.d.ts +23 -0
- package/dist/relations/graph.js +115 -0
- package/dist/relations/mermaid.d.ts +3 -0
- package/dist/relations/mermaid.js +37 -0
- package/dist/relations/store-relations.d.ts +20 -0
- package/dist/relations/store-relations.js +158 -0
- package/dist/relations/types.d.ts +58 -0
- package/dist/relations/types.js +12 -0
- package/dist/server/client.d.ts +5 -0
- package/dist/server/client.js +215 -0
- package/dist/server/context.d.ts +21 -0
- package/dist/server/context.js +12 -0
- package/dist/server/lifecycle.d.ts +28 -0
- package/dist/server/lifecycle.js +62 -0
- package/dist/server/main.d.ts +1 -0
- package/dist/server/main.js +541 -0
- package/dist/server/projects.d.ts +21 -0
- package/dist/server/projects.js +28 -0
- package/dist/server/wire.d.ts +39 -0
- package/dist/server/wire.js +19 -0
- package/dist/store/load.d.ts +67 -0
- package/dist/store/load.js +193 -0
- package/dist/store/meta.d.ts +28 -0
- package/dist/store/meta.js +137 -0
- package/dist/store/query.d.ts +5 -0
- package/dist/store/query.js +4 -0
- package/dist/store/schema.d.ts +4 -0
- package/dist/store/schema.js +19 -0
- package/dist/store/store.d.ts +36 -0
- package/dist/store/store.js +240 -0
- package/dist/ts7/api.d.ts +35 -0
- package/dist/ts7/api.js +164 -0
- package/dist/ts7/ast.d.ts +1 -0
- package/dist/ts7/ast.js +1 -0
- package/dist/ts7/checker.d.ts +55 -0
- package/dist/ts7/checker.js +383 -0
- package/dist/ts7/projects.d.ts +27 -0
- package/dist/ts7/projects.js +94 -0
- package/package.json +47 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { SymbolFlags } from 'typescript/unstable/sync';
|
|
3
|
+
import * as ts from '../ts7/ast.js';
|
|
4
|
+
import { declarationTarget, moduleTarget, relative } from './classify.js';
|
|
5
|
+
import { describeFile } from './descriptor.js';
|
|
6
|
+
import { isIndexedDeclaration } from './scope.js';
|
|
7
|
+
const walkStates = new WeakMap();
|
|
8
|
+
/** Returns the snapshot-backed file state created by walkIndex for heritage enrichment. */
|
|
9
|
+
export function walkFileStates(graph) {
|
|
10
|
+
const states = walkStates.get(graph);
|
|
11
|
+
if (!states)
|
|
12
|
+
throw new Error('Heritage enrichment requires the graph returned by walkIndex.');
|
|
13
|
+
return states;
|
|
14
|
+
}
|
|
15
|
+
export function walkIndex(root, program, projects) {
|
|
16
|
+
const states = statesFor(root, program, projects.records());
|
|
17
|
+
const lookup = {
|
|
18
|
+
root,
|
|
19
|
+
indexedByStart: new Map(states.map((state) => [state.file, state.byStart])),
|
|
20
|
+
modulesByFile: new Map(states.map((state) => [state.file, state.indexed.find((descriptor) => descriptor.scope === 'module').key])),
|
|
21
|
+
};
|
|
22
|
+
const declarations = new Map();
|
|
23
|
+
const externals = new Map();
|
|
24
|
+
const fileRelationships = new Map();
|
|
25
|
+
let internalSites = 0;
|
|
26
|
+
let unresolvedOccurrences = 0;
|
|
27
|
+
const addExternal = (node) => { externals.set(node.key, node); };
|
|
28
|
+
const addTarget = (target, unresolved) => {
|
|
29
|
+
if (target === null) {
|
|
30
|
+
internalSites++;
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
if (unresolved) {
|
|
34
|
+
unresolvedOccurrences++;
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
if (target.external)
|
|
38
|
+
addExternal(target.node);
|
|
39
|
+
return target.key;
|
|
40
|
+
};
|
|
41
|
+
for (const state of states) {
|
|
42
|
+
const relationships = [];
|
|
43
|
+
fileRelationships.set(state.file, relationships);
|
|
44
|
+
for (const descriptor of state.indexed) {
|
|
45
|
+
const existing = declarations.get(descriptor.key);
|
|
46
|
+
if (existing) {
|
|
47
|
+
existing.declCount++;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const declaration = declarationNode(state, descriptor);
|
|
51
|
+
declarations.set(declaration.key, declaration);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const { symbols: symbolTargets, definitions } = declarationSymbolTargets(states);
|
|
55
|
+
const resolvedTargets = new Map();
|
|
56
|
+
for (const state of states) {
|
|
57
|
+
const relationships = fileRelationships.get(state.file);
|
|
58
|
+
const added = new Set();
|
|
59
|
+
const add = (relationship) => {
|
|
60
|
+
const site = `${relationship.type}:${relationship.from}:${relationship.to}:${'line' in relationship ? relationship.line : ''}:${'col' in relationship ? relationship.col : ''}:${'name' in relationship ? relationship.name : ''}`;
|
|
61
|
+
if (!added.has(site)) {
|
|
62
|
+
added.add(site);
|
|
63
|
+
relationships.push(relationship);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
for (const descriptor of state.indexed) {
|
|
67
|
+
const declaration = declarations.get(descriptor.key);
|
|
68
|
+
if (declaration.container)
|
|
69
|
+
add({ type: 'CONTAINS', from: declaration.container, to: declaration.key });
|
|
70
|
+
}
|
|
71
|
+
const definitionTargets = definitions.get(state.file) ?? new Map();
|
|
72
|
+
const positions = [...new Set(state.semanticNodes.map((node) => node.getStart(state.source)).filter((position) => !definitionTargets.has(position) || state.typeIdentifierPositions.has(position)))];
|
|
73
|
+
const symbols = state.checker.getSymbolAtPosition(state.absolute, positions);
|
|
74
|
+
const symbolsByPosition = new Map(positions.map((position, index) => [position, symbols[index]]));
|
|
75
|
+
const symbolFor = (node) => symbolsByPosition.get(node.getStart(state.source));
|
|
76
|
+
const seenReferences = new Set();
|
|
77
|
+
const callTargets = new Map();
|
|
78
|
+
const seenTypes = new Set();
|
|
79
|
+
const seenUnresolved = new Set();
|
|
80
|
+
const aliases = new Map();
|
|
81
|
+
const alias = (symbol) => {
|
|
82
|
+
const cached = aliases.get(symbol.id);
|
|
83
|
+
if (cached)
|
|
84
|
+
return cached;
|
|
85
|
+
const resolved = symbol.flags & SymbolFlags.Alias ? state.checker.getAliasedSymbol(symbol) : symbol;
|
|
86
|
+
aliases.set(symbol.id, resolved);
|
|
87
|
+
return resolved;
|
|
88
|
+
};
|
|
89
|
+
const externalModules = new Map();
|
|
90
|
+
state.source.forEachChild((node) => {
|
|
91
|
+
if (!ts.isImportDeclaration(node))
|
|
92
|
+
return;
|
|
93
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
94
|
+
const moduleText = ts.isStringLiteral(moduleSpecifier) ? moduleSpecifier.text : moduleSpecifier.getText(state.source);
|
|
95
|
+
const target = moduleTarget(symbolFor(moduleSpecifier), lookup, alias, moduleText);
|
|
96
|
+
if (!target?.external)
|
|
97
|
+
return;
|
|
98
|
+
for (const binding of importBindings(node)) {
|
|
99
|
+
const symbol = symbolFor(binding);
|
|
100
|
+
if (symbol)
|
|
101
|
+
externalModules.set(symbol.id, target.node.module);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
const targetFor = (symbol, position) => {
|
|
105
|
+
if (!symbol || state.checker.isUnknownSymbol(symbol)) {
|
|
106
|
+
if (!seenUnresolved.has(position)) {
|
|
107
|
+
seenUnresolved.add(position);
|
|
108
|
+
unresolvedOccurrences++;
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const resolved = alias(symbol);
|
|
113
|
+
if (symbolTargets.has(resolved.id)) {
|
|
114
|
+
const key = symbolTargets.get(resolved.id);
|
|
115
|
+
if (key === null) {
|
|
116
|
+
internalSites++;
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
return key;
|
|
120
|
+
}
|
|
121
|
+
const externalIdentity = externalModules.get(symbol.id);
|
|
122
|
+
const cacheKey = `${resolved.id}:${externalIdentity ?? ''}`;
|
|
123
|
+
const target = resolvedTargets.has(cacheKey) ? resolvedTargets.get(cacheKey) : declarationTarget(resolved, lookup, (candidate) => candidate, externalIdentity);
|
|
124
|
+
resolvedTargets.set(cacheKey, target);
|
|
125
|
+
return addTarget(target, false);
|
|
126
|
+
};
|
|
127
|
+
const definitionTarget = (position) => {
|
|
128
|
+
if (!definitionTargets.has(position))
|
|
129
|
+
return undefined;
|
|
130
|
+
const target = definitionTargets.get(position);
|
|
131
|
+
if (target === null)
|
|
132
|
+
internalSites++;
|
|
133
|
+
return target;
|
|
134
|
+
};
|
|
135
|
+
const ownerFor = (node) => ownerForNode(state, node).key;
|
|
136
|
+
const point = (node) => {
|
|
137
|
+
const value = state.source.getLineAndCharacterOfPosition(node.getStart(state.source));
|
|
138
|
+
return { line: value.line + 1, col: value.character + 1 };
|
|
139
|
+
};
|
|
140
|
+
const reference = (node, role, target) => {
|
|
141
|
+
const start = node.getStart(state.source);
|
|
142
|
+
if (seenReferences.has(start))
|
|
143
|
+
return;
|
|
144
|
+
seenReferences.add(start);
|
|
145
|
+
const resolved = typeof target === 'function' ? target() : target === undefined ? targetFor(symbolFor(node), start) : target;
|
|
146
|
+
if (!resolved)
|
|
147
|
+
return;
|
|
148
|
+
const site = point(node);
|
|
149
|
+
add({ type: 'REFERENCES', from: ownerFor(node), to: resolved, ...site, role });
|
|
150
|
+
};
|
|
151
|
+
const call = (node, callee) => {
|
|
152
|
+
const start = callee.getStart(state.source);
|
|
153
|
+
const target = targetFor(symbolFor(callee), start);
|
|
154
|
+
callTargets.set(start, target);
|
|
155
|
+
reference(callee, 'call', target);
|
|
156
|
+
if (!target)
|
|
157
|
+
return;
|
|
158
|
+
const site = point(callee);
|
|
159
|
+
add({ type: 'CALLS', from: ownerFor(callee), to: target, ...site, dispatch: false });
|
|
160
|
+
};
|
|
161
|
+
const typeMention = (node) => {
|
|
162
|
+
const start = node.getStart(state.source);
|
|
163
|
+
if (seenTypes.has(start))
|
|
164
|
+
return;
|
|
165
|
+
seenTypes.add(start);
|
|
166
|
+
const identifier = firstIdentifier(node);
|
|
167
|
+
if (!identifier)
|
|
168
|
+
return;
|
|
169
|
+
const target = targetFor(symbolFor(identifier), start);
|
|
170
|
+
if (!target)
|
|
171
|
+
return;
|
|
172
|
+
const site = point(node);
|
|
173
|
+
add({ type: 'TYPE_MENTION', from: ownerFor(node), to: target, ...site, position: typePosition(node) });
|
|
174
|
+
};
|
|
175
|
+
const visit = (node) => {
|
|
176
|
+
const callee = callCallee(node);
|
|
177
|
+
if (callee)
|
|
178
|
+
call(node, callee);
|
|
179
|
+
if (ts.isIdentifier(node)) {
|
|
180
|
+
const start = node.getStart(state.source);
|
|
181
|
+
reference(node, referenceRole(node, callee !== null), () => {
|
|
182
|
+
if (callTargets.has(start))
|
|
183
|
+
return callTargets.get(start);
|
|
184
|
+
const target = definitionTarget(start);
|
|
185
|
+
return target === undefined ? targetFor(symbolFor(node), start) : target;
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
if (isTypeNode(node))
|
|
189
|
+
typeMention(node);
|
|
190
|
+
if (ts.isImportDeclaration(node)) {
|
|
191
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
192
|
+
const moduleText = ts.isStringLiteral(moduleSpecifier) ? moduleSpecifier.text : moduleSpecifier.getText(state.source);
|
|
193
|
+
const target = moduleTarget(symbolFor(moduleSpecifier), lookup, alias, moduleText);
|
|
194
|
+
const key = addTarget(target, false);
|
|
195
|
+
if (key) {
|
|
196
|
+
const site = point(moduleSpecifier);
|
|
197
|
+
add({ type: 'IMPORTS', from: `${state.file}#<module>`, to: key, line: site.line, names: importNames(node, state.source) });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (ts.isExportDeclaration(node))
|
|
201
|
+
exportDeclaration(node, state, lookup, addTarget, add, alias);
|
|
202
|
+
node.forEachChild(visit);
|
|
203
|
+
};
|
|
204
|
+
state.source.forEachChild(visit);
|
|
205
|
+
directExports(state, add);
|
|
206
|
+
}
|
|
207
|
+
const files = new Map();
|
|
208
|
+
for (const state of states) {
|
|
209
|
+
const ids = [...new Set(state.indexed.map((descriptor) => descriptor.key))];
|
|
210
|
+
files.set(state.file, { declarations: ids, relationships: fileRelationships.get(state.file) });
|
|
211
|
+
}
|
|
212
|
+
const graph = { declarations, externals, files, relationships: [...fileRelationships.values()].flat(), internalSites, unresolvedOccurrences };
|
|
213
|
+
walkStates.set(graph, states);
|
|
214
|
+
return graph;
|
|
215
|
+
}
|
|
216
|
+
function statesFor(root, program, configured) {
|
|
217
|
+
const owner = new Map();
|
|
218
|
+
for (const project of configured)
|
|
219
|
+
for (const file of project.files)
|
|
220
|
+
if (!owner.has(file))
|
|
221
|
+
owner.set(file, project);
|
|
222
|
+
return program.withSnapshot((snapshot) => [...owner].sort(([left], [right]) => left.localeCompare(right)).flatMap(([absolute, configuredProject]) => {
|
|
223
|
+
const project = snapshot.getProject(configuredProject.config);
|
|
224
|
+
const source = project?.program.getSourceFile(absolute);
|
|
225
|
+
if (!source || !project)
|
|
226
|
+
return [];
|
|
227
|
+
const file = relative(root, absolute);
|
|
228
|
+
const descriptors = describeFile(file, source.text);
|
|
229
|
+
const indexed = descriptors.filter(isIndexedDeclaration);
|
|
230
|
+
const byStart = new Map(indexed.map((descriptor) => [descriptor.start, descriptor]));
|
|
231
|
+
const module = indexed.find((descriptor) => descriptor.scope === 'module');
|
|
232
|
+
if (!module)
|
|
233
|
+
throw new Error(`No module declaration exists for ${file}.`);
|
|
234
|
+
const nodesByStart = new Map();
|
|
235
|
+
const ownersByStart = new Map();
|
|
236
|
+
const semanticNodes = [];
|
|
237
|
+
const typeIdentifierPositions = new Set();
|
|
238
|
+
const collect = (node, parentOwner) => {
|
|
239
|
+
const start = node.getStart(source);
|
|
240
|
+
const nextOwner = byStart.get(start) ?? parentOwner;
|
|
241
|
+
if (!nodesByStart.has(start))
|
|
242
|
+
nodesByStart.set(start, node);
|
|
243
|
+
if (!ownersByStart.has(start))
|
|
244
|
+
ownersByStart.set(start, nextOwner);
|
|
245
|
+
if (ts.isIdentifier(node))
|
|
246
|
+
semanticNodes.push(node);
|
|
247
|
+
if (isTypeNode(node)) {
|
|
248
|
+
const identifier = firstIdentifier(node);
|
|
249
|
+
if (identifier)
|
|
250
|
+
typeIdentifierPositions.add(identifier.getStart(source));
|
|
251
|
+
}
|
|
252
|
+
if (ts.isImportDeclaration(node))
|
|
253
|
+
semanticNodes.push(node.moduleSpecifier);
|
|
254
|
+
node.forEachChild((child) => collect(child, nextOwner));
|
|
255
|
+
};
|
|
256
|
+
source.forEachChild((node) => collect(node, module));
|
|
257
|
+
return [{
|
|
258
|
+
absolute,
|
|
259
|
+
file,
|
|
260
|
+
source,
|
|
261
|
+
project,
|
|
262
|
+
checker: project.checker,
|
|
263
|
+
descriptors,
|
|
264
|
+
indexed,
|
|
265
|
+
byStart,
|
|
266
|
+
nodesByStart,
|
|
267
|
+
ownersByStart,
|
|
268
|
+
semanticNodes,
|
|
269
|
+
typeIdentifierPositions,
|
|
270
|
+
}];
|
|
271
|
+
}));
|
|
272
|
+
}
|
|
273
|
+
function ownerForNode(state, node) {
|
|
274
|
+
if (ts.isSourceFile(node))
|
|
275
|
+
return state.indexed.find((descriptor) => descriptor.scope === 'module');
|
|
276
|
+
const owner = state.ownersByStart.get(node.getStart(state.source));
|
|
277
|
+
if (owner && ownsSites(state, owner))
|
|
278
|
+
return owner;
|
|
279
|
+
for (let current = node.parent; current && !ts.isSourceFile(current); current = current.parent) {
|
|
280
|
+
const descriptor = state.byStart.get(current.getStart(state.source));
|
|
281
|
+
if (descriptor && ownsSites(state, descriptor))
|
|
282
|
+
return descriptor;
|
|
283
|
+
}
|
|
284
|
+
return state.indexed.find((descriptor) => descriptor.scope === 'module');
|
|
285
|
+
}
|
|
286
|
+
function ownsSites(state, descriptor) {
|
|
287
|
+
const node = state.nodesByStart.get(descriptor.start);
|
|
288
|
+
if (!node)
|
|
289
|
+
return true;
|
|
290
|
+
if (ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isClassExpression(node))
|
|
291
|
+
return false;
|
|
292
|
+
if (!(ts.isVariableDeclaration(node) || ts.isBindingElement(node) || ts.isPropertyDeclaration(node)))
|
|
293
|
+
return true;
|
|
294
|
+
const initializer = node.initializer;
|
|
295
|
+
return !!initializer && (ts.isFunctionExpression(initializer) || ts.isArrowFunction(initializer) || ts.isClassExpression(initializer));
|
|
296
|
+
}
|
|
297
|
+
function declarationSymbolTargets(states) {
|
|
298
|
+
const descriptorsByFileStart = new Map();
|
|
299
|
+
for (const state of states)
|
|
300
|
+
for (const descriptor of state.descriptors)
|
|
301
|
+
descriptorsByFileStart.set(`${path.resolve(state.absolute)}:${descriptor.start}`, { state, descriptor });
|
|
302
|
+
const symbolsById = new Map();
|
|
303
|
+
const definitions = new Map();
|
|
304
|
+
for (const state of states) {
|
|
305
|
+
const descriptors = state.descriptors.filter((descriptor) => descriptor.scope !== 'module');
|
|
306
|
+
const symbols = state.checker.getSymbolAtPosition(state.absolute, descriptors.map((descriptor) => descriptor.nameStart));
|
|
307
|
+
for (const symbol of symbols) {
|
|
308
|
+
if (!symbol)
|
|
309
|
+
continue;
|
|
310
|
+
const declarations = symbol.declarations.flatMap((handle) => {
|
|
311
|
+
const declaration = handle.resolve();
|
|
312
|
+
if (!declaration)
|
|
313
|
+
return [];
|
|
314
|
+
const file = path.resolve(declaration.getSourceFile().fileName);
|
|
315
|
+
const target = descriptorsByFileStart.get(`${file}:${declaration.getStart(declaration.getSourceFile())}`);
|
|
316
|
+
return target ? [target] : [];
|
|
317
|
+
});
|
|
318
|
+
if (declarations.length === 0)
|
|
319
|
+
continue;
|
|
320
|
+
const target = declarations.find(({ descriptor }) => isIndexedDeclaration(descriptor))?.descriptor.key ?? null;
|
|
321
|
+
symbolsById.set(symbol.id, target);
|
|
322
|
+
for (const { state: declarationState, descriptor } of declarations) {
|
|
323
|
+
const file = definitions.get(declarationState.file) ?? new Map();
|
|
324
|
+
file.set(descriptor.nameStart, target);
|
|
325
|
+
definitions.set(declarationState.file, file);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return { symbols: symbolsById, definitions };
|
|
330
|
+
}
|
|
331
|
+
function declarationNode(state, descriptor) {
|
|
332
|
+
const name = state.source.getLineAndCharacterOfPosition(descriptor.nameStart);
|
|
333
|
+
const start = state.source.getLineAndCharacterOfPosition(descriptor.start);
|
|
334
|
+
const end = state.source.getLineAndCharacterOfPosition(descriptor.end);
|
|
335
|
+
const declaration = state.nodesByStart.get(descriptor.start);
|
|
336
|
+
const kind = declarationKind(descriptor, declaration);
|
|
337
|
+
const container = descriptor.scope === 'module'
|
|
338
|
+
? null
|
|
339
|
+
: declaration
|
|
340
|
+
? parameterPropertyContainer(state, descriptor, declaration)
|
|
341
|
+
: `${state.file}#<module>`;
|
|
342
|
+
const dir = path.posix.dirname(state.file);
|
|
343
|
+
const segments = state.file.split('/');
|
|
344
|
+
return {
|
|
345
|
+
key: descriptor.key,
|
|
346
|
+
at: `${state.file}:${name.line + 1}:${name.character + 1}`,
|
|
347
|
+
name: descriptor.name,
|
|
348
|
+
qname: descriptor.path,
|
|
349
|
+
kind,
|
|
350
|
+
exported: descriptor.scope !== 'module' && !!declaration && isExported(declaration),
|
|
351
|
+
file: state.file,
|
|
352
|
+
dir: dir === '.' ? '' : dir,
|
|
353
|
+
top: segments.length >= 2 ? `${segments[0]}/${segments[1]}` : dir,
|
|
354
|
+
line: descriptor.scope === 'module' ? 0 : name.line + 1,
|
|
355
|
+
col: descriptor.scope === 'module' ? 0 : name.character + 1,
|
|
356
|
+
endLine: end.line + 1,
|
|
357
|
+
// The full declaration span (modifiers and decorators included): `show` recovers its first line as `endLine - lines + 1`.
|
|
358
|
+
lines: Math.max(1, end.line - start.line + 1),
|
|
359
|
+
declCount: 1,
|
|
360
|
+
container,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function declarationKind(descriptor, node) {
|
|
364
|
+
switch (descriptor.kind) {
|
|
365
|
+
case 'FunctionDeclaration': return 'function';
|
|
366
|
+
case 'ClassDeclaration':
|
|
367
|
+
case 'ClassExpression': return 'class';
|
|
368
|
+
case 'InterfaceDeclaration': return 'interface';
|
|
369
|
+
case 'TypeAliasDeclaration': return 'type';
|
|
370
|
+
case 'EnumDeclaration': return 'enum';
|
|
371
|
+
case 'ModuleDeclaration': return 'namespace';
|
|
372
|
+
case 'MethodDeclaration':
|
|
373
|
+
case 'MethodSignature': return 'method';
|
|
374
|
+
case 'Parameter':
|
|
375
|
+
case 'PropertyDeclaration':
|
|
376
|
+
case 'PropertySignature':
|
|
377
|
+
case 'EnumMember': return 'property';
|
|
378
|
+
case 'GetAccessor': return 'getter';
|
|
379
|
+
case 'SetAccessor': return 'setter';
|
|
380
|
+
case 'Constructor': return 'constructor';
|
|
381
|
+
case 'VariableDeclaration':
|
|
382
|
+
case 'BindingElement': return variableKind(node);
|
|
383
|
+
case 'FunctionExpression':
|
|
384
|
+
case 'ArrowFunction': return 'function';
|
|
385
|
+
default: return descriptor.scope === 'module' ? 'module' : 'property';
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
function parameterPropertyContainer(state, descriptor, declaration) {
|
|
389
|
+
if (descriptor.kind === 'Parameter' && ts.isParameterDeclaration(declaration) && ts.isConstructorDeclaration(declaration.parent) && declaration.parent.parent)
|
|
390
|
+
return ownerForNode(state, declaration.parent.parent).key;
|
|
391
|
+
return ownerForNode(state, declaration.parent).key;
|
|
392
|
+
}
|
|
393
|
+
function variableKind(node) {
|
|
394
|
+
for (let current = node; current; current = current.parent) {
|
|
395
|
+
if (!ts.isVariableDeclaration(current))
|
|
396
|
+
continue;
|
|
397
|
+
const parent = current.parent;
|
|
398
|
+
const flags = ts.isVariableDeclarationList(parent) ? parent.flags : 0;
|
|
399
|
+
if (flags & ts.NodeFlags.Const)
|
|
400
|
+
return 'const';
|
|
401
|
+
if (flags & ts.NodeFlags.Let)
|
|
402
|
+
return 'let';
|
|
403
|
+
return 'var';
|
|
404
|
+
}
|
|
405
|
+
return 'var';
|
|
406
|
+
}
|
|
407
|
+
function isExported(node) {
|
|
408
|
+
if (node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword))
|
|
409
|
+
return true;
|
|
410
|
+
if (ts.isExportAssignment(node))
|
|
411
|
+
return true;
|
|
412
|
+
// Only a variable's own statement can export it; a loop variable or a nested local inside an exported statement is not exported.
|
|
413
|
+
let current = node;
|
|
414
|
+
while (ts.isBindingElement(current) || ts.isObjectBindingPattern(current) || ts.isArrayBindingPattern(current) || ts.isVariableDeclaration(current))
|
|
415
|
+
current = current.parent;
|
|
416
|
+
const statement = ts.isVariableDeclarationList(current) ? current.parent : undefined;
|
|
417
|
+
return !!statement && ts.isVariableStatement(statement) && statement.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword) === true;
|
|
418
|
+
}
|
|
419
|
+
function callCallee(node) {
|
|
420
|
+
let expression;
|
|
421
|
+
if (ts.isCallExpression(node) || ts.isNewExpression(node))
|
|
422
|
+
expression = node.expression;
|
|
423
|
+
else if (ts.isTaggedTemplateExpression(node))
|
|
424
|
+
expression = node.tag;
|
|
425
|
+
else if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node))
|
|
426
|
+
expression = node.tagName;
|
|
427
|
+
else
|
|
428
|
+
return null;
|
|
429
|
+
return ts.isPropertyAccessExpression(expression) ? expression.name : expression;
|
|
430
|
+
}
|
|
431
|
+
function referenceRole(node, call) {
|
|
432
|
+
for (let current = node; current; current = current.parent) {
|
|
433
|
+
if (ts.isImportDeclaration(current) || ts.isImportSpecifier(current) || ts.isNamespaceImport(current) || ts.isImportClause(current) || ts.isExportSpecifier(current))
|
|
434
|
+
return 'import';
|
|
435
|
+
if (isTypeNode(current))
|
|
436
|
+
return 'type';
|
|
437
|
+
if (call)
|
|
438
|
+
return 'call';
|
|
439
|
+
if (isWrite(node, current))
|
|
440
|
+
return 'write';
|
|
441
|
+
}
|
|
442
|
+
return isDeclarationName(node) ? 'definition' : 'read';
|
|
443
|
+
}
|
|
444
|
+
function isDeclarationName(node) {
|
|
445
|
+
if (node.parent.name !== node)
|
|
446
|
+
return false;
|
|
447
|
+
switch (node.parent.kind) {
|
|
448
|
+
case ts.SyntaxKind.FunctionDeclaration:
|
|
449
|
+
case ts.SyntaxKind.ClassDeclaration:
|
|
450
|
+
case ts.SyntaxKind.InterfaceDeclaration:
|
|
451
|
+
case ts.SyntaxKind.TypeAliasDeclaration:
|
|
452
|
+
case ts.SyntaxKind.EnumDeclaration:
|
|
453
|
+
case ts.SyntaxKind.ModuleDeclaration:
|
|
454
|
+
case ts.SyntaxKind.VariableDeclaration:
|
|
455
|
+
case ts.SyntaxKind.BindingElement:
|
|
456
|
+
case ts.SyntaxKind.Parameter:
|
|
457
|
+
case ts.SyntaxKind.TypeParameter:
|
|
458
|
+
case ts.SyntaxKind.MethodDeclaration:
|
|
459
|
+
case ts.SyntaxKind.MethodSignature:
|
|
460
|
+
case ts.SyntaxKind.PropertyDeclaration:
|
|
461
|
+
case ts.SyntaxKind.PropertySignature:
|
|
462
|
+
case ts.SyntaxKind.PropertyAssignment:
|
|
463
|
+
case ts.SyntaxKind.ShorthandPropertyAssignment:
|
|
464
|
+
case ts.SyntaxKind.EnumMember:
|
|
465
|
+
case ts.SyntaxKind.GetAccessor:
|
|
466
|
+
case ts.SyntaxKind.SetAccessor:
|
|
467
|
+
return true;
|
|
468
|
+
default:
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
function isWrite(node, current) {
|
|
473
|
+
if ((ts.isBinaryExpression(current) && current.left === node && current.operatorToken.kind >= ts.SyntaxKind.FirstAssignment && current.operatorToken.kind <= ts.SyntaxKind.LastAssignment) || ts.isPrefixUnaryExpression(current) || ts.isPostfixUnaryExpression(current))
|
|
474
|
+
return true;
|
|
475
|
+
return false;
|
|
476
|
+
}
|
|
477
|
+
function isTypeNode(node) {
|
|
478
|
+
return node.kind >= ts.SyntaxKind.FirstTypeNode && node.kind <= ts.SyntaxKind.LastTypeNode;
|
|
479
|
+
}
|
|
480
|
+
function firstIdentifier(node) {
|
|
481
|
+
if (ts.isIdentifier(node))
|
|
482
|
+
return node;
|
|
483
|
+
return node.forEachChild(firstIdentifier);
|
|
484
|
+
}
|
|
485
|
+
function typePosition(node) {
|
|
486
|
+
const parent = node.parent;
|
|
487
|
+
if (ts.isParameterDeclaration(parent))
|
|
488
|
+
return 'parameter';
|
|
489
|
+
if (isFunctionLike(parent))
|
|
490
|
+
return 'return';
|
|
491
|
+
if (ts.isPropertyDeclaration(parent) || parent.kind === ts.SyntaxKind.PropertySignature)
|
|
492
|
+
return 'property';
|
|
493
|
+
if (ts.isVariableDeclaration(parent))
|
|
494
|
+
return 'variable';
|
|
495
|
+
return 'type';
|
|
496
|
+
}
|
|
497
|
+
function isFunctionLike(node) {
|
|
498
|
+
return node.kind === ts.SyntaxKind.FunctionDeclaration || node.kind === ts.SyntaxKind.FunctionExpression || node.kind === ts.SyntaxKind.ArrowFunction || node.kind === ts.SyntaxKind.MethodDeclaration || node.kind === ts.SyntaxKind.MethodSignature || node.kind === ts.SyntaxKind.GetAccessor || node.kind === ts.SyntaxKind.SetAccessor || node.kind === ts.SyntaxKind.Constructor || node.kind === ts.SyntaxKind.CallSignature || node.kind === ts.SyntaxKind.ConstructSignature || node.kind === ts.SyntaxKind.IndexSignature;
|
|
499
|
+
}
|
|
500
|
+
function importNames(node, _source) {
|
|
501
|
+
const clause = node.importClause;
|
|
502
|
+
if (!clause)
|
|
503
|
+
return '*';
|
|
504
|
+
const names = [];
|
|
505
|
+
if (clause.name)
|
|
506
|
+
names.push(clause.name.text);
|
|
507
|
+
if (clause.namedBindings && ts.isNamespaceImport(clause.namedBindings))
|
|
508
|
+
names.push(`* as ${clause.namedBindings.name.text}`);
|
|
509
|
+
if (clause.namedBindings && ts.isNamedImports(clause.namedBindings))
|
|
510
|
+
for (const element of clause.namedBindings.elements)
|
|
511
|
+
names.push(element.propertyName ? `${element.propertyName.text} as ${element.name.text}` : element.name.text);
|
|
512
|
+
return names.join(', ');
|
|
513
|
+
}
|
|
514
|
+
function importBindings(node) {
|
|
515
|
+
const clause = node.importClause;
|
|
516
|
+
if (!clause)
|
|
517
|
+
return [];
|
|
518
|
+
const bindings = [];
|
|
519
|
+
if (clause.name)
|
|
520
|
+
bindings.push(clause.name);
|
|
521
|
+
if (clause.namedBindings && ts.isNamespaceImport(clause.namedBindings))
|
|
522
|
+
bindings.push(clause.namedBindings.name);
|
|
523
|
+
if (clause.namedBindings && ts.isNamedImports(clause.namedBindings))
|
|
524
|
+
for (const element of clause.namedBindings.elements)
|
|
525
|
+
bindings.push(element.name);
|
|
526
|
+
return bindings;
|
|
527
|
+
}
|
|
528
|
+
function directExports(state, add) {
|
|
529
|
+
for (const descriptor of state.indexed) {
|
|
530
|
+
const node = state.nodesByStart.get(descriptor.start);
|
|
531
|
+
if (!node || !isExported(node))
|
|
532
|
+
continue;
|
|
533
|
+
const site = state.source.getLineAndCharacterOfPosition(descriptor.nameStart);
|
|
534
|
+
add({ type: 'EXPORTS', from: `${state.file}#<module>`, to: descriptor.key, name: descriptor.name, line: site.line + 1, col: site.character + 1 });
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
function exportDeclaration(stateNode, state, lookup, addTarget, add, alias) {
|
|
538
|
+
const clause = stateNode.exportClause;
|
|
539
|
+
if (!clause || !ts.isNamedExports(clause))
|
|
540
|
+
return;
|
|
541
|
+
for (const element of clause.elements) {
|
|
542
|
+
const symbol = state.checker.getExportSpecifierLocalTargetSymbol(element);
|
|
543
|
+
const target = symbol ? declarationTarget(symbol, lookup, alias) : null;
|
|
544
|
+
const key = addTarget(target, !symbol);
|
|
545
|
+
if (!key || target?.external)
|
|
546
|
+
continue;
|
|
547
|
+
const site = state.source.getLineAndCharacterOfPosition(element.name.getStart(state.source));
|
|
548
|
+
add({ type: 'EXPORTS', from: `${state.file}#<module>`, to: key, name: element.name.text, line: site.line + 1, col: site.character + 1 });
|
|
549
|
+
}
|
|
550
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ConfiguredProjects } from '../ts7/projects.js';
|
|
2
|
+
export interface WatchBatch {
|
|
3
|
+
fileChanges: {
|
|
4
|
+
changed?: string[];
|
|
5
|
+
created?: string[];
|
|
6
|
+
deleted?: string[];
|
|
7
|
+
} | {
|
|
8
|
+
invalidateAll: true;
|
|
9
|
+
};
|
|
10
|
+
full: boolean;
|
|
11
|
+
git: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface IndexWatcher {
|
|
14
|
+
close(): void;
|
|
15
|
+
refresh(): void;
|
|
16
|
+
}
|
|
17
|
+
/** Watches configured source directories; events supply text changes, never semantic invalidation. */
|
|
18
|
+
export declare function watchIndex(root: string, projects: ConfiguredProjects, refresh: (batch: WatchBatch) => Promise<void>, reportError: (error: unknown) => void, markStale: (flush: () => Promise<void>) => void): IndexWatcher;
|