dep-context-mcp 1.0.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/README.md +380 -0
- package/dist/analysis/relationships.d.ts +25 -0
- package/dist/analysis/relationships.d.ts.map +1 -0
- package/dist/analysis/relationships.js +26 -0
- package/dist/analysis/relationships.js.map +1 -0
- package/dist/build-info.d.ts +15 -0
- package/dist/build-info.d.ts.map +1 -0
- package/dist/build-info.js +24 -0
- package/dist/build-info.js.map +1 -0
- package/dist/cache/manager.d.ts +19 -0
- package/dist/cache/manager.d.ts.map +1 -0
- package/dist/cache/manager.js +131 -0
- package/dist/cache/manager.js.map +1 -0
- package/dist/cache/registry.d.ts +44 -0
- package/dist/cache/registry.d.ts.map +1 -0
- package/dist/cache/registry.js +92 -0
- package/dist/cache/registry.js.map +1 -0
- package/dist/config/loader.d.ts +62 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +193 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/extraction/examples.d.ts +22 -0
- package/dist/extraction/examples.d.ts.map +1 -0
- package/dist/extraction/examples.js +109 -0
- package/dist/extraction/examples.js.map +1 -0
- package/dist/extraction/external-resolver.d.ts +26 -0
- package/dist/extraction/external-resolver.d.ts.map +1 -0
- package/dist/extraction/external-resolver.js +79 -0
- package/dist/extraction/external-resolver.js.map +1 -0
- package/dist/extraction/import-tracker.d.ts +58 -0
- package/dist/extraction/import-tracker.d.ts.map +1 -0
- package/dist/extraction/import-tracker.js +113 -0
- package/dist/extraction/import-tracker.js.map +1 -0
- package/dist/extraction/readme.d.ts +19 -0
- package/dist/extraction/readme.d.ts.map +1 -0
- package/dist/extraction/readme.js +79 -0
- package/dist/extraction/readme.js.map +1 -0
- package/dist/extraction/snapshot.d.ts +32 -0
- package/dist/extraction/snapshot.d.ts.map +1 -0
- package/dist/extraction/snapshot.js +258 -0
- package/dist/extraction/snapshot.js.map +1 -0
- package/dist/extraction/typescript.d.ts +104 -0
- package/dist/extraction/typescript.d.ts.map +1 -0
- package/dist/extraction/typescript.js +647 -0
- package/dist/extraction/typescript.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +329 -0
- package/dist/index.js.map +1 -0
- package/dist/resolver/package.d.ts +62 -0
- package/dist/resolver/package.d.ts.map +1 -0
- package/dist/resolver/package.js +334 -0
- package/dist/resolver/package.js.map +1 -0
- package/dist/tools/get-dependency.d.ts +55 -0
- package/dist/tools/get-dependency.d.ts.map +1 -0
- package/dist/tools/get-dependency.js +105 -0
- package/dist/tools/get-dependency.js.map +1 -0
- package/dist/tools/index-dependencies.d.ts +51 -0
- package/dist/tools/index-dependencies.d.ts.map +1 -0
- package/dist/tools/index-dependencies.js +90 -0
- package/dist/tools/index-dependencies.js.map +1 -0
- package/dist/tools/index-status.d.ts +36 -0
- package/dist/tools/index-status.d.ts.map +1 -0
- package/dist/tools/index-status.js +45 -0
- package/dist/tools/index-status.js.map +1 -0
- package/dist/tools/list.d.ts +48 -0
- package/dist/tools/list.d.ts.map +1 -0
- package/dist/tools/list.js +55 -0
- package/dist/tools/list.js.map +1 -0
- package/dist/tools/refresh.d.ts +59 -0
- package/dist/tools/refresh.d.ts.map +1 -0
- package/dist/tools/refresh.js +109 -0
- package/dist/tools/refresh.js.map +1 -0
- package/dist/tools/relationships.d.ts +41 -0
- package/dist/tools/relationships.d.ts.map +1 -0
- package/dist/tools/relationships.js +36 -0
- package/dist/tools/relationships.js.map +1 -0
- package/dist/tools/search-codebase.d.ts +64 -0
- package/dist/tools/search-codebase.d.ts.map +1 -0
- package/dist/tools/search-codebase.js +117 -0
- package/dist/tools/search-codebase.js.map +1 -0
- package/dist/tools/search.d.ts +37 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +77 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/utils/logger.d.ts +19 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +71 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/vector/chroma.d.ts +38 -0
- package/dist/vector/chroma.d.ts.map +1 -0
- package/dist/vector/chroma.js +69 -0
- package/dist/vector/chroma.js.map +1 -0
- package/dist/vector/chunker.d.ts +29 -0
- package/dist/vector/chunker.d.ts.map +1 -0
- package/dist/vector/chunker.js +205 -0
- package/dist/vector/chunker.js.map +1 -0
- package/dist/vector/embeddings.d.ts +36 -0
- package/dist/vector/embeddings.d.ts.map +1 -0
- package/dist/vector/embeddings.js +106 -0
- package/dist/vector/embeddings.js.map +1 -0
- package/dist/vector/file-store.d.ts +10 -0
- package/dist/vector/file-store.d.ts.map +1 -0
- package/dist/vector/file-store.js +80 -0
- package/dist/vector/file-store.js.map +1 -0
- package/dist/vector/index-manager.d.ts +39 -0
- package/dist/vector/index-manager.d.ts.map +1 -0
- package/dist/vector/index-manager.js +105 -0
- package/dist/vector/index-manager.js.map +1 -0
- package/dist/vector/provider-factory.d.ts +23 -0
- package/dist/vector/provider-factory.d.ts.map +1 -0
- package/dist/vector/provider-factory.js +50 -0
- package/dist/vector/provider-factory.js.map +1 -0
- package/dist/vector/storage-factory.d.ts +17 -0
- package/dist/vector/storage-factory.d.ts.map +1 -0
- package/dist/vector/storage-factory.js +55 -0
- package/dist/vector/storage-factory.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript type extractor - parses .d.ts files using the TypeScript compiler API
|
|
3
|
+
*/
|
|
4
|
+
import * as ts from 'typescript';
|
|
5
|
+
import { readFile } from 'node:fs/promises';
|
|
6
|
+
import { dirname, join, resolve } from 'node:path';
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
import { createImportTracker } from './import-tracker.js';
|
|
9
|
+
import { createExternalPackageResolver } from './external-resolver.js';
|
|
10
|
+
/**
|
|
11
|
+
* Extracts JSDoc comment from a node
|
|
12
|
+
*/
|
|
13
|
+
function getJsDoc(node, sourceFile) {
|
|
14
|
+
const jsDocTags = ts.getJSDocTags(node);
|
|
15
|
+
if (jsDocTags.length > 0) {
|
|
16
|
+
return jsDocTags.map(tag => {
|
|
17
|
+
const tagName = tag.tagName.text;
|
|
18
|
+
const comment = typeof tag.comment === 'string'
|
|
19
|
+
? tag.comment
|
|
20
|
+
: tag.comment?.map(c => c.text).join('') ?? '';
|
|
21
|
+
return `@${tagName} ${comment}`.trim();
|
|
22
|
+
}).join('\n');
|
|
23
|
+
}
|
|
24
|
+
// Try to get the leading comment
|
|
25
|
+
const fullText = sourceFile.getFullText();
|
|
26
|
+
const nodeStart = node.getFullStart();
|
|
27
|
+
const leadingComments = ts.getLeadingCommentRanges(fullText, nodeStart);
|
|
28
|
+
if (leadingComments && leadingComments.length > 0) {
|
|
29
|
+
const lastComment = leadingComments[leadingComments.length - 1];
|
|
30
|
+
const commentText = fullText.slice(lastComment.pos, lastComment.end);
|
|
31
|
+
// Parse JSDoc style comments
|
|
32
|
+
if (commentText.startsWith('/**')) {
|
|
33
|
+
return commentText
|
|
34
|
+
.replace(/^\/\*\*\s*/, '')
|
|
35
|
+
.replace(/\s*\*\/$/, '')
|
|
36
|
+
.split('\n')
|
|
37
|
+
.map(line => line.replace(/^\s*\*\s?/, ''))
|
|
38
|
+
.join('\n')
|
|
39
|
+
.trim();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Checks if a node has an export modifier
|
|
46
|
+
*/
|
|
47
|
+
function isExported(node) {
|
|
48
|
+
const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
|
|
49
|
+
return modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Extracts parameter information from a parameter declaration
|
|
53
|
+
*/
|
|
54
|
+
function extractParameter(param, sourceFile) {
|
|
55
|
+
const name = param.name.getText(sourceFile);
|
|
56
|
+
const type = param.type ? param.type.getText(sourceFile) : 'any';
|
|
57
|
+
const optional = param.questionToken !== undefined || param.initializer !== undefined;
|
|
58
|
+
const defaultValue = param.initializer ? param.initializer.getText(sourceFile) : undefined;
|
|
59
|
+
return { name, type, optional, defaultValue };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Extracts a function signature as a string
|
|
63
|
+
*/
|
|
64
|
+
function getFunctionSignature(node, sourceFile) {
|
|
65
|
+
const name = node.name?.getText(sourceFile) ?? 'anonymous';
|
|
66
|
+
const typeParams = node.typeParameters
|
|
67
|
+
? `<${node.typeParameters.map(tp => tp.getText(sourceFile)).join(', ')}>`
|
|
68
|
+
: '';
|
|
69
|
+
const params = node.parameters
|
|
70
|
+
.map(p => {
|
|
71
|
+
const paramName = p.name.getText(sourceFile);
|
|
72
|
+
const paramType = p.type ? p.type.getText(sourceFile) : 'any';
|
|
73
|
+
const optional = p.questionToken ? '?' : '';
|
|
74
|
+
return `${paramName}${optional}: ${paramType}`;
|
|
75
|
+
})
|
|
76
|
+
.join(', ');
|
|
77
|
+
const returnType = node.type ? node.type.getText(sourceFile) : 'void';
|
|
78
|
+
return `${name}${typeParams}(${params}): ${returnType}`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Extracts a function declaration
|
|
82
|
+
*/
|
|
83
|
+
function extractFunction(node, sourceFile) {
|
|
84
|
+
const name = node.name?.getText(sourceFile) ?? 'anonymous';
|
|
85
|
+
const signature = `function ${getFunctionSignature(node, sourceFile)}`;
|
|
86
|
+
const parameters = node.parameters.map(p => extractParameter(p, sourceFile));
|
|
87
|
+
const returnType = node.type ? node.type.getText(sourceFile) : 'void';
|
|
88
|
+
const jsDoc = getJsDoc(node, sourceFile);
|
|
89
|
+
const isAsync = node.modifiers?.some(m => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false;
|
|
90
|
+
return { name, signature, parameters, returnType, jsDoc, isAsync };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Extracts a method from a class or interface
|
|
94
|
+
*/
|
|
95
|
+
function extractMethod(node, sourceFile) {
|
|
96
|
+
const name = node.name.getText(sourceFile);
|
|
97
|
+
const signature = getFunctionSignature(node, sourceFile);
|
|
98
|
+
const parameters = node.parameters.map(p => extractParameter(p, sourceFile));
|
|
99
|
+
const returnType = node.type ? node.type.getText(sourceFile) : 'void';
|
|
100
|
+
const jsDoc = getJsDoc(node, sourceFile);
|
|
101
|
+
let isAsync = false;
|
|
102
|
+
let isStatic = false;
|
|
103
|
+
if (ts.isMethodDeclaration(node)) {
|
|
104
|
+
const modifiers = ts.getModifiers(node);
|
|
105
|
+
isAsync = modifiers?.some(m => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false;
|
|
106
|
+
isStatic = modifiers?.some(m => m.kind === ts.SyntaxKind.StaticKeyword) ?? false;
|
|
107
|
+
}
|
|
108
|
+
return { name, signature, parameters, returnType, jsDoc, isAsync, isStatic };
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Extracts a property from a class or interface
|
|
112
|
+
*/
|
|
113
|
+
function extractProperty(node, sourceFile) {
|
|
114
|
+
const name = node.name.getText(sourceFile);
|
|
115
|
+
const type = node.type ? node.type.getText(sourceFile) : 'any';
|
|
116
|
+
const optional = node.questionToken !== undefined;
|
|
117
|
+
const jsDoc = getJsDoc(node, sourceFile);
|
|
118
|
+
let readonly = false;
|
|
119
|
+
if (ts.isPropertyDeclaration(node)) {
|
|
120
|
+
const modifiers = ts.getModifiers(node);
|
|
121
|
+
readonly = modifiers?.some(m => m.kind === ts.SyntaxKind.ReadonlyKeyword) ?? false;
|
|
122
|
+
}
|
|
123
|
+
else if (ts.isPropertySignature(node)) {
|
|
124
|
+
readonly = node.modifiers?.some(m => m.kind === ts.SyntaxKind.ReadonlyKeyword) ?? false;
|
|
125
|
+
}
|
|
126
|
+
return { name, type, optional, readonly, jsDoc };
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Extracts a class declaration
|
|
130
|
+
*/
|
|
131
|
+
function extractClass(node, sourceFile) {
|
|
132
|
+
const name = node.name?.getText(sourceFile) ?? 'anonymous';
|
|
133
|
+
const jsDoc = getJsDoc(node, sourceFile);
|
|
134
|
+
let constructorSignature;
|
|
135
|
+
const methods = [];
|
|
136
|
+
const properties = [];
|
|
137
|
+
for (const member of node.members) {
|
|
138
|
+
if (ts.isConstructorDeclaration(member)) {
|
|
139
|
+
const params = member.parameters
|
|
140
|
+
.map(p => {
|
|
141
|
+
const paramName = p.name.getText(sourceFile);
|
|
142
|
+
const paramType = p.type ? p.type.getText(sourceFile) : 'any';
|
|
143
|
+
const optional = p.questionToken ? '?' : '';
|
|
144
|
+
return `${paramName}${optional}: ${paramType}`;
|
|
145
|
+
})
|
|
146
|
+
.join(', ');
|
|
147
|
+
constructorSignature = `constructor(${params})`;
|
|
148
|
+
}
|
|
149
|
+
else if (ts.isMethodDeclaration(member)) {
|
|
150
|
+
// Only include public methods
|
|
151
|
+
const modifiers = ts.getModifiers(member);
|
|
152
|
+
const isPrivate = modifiers?.some(m => m.kind === ts.SyntaxKind.PrivateKeyword);
|
|
153
|
+
if (!isPrivate) {
|
|
154
|
+
methods.push(extractMethod(member, sourceFile));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else if (ts.isPropertyDeclaration(member)) {
|
|
158
|
+
// Only include public properties
|
|
159
|
+
const modifiers = ts.getModifiers(member);
|
|
160
|
+
const isPrivate = modifiers?.some(m => m.kind === ts.SyntaxKind.PrivateKeyword);
|
|
161
|
+
if (!isPrivate) {
|
|
162
|
+
properties.push(extractProperty(member, sourceFile));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return { name, constructorSignature, methods, properties, jsDoc };
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Extracts an interface declaration
|
|
170
|
+
*/
|
|
171
|
+
function extractInterface(node, sourceFile) {
|
|
172
|
+
const name = node.name.getText(sourceFile);
|
|
173
|
+
const jsDoc = getJsDoc(node, sourceFile);
|
|
174
|
+
const methods = [];
|
|
175
|
+
const properties = [];
|
|
176
|
+
for (const member of node.members) {
|
|
177
|
+
if (ts.isMethodSignature(member)) {
|
|
178
|
+
methods.push(extractMethod(member, sourceFile));
|
|
179
|
+
}
|
|
180
|
+
else if (ts.isPropertySignature(member)) {
|
|
181
|
+
properties.push(extractProperty(member, sourceFile));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return { name, properties, methods, jsDoc };
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Extracts a type alias declaration
|
|
188
|
+
*/
|
|
189
|
+
function extractTypeAlias(node, sourceFile) {
|
|
190
|
+
const name = node.name.getText(sourceFile);
|
|
191
|
+
const definition = node.type.getText(sourceFile);
|
|
192
|
+
const jsDoc = getJsDoc(node, sourceFile);
|
|
193
|
+
return { name, definition, jsDoc };
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Extracts re-export statements from a source file
|
|
197
|
+
* Distinguishes between star exports and named re-exports
|
|
198
|
+
*/
|
|
199
|
+
function extractReExports(sourceFile) {
|
|
200
|
+
const result = {
|
|
201
|
+
starExports: [],
|
|
202
|
+
namedReExports: []
|
|
203
|
+
};
|
|
204
|
+
for (const statement of sourceFile.statements) {
|
|
205
|
+
if (ts.isExportDeclaration(statement) && statement.moduleSpecifier) {
|
|
206
|
+
const moduleSpecifier = statement.moduleSpecifier;
|
|
207
|
+
if (!ts.isStringLiteral(moduleSpecifier))
|
|
208
|
+
continue;
|
|
209
|
+
const sourcePath = moduleSpecifier.text;
|
|
210
|
+
if (!statement.exportClause) {
|
|
211
|
+
// export * from './module' - true star export
|
|
212
|
+
result.starExports.push(sourcePath);
|
|
213
|
+
}
|
|
214
|
+
else if (ts.isNamedExports(statement.exportClause)) {
|
|
215
|
+
// export { x, y } from './module' or export { x as y } from './module'
|
|
216
|
+
for (const element of statement.exportClause.elements) {
|
|
217
|
+
const exportedName = element.name.text;
|
|
218
|
+
// propertyName is the original name if aliased (export { original as exported })
|
|
219
|
+
const originalName = element.propertyName?.text ?? exportedName;
|
|
220
|
+
result.namedReExports.push({ exportedName, originalName, sourcePath });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return result;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Creates an empty extraction result
|
|
229
|
+
*/
|
|
230
|
+
function createEmptyResult() {
|
|
231
|
+
return {
|
|
232
|
+
functions: [],
|
|
233
|
+
classes: [],
|
|
234
|
+
interfaces: [],
|
|
235
|
+
types: [],
|
|
236
|
+
reExports: [],
|
|
237
|
+
importedExports: [],
|
|
238
|
+
errors: []
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Merges two extraction results
|
|
243
|
+
*/
|
|
244
|
+
function mergeResults(a, b) {
|
|
245
|
+
return {
|
|
246
|
+
functions: [...a.functions, ...b.functions],
|
|
247
|
+
classes: [...a.classes, ...b.classes],
|
|
248
|
+
interfaces: [...a.interfaces, ...b.interfaces],
|
|
249
|
+
types: [...a.types, ...b.types],
|
|
250
|
+
reExports: [...a.reExports, ...b.reExports],
|
|
251
|
+
importedExports: [...a.importedExports, ...b.importedExports],
|
|
252
|
+
errors: [...a.errors, ...b.errors]
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Extracts all exports from a TypeScript declaration file
|
|
257
|
+
*/
|
|
258
|
+
export async function extract(entryPath) {
|
|
259
|
+
const result = createEmptyResult();
|
|
260
|
+
let content;
|
|
261
|
+
try {
|
|
262
|
+
content = await readFile(entryPath, 'utf-8');
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
result.errors.push(`Failed to read file: ${entryPath}`);
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
let sourceFile;
|
|
269
|
+
try {
|
|
270
|
+
sourceFile = ts.createSourceFile(entryPath, content, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
result.errors.push(`Failed to parse file: ${entryPath}`);
|
|
274
|
+
return result;
|
|
275
|
+
}
|
|
276
|
+
// Track imports for import-then-export pattern detection
|
|
277
|
+
const importTracker = createImportTracker(sourceFile);
|
|
278
|
+
// Extract re-exports (export * from './module' and export { x } from './module')
|
|
279
|
+
const reExportInfo = extractReExports(sourceFile);
|
|
280
|
+
result.reExports.push(...reExportInfo.starExports);
|
|
281
|
+
// Named re-exports are treated like importedExports (they need symbol extraction with renaming)
|
|
282
|
+
result.importedExports.push(...reExportInfo.namedReExports);
|
|
283
|
+
// Build a map of all declarations in the file (for export { x } resolution)
|
|
284
|
+
const declarations = new Map();
|
|
285
|
+
// First pass: collect all declarations
|
|
286
|
+
function collectDeclarations(node) {
|
|
287
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
288
|
+
declarations.set(node.name.text, node);
|
|
289
|
+
}
|
|
290
|
+
if (ts.isClassDeclaration(node) && node.name) {
|
|
291
|
+
declarations.set(node.name.text, node);
|
|
292
|
+
}
|
|
293
|
+
if (ts.isInterfaceDeclaration(node)) {
|
|
294
|
+
declarations.set(node.name.text, node);
|
|
295
|
+
}
|
|
296
|
+
if (ts.isTypeAliasDeclaration(node)) {
|
|
297
|
+
declarations.set(node.name.text, node);
|
|
298
|
+
}
|
|
299
|
+
ts.forEachChild(node, collectDeclarations);
|
|
300
|
+
}
|
|
301
|
+
collectDeclarations(sourceFile);
|
|
302
|
+
// Track which symbols are exported via export { x } declarations
|
|
303
|
+
const exportedViaDeclaration = new Map(); // localName -> exportedName
|
|
304
|
+
// Walk the AST
|
|
305
|
+
function visit(node) {
|
|
306
|
+
try {
|
|
307
|
+
// Exported function declaration (with export keyword)
|
|
308
|
+
if (ts.isFunctionDeclaration(node) && isExported(node) && node.name) {
|
|
309
|
+
result.functions.push(extractFunction(node, sourceFile));
|
|
310
|
+
}
|
|
311
|
+
// Exported class declaration (with export keyword)
|
|
312
|
+
if (ts.isClassDeclaration(node) && isExported(node) && node.name) {
|
|
313
|
+
result.classes.push(extractClass(node, sourceFile));
|
|
314
|
+
}
|
|
315
|
+
// Exported interface declaration (with export keyword)
|
|
316
|
+
if (ts.isInterfaceDeclaration(node) && isExported(node)) {
|
|
317
|
+
result.interfaces.push(extractInterface(node, sourceFile));
|
|
318
|
+
}
|
|
319
|
+
// Exported type alias (with export keyword)
|
|
320
|
+
if (ts.isTypeAliasDeclaration(node) && isExported(node)) {
|
|
321
|
+
result.types.push(extractTypeAlias(node, sourceFile));
|
|
322
|
+
}
|
|
323
|
+
// Handle export declarations without module specifier (export { x, y } or export { x as y })
|
|
324
|
+
if (ts.isExportDeclaration(node) && node.exportClause && !node.moduleSpecifier) {
|
|
325
|
+
if (ts.isNamedExports(node.exportClause)) {
|
|
326
|
+
for (const element of node.exportClause.elements) {
|
|
327
|
+
// The exported name (what consumers see)
|
|
328
|
+
const exportedName = element.name.text;
|
|
329
|
+
// The local name (might be aliased: export { local as exported })
|
|
330
|
+
const localName = element.propertyName?.text ?? exportedName;
|
|
331
|
+
// Check if this is an imported symbol
|
|
332
|
+
const importInfo = importTracker.getImport(localName);
|
|
333
|
+
if (importInfo) {
|
|
334
|
+
result.importedExports.push({
|
|
335
|
+
exportedName,
|
|
336
|
+
originalName: importInfo.originalName,
|
|
337
|
+
sourcePath: importInfo.sourcePath
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
// Not an import - check if it's a local declaration
|
|
342
|
+
// Record it for extraction with the exported name
|
|
343
|
+
exportedViaDeclaration.set(localName, exportedName);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
catch (error) {
|
|
350
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
351
|
+
result.errors.push(`Error processing node: ${message}`);
|
|
352
|
+
}
|
|
353
|
+
ts.forEachChild(node, visit);
|
|
354
|
+
}
|
|
355
|
+
visit(sourceFile);
|
|
356
|
+
// Second pass: extract declarations that are exported via export { x } or export { x as y }
|
|
357
|
+
for (const [localName, exportedName] of exportedViaDeclaration) {
|
|
358
|
+
const decl = declarations.get(localName);
|
|
359
|
+
if (!decl)
|
|
360
|
+
continue;
|
|
361
|
+
if (ts.isFunctionDeclaration(decl) && decl.name) {
|
|
362
|
+
const extracted = extractFunction(decl, sourceFile);
|
|
363
|
+
// Rename if aliased
|
|
364
|
+
if (localName !== exportedName) {
|
|
365
|
+
extracted.name = exportedName;
|
|
366
|
+
extracted.signature = extracted.signature.replace(localName, exportedName);
|
|
367
|
+
}
|
|
368
|
+
result.functions.push(extracted);
|
|
369
|
+
}
|
|
370
|
+
if (ts.isClassDeclaration(decl) && decl.name) {
|
|
371
|
+
const extracted = extractClass(decl, sourceFile);
|
|
372
|
+
if (localName !== exportedName) {
|
|
373
|
+
extracted.name = exportedName;
|
|
374
|
+
}
|
|
375
|
+
result.classes.push(extracted);
|
|
376
|
+
}
|
|
377
|
+
if (ts.isInterfaceDeclaration(decl)) {
|
|
378
|
+
const extracted = extractInterface(decl, sourceFile);
|
|
379
|
+
if (localName !== exportedName) {
|
|
380
|
+
extracted.name = exportedName;
|
|
381
|
+
}
|
|
382
|
+
result.interfaces.push(extracted);
|
|
383
|
+
}
|
|
384
|
+
if (ts.isTypeAliasDeclaration(decl)) {
|
|
385
|
+
const extracted = extractTypeAlias(decl, sourceFile);
|
|
386
|
+
if (localName !== exportedName) {
|
|
387
|
+
extracted.name = exportedName;
|
|
388
|
+
}
|
|
389
|
+
result.types.push(extracted);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return result;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Checks if a module path is external (not relative)
|
|
396
|
+
*/
|
|
397
|
+
function isExternalModule(modulePath) {
|
|
398
|
+
return !modulePath.startsWith('.') && !modulePath.startsWith('/');
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Resolves a module path to an actual file path
|
|
402
|
+
* Handles .js → .d.ts mapping and index.d.ts fallbacks
|
|
403
|
+
* For external modules, returns undefined (handled separately)
|
|
404
|
+
*/
|
|
405
|
+
function resolveModulePath(modulePath, baseDir) {
|
|
406
|
+
// External modules are handled separately
|
|
407
|
+
if (isExternalModule(modulePath)) {
|
|
408
|
+
return undefined;
|
|
409
|
+
}
|
|
410
|
+
let resolvedPath = resolve(baseDir, modulePath);
|
|
411
|
+
// Handle .js extension (common in ESM - maps to .d.ts)
|
|
412
|
+
if (resolvedPath.endsWith('.js')) {
|
|
413
|
+
const dtsPath = resolvedPath.replace(/\.js$/, '.d.ts');
|
|
414
|
+
if (existsSync(dtsPath)) {
|
|
415
|
+
return dtsPath;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
// Try with .d.ts extension
|
|
419
|
+
if (!resolvedPath.endsWith('.d.ts') && !resolvedPath.endsWith('.ts')) {
|
|
420
|
+
if (existsSync(resolvedPath + '.d.ts')) {
|
|
421
|
+
return resolvedPath + '.d.ts';
|
|
422
|
+
}
|
|
423
|
+
else if (existsSync(resolvedPath + '.ts')) {
|
|
424
|
+
return resolvedPath + '.ts';
|
|
425
|
+
}
|
|
426
|
+
else if (existsSync(join(resolvedPath, 'index.d.ts'))) {
|
|
427
|
+
return join(resolvedPath, 'index.d.ts');
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
// Already has extension
|
|
431
|
+
if (existsSync(resolvedPath)) {
|
|
432
|
+
return resolvedPath;
|
|
433
|
+
}
|
|
434
|
+
return undefined;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Extracts specific symbols from a source file
|
|
438
|
+
* Used for import-then-export pattern resolution
|
|
439
|
+
*/
|
|
440
|
+
async function extractSymbolsFromSource(sourcePath, symbols, visited) {
|
|
441
|
+
const result = createEmptyResult();
|
|
442
|
+
// Skip if already visited (cycle detection)
|
|
443
|
+
if (visited.has(sourcePath)) {
|
|
444
|
+
return result;
|
|
445
|
+
}
|
|
446
|
+
visited.add(sourcePath);
|
|
447
|
+
// Extract all exports from source file
|
|
448
|
+
const sourceResult = await extract(sourcePath);
|
|
449
|
+
// Follow any re-exports in the source file first
|
|
450
|
+
const followedSource = await followReExports(sourceResult, sourcePath, visited);
|
|
451
|
+
// Filter to only the symbols we're looking for
|
|
452
|
+
const symbolNames = new Set(symbols.map(s => s.originalName));
|
|
453
|
+
result.functions = followedSource.functions.filter(f => symbolNames.has(f.name));
|
|
454
|
+
result.classes = followedSource.classes.filter(c => symbolNames.has(c.name));
|
|
455
|
+
result.interfaces = followedSource.interfaces.filter(i => symbolNames.has(i.name));
|
|
456
|
+
result.types = followedSource.types.filter(t => symbolNames.has(t.name));
|
|
457
|
+
// Handle aliased exports - rename to the exported name
|
|
458
|
+
for (const sym of symbols) {
|
|
459
|
+
if (sym.exportedName !== sym.originalName) {
|
|
460
|
+
// Rename functions
|
|
461
|
+
const fn = result.functions.find(f => f.name === sym.originalName);
|
|
462
|
+
if (fn) {
|
|
463
|
+
fn.name = sym.exportedName;
|
|
464
|
+
fn.signature = fn.signature.replace(sym.originalName, sym.exportedName);
|
|
465
|
+
}
|
|
466
|
+
// Rename classes
|
|
467
|
+
const cls = result.classes.find(c => c.name === sym.originalName);
|
|
468
|
+
if (cls) {
|
|
469
|
+
cls.name = sym.exportedName;
|
|
470
|
+
}
|
|
471
|
+
// Rename interfaces
|
|
472
|
+
const iface = result.interfaces.find(i => i.name === sym.originalName);
|
|
473
|
+
if (iface) {
|
|
474
|
+
iface.name = sym.exportedName;
|
|
475
|
+
}
|
|
476
|
+
// Rename types
|
|
477
|
+
const typ = result.types.find(t => t.name === sym.originalName);
|
|
478
|
+
if (typ) {
|
|
479
|
+
typ.name = sym.exportedName;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
// If we didn't find some symbols, they might be in importedExports of the source
|
|
484
|
+
// (nested import-then-export pattern)
|
|
485
|
+
const foundNames = new Set([
|
|
486
|
+
...result.functions.map(f => f.name),
|
|
487
|
+
...result.classes.map(c => c.name),
|
|
488
|
+
...result.interfaces.map(i => i.name),
|
|
489
|
+
...result.types.map(t => t.name)
|
|
490
|
+
]);
|
|
491
|
+
const missingSymbols = symbols.filter(s => !foundNames.has(s.exportedName));
|
|
492
|
+
if (missingSymbols.length > 0 && followedSource.importedExports.length > 0) {
|
|
493
|
+
// Check if missing symbols are in importedExports
|
|
494
|
+
const baseDir = dirname(sourcePath);
|
|
495
|
+
for (const missing of missingSymbols) {
|
|
496
|
+
const importedExport = followedSource.importedExports.find(ie => ie.exportedName === missing.originalName);
|
|
497
|
+
if (importedExport) {
|
|
498
|
+
const nestedPath = resolveModulePath(importedExport.sourcePath, baseDir);
|
|
499
|
+
if (nestedPath) {
|
|
500
|
+
const nestedResult = await extractSymbolsFromSource(nestedPath, [{ exportedName: missing.exportedName, originalName: importedExport.originalName }], visited);
|
|
501
|
+
result.functions.push(...nestedResult.functions);
|
|
502
|
+
result.classes.push(...nestedResult.classes);
|
|
503
|
+
result.interfaces.push(...nestedResult.interfaces);
|
|
504
|
+
result.types.push(...nestedResult.types);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return result;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Follows re-exports and extracts from referenced modules
|
|
513
|
+
*/
|
|
514
|
+
export async function followReExports(result, basePath, visited = new Set(), options = {}) {
|
|
515
|
+
const baseDir = dirname(basePath);
|
|
516
|
+
let mergedResult = { ...result };
|
|
517
|
+
const { externalResolver, maxExternalDepth = 2 } = options;
|
|
518
|
+
// Track external packages we've already processed
|
|
519
|
+
const processedExternalPackages = new Set();
|
|
520
|
+
// Follow traditional re-exports (export * from './module' or 'external-package')
|
|
521
|
+
for (const reExport of result.reExports) {
|
|
522
|
+
// Check if this is an external package
|
|
523
|
+
if (isExternalModule(reExport)) {
|
|
524
|
+
// Only follow external packages if we have a resolver
|
|
525
|
+
if (!externalResolver) {
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
// Extract package name (handle scoped packages)
|
|
529
|
+
const packageName = reExport.startsWith('@')
|
|
530
|
+
? reExport.split('/').slice(0, 2).join('/')
|
|
531
|
+
: reExport.split('/')[0];
|
|
532
|
+
// Skip if already processed
|
|
533
|
+
if (processedExternalPackages.has(packageName)) {
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
processedExternalPackages.add(packageName);
|
|
537
|
+
// Resolve external package
|
|
538
|
+
const resolved = await externalResolver.resolve(packageName);
|
|
539
|
+
if (!resolved.success || !resolved.typesPath) {
|
|
540
|
+
mergedResult.errors.push(`Could not resolve external package: ${packageName}`);
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
// Skip if already visited
|
|
544
|
+
if (visited.has(resolved.typesPath)) {
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
visited.add(resolved.typesPath);
|
|
548
|
+
// Extract from external package (with reduced depth for externals)
|
|
549
|
+
const externalResult = await extract(resolved.typesPath);
|
|
550
|
+
// Don't follow external packages' externals too deeply
|
|
551
|
+
const followedExternal = await followReExports(externalResult, resolved.typesPath, visited, { externalResolver: maxExternalDepth > 1 ? externalResolver : undefined, maxExternalDepth: maxExternalDepth - 1 });
|
|
552
|
+
mergedResult = mergeResults(mergedResult, followedExternal);
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
// Handle relative paths
|
|
556
|
+
const resolvedPath = resolveModulePath(reExport, baseDir);
|
|
557
|
+
if (!resolvedPath) {
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
// Skip if already visited (circular reference protection)
|
|
561
|
+
if (visited.has(resolvedPath)) {
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
visited.add(resolvedPath);
|
|
565
|
+
// Extract from the re-exported module
|
|
566
|
+
const reExportResult = await extract(resolvedPath);
|
|
567
|
+
const followedResult = await followReExports(reExportResult, resolvedPath, visited, options);
|
|
568
|
+
mergedResult = mergeResults(mergedResult, followedResult);
|
|
569
|
+
}
|
|
570
|
+
// Follow import-then-export patterns
|
|
571
|
+
// Group by source path for efficiency
|
|
572
|
+
const importsBySource = new Map();
|
|
573
|
+
const externalImports = new Map();
|
|
574
|
+
for (const importedExport of result.importedExports) {
|
|
575
|
+
if (isExternalModule(importedExport.sourcePath)) {
|
|
576
|
+
// External package import
|
|
577
|
+
const packageName = importedExport.sourcePath.startsWith('@')
|
|
578
|
+
? importedExport.sourcePath.split('/').slice(0, 2).join('/')
|
|
579
|
+
: importedExport.sourcePath.split('/')[0];
|
|
580
|
+
if (!externalImports.has(packageName)) {
|
|
581
|
+
externalImports.set(packageName, []);
|
|
582
|
+
}
|
|
583
|
+
externalImports.get(packageName).push({
|
|
584
|
+
exportedName: importedExport.exportedName,
|
|
585
|
+
originalName: importedExport.originalName
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
// Relative import
|
|
590
|
+
const resolvedPath = resolveModulePath(importedExport.sourcePath, baseDir);
|
|
591
|
+
if (!resolvedPath) {
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
if (!importsBySource.has(resolvedPath)) {
|
|
595
|
+
importsBySource.set(resolvedPath, []);
|
|
596
|
+
}
|
|
597
|
+
importsBySource.get(resolvedPath).push({
|
|
598
|
+
exportedName: importedExport.exportedName,
|
|
599
|
+
originalName: importedExport.originalName
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
// Extract symbols from relative sources
|
|
604
|
+
for (const [sourcePath, symbols] of importsBySource) {
|
|
605
|
+
const extractedSymbols = await extractSymbolsFromSource(sourcePath, symbols, visited);
|
|
606
|
+
mergedResult = mergeResults(mergedResult, extractedSymbols);
|
|
607
|
+
}
|
|
608
|
+
// Extract symbols from external packages
|
|
609
|
+
if (externalResolver) {
|
|
610
|
+
for (const [packageName, symbols] of externalImports) {
|
|
611
|
+
// Skip if already processed
|
|
612
|
+
if (processedExternalPackages.has(packageName)) {
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
processedExternalPackages.add(packageName);
|
|
616
|
+
const resolved = await externalResolver.resolve(packageName);
|
|
617
|
+
if (!resolved.success || !resolved.typesPath) {
|
|
618
|
+
mergedResult.errors.push(`Could not resolve external package for imports: ${packageName}`);
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
// Skip if already visited
|
|
622
|
+
if (visited.has(resolved.typesPath)) {
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
// Extract specific symbols from external package
|
|
626
|
+
const extractedSymbols = await extractSymbolsFromSource(resolved.typesPath, symbols, visited);
|
|
627
|
+
mergedResult = mergeResults(mergedResult, extractedSymbols);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
// Clear re-exports and importedExports from final result (they've been followed)
|
|
631
|
+
mergedResult.reExports = [];
|
|
632
|
+
mergedResult.importedExports = [];
|
|
633
|
+
return mergedResult;
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Creates a TypeExtractor instance
|
|
637
|
+
*/
|
|
638
|
+
export function createTypeExtractor(projectRoot) {
|
|
639
|
+
const externalResolver = projectRoot ? createExternalPackageResolver(projectRoot) : undefined;
|
|
640
|
+
return {
|
|
641
|
+
extract,
|
|
642
|
+
followReExports: (result, basePath, visited) => followReExports(result, basePath, visited, { externalResolver }),
|
|
643
|
+
clearExternalCache: () => externalResolver?.clearCache(),
|
|
644
|
+
getExternalCacheStats: () => externalResolver?.getCacheStats() ?? { resolved: 0, failed: 0 }
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
//# sourceMappingURL=typescript.js.map
|