@codemap-ai/code-index 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/LICENSE +21 -0
- package/dist/file-discovery.d.ts +36 -0
- package/dist/file-discovery.d.ts.map +1 -0
- package/dist/file-discovery.js +204 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/language-utils.d.ts +10 -0
- package/dist/language-utils.d.ts.map +1 -0
- package/dist/language-utils.js +75 -0
- package/dist/parsers/dart.d.ts +4 -0
- package/dist/parsers/dart.d.ts.map +1 -0
- package/dist/parsers/dart.js +353 -0
- package/dist/parsers/index.d.ts +11 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +32 -0
- package/dist/parsers/java.d.ts +4 -0
- package/dist/parsers/java.d.ts.map +1 -0
- package/dist/parsers/java.js +222 -0
- package/dist/parsers/kotlin.d.ts +4 -0
- package/dist/parsers/kotlin.d.ts.map +1 -0
- package/dist/parsers/kotlin.js +221 -0
- package/dist/parsers/php.d.ts +4 -0
- package/dist/parsers/php.d.ts.map +1 -0
- package/dist/parsers/php.js +215 -0
- package/dist/parsers/python.d.ts +4 -0
- package/dist/parsers/python.d.ts.map +1 -0
- package/dist/parsers/python.js +200 -0
- package/dist/parsers/shared.d.ts +30 -0
- package/dist/parsers/shared.d.ts.map +1 -0
- package/dist/parsers/shared.js +200 -0
- package/dist/parsers/types.d.ts +93 -0
- package/dist/parsers/types.d.ts.map +1 -0
- package/dist/parsers/types.js +12 -0
- package/dist/parsers/typescript.d.ts +5 -0
- package/dist/parsers/typescript.d.ts.map +1 -0
- package/dist/parsers/typescript.js +549 -0
- package/dist/ts-resolver.d.ts +17 -0
- package/dist/ts-resolver.d.ts.map +1 -0
- package/dist/ts-resolver.js +110 -0
- package/package.json +30 -0
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseTypeScriptOrJavaScriptFile = parseTypeScriptOrJavaScriptFile;
|
|
7
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
8
|
+
const shared_js_1 = require("./shared.js");
|
|
9
|
+
function createSourceFile(file) {
|
|
10
|
+
return typescript_1.default.createSourceFile(file.baseName, file.content ?? "", typescript_1.default.ScriptTarget.Latest, true, file.extension === "tsx" || file.extension === "jsx"
|
|
11
|
+
? typescript_1.default.ScriptKind.TSX
|
|
12
|
+
: typescript_1.default.ScriptKind.TS);
|
|
13
|
+
}
|
|
14
|
+
function getLineCol(sourceFile, pos) {
|
|
15
|
+
const lc = sourceFile.getLineAndCharacterOfPosition(pos);
|
|
16
|
+
return { line: lc.line + 1, col: lc.character };
|
|
17
|
+
}
|
|
18
|
+
// ─── Import/export extraction ─────────────────────────────────────────────────
|
|
19
|
+
function extractImportsWithAst(file, sourceFile, filePathSet, projectImportId, workspacePath, resolverConfigs) {
|
|
20
|
+
const imports = [];
|
|
21
|
+
const exports = [];
|
|
22
|
+
const issues = [];
|
|
23
|
+
const externalSymbols = [];
|
|
24
|
+
function getNodeRange(node) {
|
|
25
|
+
const start = getLineCol(sourceFile, node.getStart(sourceFile));
|
|
26
|
+
const end = getLineCol(sourceFile, node.getEnd());
|
|
27
|
+
return {
|
|
28
|
+
line: start.line,
|
|
29
|
+
col: start.col,
|
|
30
|
+
endLine: end.line,
|
|
31
|
+
endCol: end.col,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const pushImport = (moduleSpecifier, importKind, isTypeOnly, node, importedNames) => {
|
|
35
|
+
const { line, col, endLine, endCol } = getNodeRange(node);
|
|
36
|
+
const isRelative = moduleSpecifier.startsWith(".");
|
|
37
|
+
const aliasResolution = !isRelative
|
|
38
|
+
? (0, shared_js_1.resolveTsconfigAliasTargetPath)(workspacePath, file.path, moduleSpecifier, file.language, filePathSet, resolverConfigs)
|
|
39
|
+
: null;
|
|
40
|
+
const resolution = isRelative
|
|
41
|
+
? (0, shared_js_1.resolveRelativeTargetPath)(file.path, moduleSpecifier, file.language, filePathSet)
|
|
42
|
+
: (aliasResolution ?? { resolvedPath: null, attemptedPath: null });
|
|
43
|
+
const importLocalKey = (0, shared_js_1.buildImportLocalKey)(file.path, importKind, moduleSpecifier, line, col);
|
|
44
|
+
imports.push({
|
|
45
|
+
localKey: importLocalKey,
|
|
46
|
+
moduleSpecifier,
|
|
47
|
+
importKind,
|
|
48
|
+
isTypeOnly,
|
|
49
|
+
importedNames,
|
|
50
|
+
line,
|
|
51
|
+
col,
|
|
52
|
+
endLine,
|
|
53
|
+
endCol,
|
|
54
|
+
resolutionKind: isRelative
|
|
55
|
+
? resolution.resolvedPath ? "relative_path" : "unresolved"
|
|
56
|
+
: aliasResolution?.resolvedPath ? "tsconfig_alias"
|
|
57
|
+
: aliasResolution?.matched ? "unresolved" : "package",
|
|
58
|
+
targetPathText: resolution.resolvedPath ?? resolution.attemptedPath,
|
|
59
|
+
targetExternalSymbolKey: isRelative || aliasResolution?.matched
|
|
60
|
+
? null
|
|
61
|
+
: `${file.language?.toLowerCase()}:${moduleSpecifier}`,
|
|
62
|
+
});
|
|
63
|
+
if (!isRelative && !aliasResolution?.matched) {
|
|
64
|
+
externalSymbols.push((0, shared_js_1.createExternalSymbolDraft)(projectImportId, file.language, moduleSpecifier));
|
|
65
|
+
}
|
|
66
|
+
else if (!resolution.resolvedPath) {
|
|
67
|
+
issues.push({
|
|
68
|
+
projectImportId,
|
|
69
|
+
severity: "warning",
|
|
70
|
+
code: "UNRESOLVED_IMPORT",
|
|
71
|
+
message: `Unable to resolve module "${moduleSpecifier}" from ${file.path}`,
|
|
72
|
+
detailJson: { filePath: file.path, moduleSpecifier },
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return importLocalKey;
|
|
76
|
+
};
|
|
77
|
+
function getModuleSpecifier(node) {
|
|
78
|
+
const spec = node.moduleSpecifier;
|
|
79
|
+
if (!spec || !typescript_1.default.isStringLiteral(spec))
|
|
80
|
+
return null;
|
|
81
|
+
return spec.text;
|
|
82
|
+
}
|
|
83
|
+
function extractNamedBindings(clause) {
|
|
84
|
+
return clause.elements.map((el) => {
|
|
85
|
+
// el.name is the local alias; el.propertyName is the original — we want the original
|
|
86
|
+
if ("propertyName" in el && el.propertyName) {
|
|
87
|
+
return el.propertyName.text;
|
|
88
|
+
}
|
|
89
|
+
return el.name.text;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
typescript_1.default.forEachChild(sourceFile, (node) => {
|
|
93
|
+
// import ... from '...'
|
|
94
|
+
if (typescript_1.default.isImportDeclaration(node)) {
|
|
95
|
+
const specifier = getModuleSpecifier(node);
|
|
96
|
+
if (!specifier)
|
|
97
|
+
return;
|
|
98
|
+
const isTypeOnly = node.importClause?.isTypeOnly ?? false;
|
|
99
|
+
const clause = node.importClause;
|
|
100
|
+
const importedNames = [];
|
|
101
|
+
if (clause) {
|
|
102
|
+
// import Foo from '...' — default import, no named
|
|
103
|
+
// import { A, B } from '...'
|
|
104
|
+
if (clause.namedBindings) {
|
|
105
|
+
if (typescript_1.default.isNamedImports(clause.namedBindings)) {
|
|
106
|
+
importedNames.push(...extractNamedBindings(clause.namedBindings));
|
|
107
|
+
}
|
|
108
|
+
// import * as ns — namespace import
|
|
109
|
+
if (typescript_1.default.isNamespaceImport(clause.namedBindings)) {
|
|
110
|
+
const nsName = clause.namedBindings.name.text;
|
|
111
|
+
const localKey = pushImport(specifier, "import", isTypeOnly, node, importedNames);
|
|
112
|
+
// patch namespaceName onto the last pushed import
|
|
113
|
+
const last = imports[imports.length - 1];
|
|
114
|
+
if (last && last.localKey === localKey)
|
|
115
|
+
last.namespaceName = nsName;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
pushImport(specifier, "import", isTypeOnly, node, importedNames);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
// export { A } from '...' / export * from '...' / export { A } (local)
|
|
124
|
+
if (typescript_1.default.isExportDeclaration(node)) {
|
|
125
|
+
const specifier = getModuleSpecifier(node);
|
|
126
|
+
const { line, col, endLine, endCol } = getNodeRange(node);
|
|
127
|
+
if (!specifier) {
|
|
128
|
+
// export { A, B } — local, no from
|
|
129
|
+
if (node.exportClause && typescript_1.default.isNamedExports(node.exportClause)) {
|
|
130
|
+
for (const el of node.exportClause.elements) {
|
|
131
|
+
const localName = el.propertyName?.text ?? el.name.text;
|
|
132
|
+
const exportName = el.name.text;
|
|
133
|
+
exports.push({
|
|
134
|
+
exportName,
|
|
135
|
+
exportKind: "named",
|
|
136
|
+
line,
|
|
137
|
+
col,
|
|
138
|
+
endLine,
|
|
139
|
+
endCol,
|
|
140
|
+
symbolLocalKey: (0, shared_js_1.buildLocalSymbolKey)(file.path, "variable", localName),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const isTypeOnly = node.isTypeOnly;
|
|
147
|
+
if (node.exportClause && typescript_1.default.isNamedExports(node.exportClause)) {
|
|
148
|
+
// export { A, B } from '...'
|
|
149
|
+
const importLocalKey = pushImport(specifier, "export_from", isTypeOnly, node, []);
|
|
150
|
+
for (const el of node.exportClause.elements) {
|
|
151
|
+
exports.push({
|
|
152
|
+
exportName: el.name.text,
|
|
153
|
+
exportKind: "re_export",
|
|
154
|
+
line,
|
|
155
|
+
col,
|
|
156
|
+
endLine,
|
|
157
|
+
endCol,
|
|
158
|
+
sourceImportLocalKey: importLocalKey,
|
|
159
|
+
targetExternalSymbolKey: specifier.startsWith(".") ? null : `${file.language?.toLowerCase()}:${specifier}`,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
// export * from '...'
|
|
165
|
+
const importLocalKey = pushImport(specifier, "export_from", isTypeOnly, node, []);
|
|
166
|
+
exports.push({
|
|
167
|
+
exportName: "*",
|
|
168
|
+
exportKind: "wildcard",
|
|
169
|
+
line,
|
|
170
|
+
col,
|
|
171
|
+
endLine,
|
|
172
|
+
endCol,
|
|
173
|
+
sourceImportLocalKey: importLocalKey,
|
|
174
|
+
targetExternalSymbolKey: specifier.startsWith(".") ? null : `${file.language?.toLowerCase()}:${specifier}`,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
// require('...') and import('...') via CallExpression — walk full subtree
|
|
180
|
+
function walkForCalls(n) {
|
|
181
|
+
if (typescript_1.default.isCallExpression(n)) {
|
|
182
|
+
const expr = n.expression;
|
|
183
|
+
const args = n.arguments;
|
|
184
|
+
if (args.length === 1 && typescript_1.default.isStringLiteral(args[0])) {
|
|
185
|
+
const specifier = args[0].text;
|
|
186
|
+
if (typescript_1.default.isIdentifier(expr) && expr.text === "require") {
|
|
187
|
+
pushImport(specifier, "require", false, n, []);
|
|
188
|
+
}
|
|
189
|
+
else if (expr.kind === typescript_1.default.SyntaxKind.ImportKeyword) {
|
|
190
|
+
pushImport(specifier, "dynamic_import", false, n, []);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
typescript_1.default.forEachChild(n, walkForCalls);
|
|
195
|
+
}
|
|
196
|
+
// Only walk statements that aren't import/export declarations (already handled)
|
|
197
|
+
if (!typescript_1.default.isImportDeclaration(node) && !typescript_1.default.isExportDeclaration(node)) {
|
|
198
|
+
walkForCalls(node);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
return { imports, exports, issues, externalSymbols };
|
|
202
|
+
}
|
|
203
|
+
// ─── Symbol extraction ────────────────────────────────────────────────────────
|
|
204
|
+
function extractSymbolsWithAst(file, sourceFile) {
|
|
205
|
+
const content = file.content ?? "";
|
|
206
|
+
const symbols = [];
|
|
207
|
+
const relationships = [];
|
|
208
|
+
const lines = content.split(/\r?\n/);
|
|
209
|
+
function getSignature(node) {
|
|
210
|
+
const { line } = getLineCol(sourceFile, node.getStart(sourceFile));
|
|
211
|
+
return (lines[line - 1] ?? "").trim().slice(0, 200);
|
|
212
|
+
}
|
|
213
|
+
function getReturnType(node) {
|
|
214
|
+
if (typescript_1.default.isFunctionDeclaration(node) ||
|
|
215
|
+
typescript_1.default.isMethodDeclaration(node) ||
|
|
216
|
+
typescript_1.default.isFunctionExpression(node) ||
|
|
217
|
+
typescript_1.default.isArrowFunction(node)) {
|
|
218
|
+
return node.type ? node.type.getText(sourceFile).slice(0, 200) : null;
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
function getJSDoc(node) {
|
|
223
|
+
const tags = typescript_1.default.getJSDocCommentsAndTags(node);
|
|
224
|
+
if (tags.length === 0)
|
|
225
|
+
return null;
|
|
226
|
+
const parts = [];
|
|
227
|
+
for (const tag of tags) {
|
|
228
|
+
if (typescript_1.default.isJSDoc(tag) && tag.comment) {
|
|
229
|
+
const text = typeof tag.comment === "string"
|
|
230
|
+
? tag.comment
|
|
231
|
+
: tag.comment.map((c) => c.text).join("");
|
|
232
|
+
if (text.trim())
|
|
233
|
+
parts.push(text.trim());
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return parts.length > 0 ? parts.join("\n").slice(0, 500) : null;
|
|
237
|
+
}
|
|
238
|
+
function pushSymbol(name, kind, node, isExported, isDefaultExport) {
|
|
239
|
+
const nameStart = content.indexOf(name, node.getStart(sourceFile));
|
|
240
|
+
const { line, col } = nameStart >= 0
|
|
241
|
+
? getLineCol(sourceFile, nameStart)
|
|
242
|
+
: getLineCol(sourceFile, node.getStart(sourceFile));
|
|
243
|
+
const end = getLineCol(sourceFile, node.getEnd());
|
|
244
|
+
symbols.push({
|
|
245
|
+
localKey: (0, shared_js_1.buildLocalSymbolKey)(file.path, kind, name),
|
|
246
|
+
stableKey: (0, shared_js_1.buildStableSymbolKey)(file.path, kind, name, line),
|
|
247
|
+
displayName: name,
|
|
248
|
+
kind,
|
|
249
|
+
language: file.language,
|
|
250
|
+
signature: getSignature(node),
|
|
251
|
+
returnType: getReturnType(node),
|
|
252
|
+
doc: getJSDoc(node),
|
|
253
|
+
isExported,
|
|
254
|
+
isDefaultExport,
|
|
255
|
+
line,
|
|
256
|
+
col,
|
|
257
|
+
endLine: end.line,
|
|
258
|
+
endCol: end.col,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
// ─── Factory return-object method extraction (heuristic #1 + type-driven #2) ──
|
|
262
|
+
function getReturnedObjectLiteral(body) {
|
|
263
|
+
if (!body)
|
|
264
|
+
return null;
|
|
265
|
+
// Arrow function shorthand: () => ({ ... })
|
|
266
|
+
if (typescript_1.default.isParenthesizedExpression(body)) {
|
|
267
|
+
const inner = body.expression;
|
|
268
|
+
return typescript_1.default.isObjectLiteralExpression(inner) ? inner : null;
|
|
269
|
+
}
|
|
270
|
+
if (typescript_1.default.isObjectLiteralExpression(body))
|
|
271
|
+
return body;
|
|
272
|
+
if (!typescript_1.default.isBlock(body))
|
|
273
|
+
return null;
|
|
274
|
+
// Block body: find the last return statement
|
|
275
|
+
let returnedObj = null;
|
|
276
|
+
for (const stmt of body.statements) {
|
|
277
|
+
if (typescript_1.default.isReturnStatement(stmt) && stmt.expression) {
|
|
278
|
+
const expr = typescript_1.default.isParenthesizedExpression(stmt.expression)
|
|
279
|
+
? stmt.expression.expression
|
|
280
|
+
: stmt.expression;
|
|
281
|
+
if (typescript_1.default.isObjectLiteralExpression(expr)) {
|
|
282
|
+
returnedObj = expr;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return returnedObj;
|
|
287
|
+
}
|
|
288
|
+
// Heuristic #2: if the function has a return type annotation that is a TypeLiteral
|
|
289
|
+
// (e.g., ): { foo(): void; bar(): string }), extract allowed method names from it.
|
|
290
|
+
// Returns null if no type annotation or annotation is not a resolvable object type.
|
|
291
|
+
function getAllowedNamesFromReturnType(node) {
|
|
292
|
+
if (!node.type)
|
|
293
|
+
return null;
|
|
294
|
+
// ): { method1(): T; method2(): U }
|
|
295
|
+
if (typescript_1.default.isTypeLiteralNode(node.type)) {
|
|
296
|
+
const names = new Set();
|
|
297
|
+
for (const member of node.type.members) {
|
|
298
|
+
if ((typescript_1.default.isMethodSignature(member) || typescript_1.default.isPropertySignature(member)) &&
|
|
299
|
+
typescript_1.default.isIdentifier(member.name)) {
|
|
300
|
+
names.add(member.name.text);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return names.size > 0 ? names : null;
|
|
304
|
+
}
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
function extractReturnObjectMethods(fnNode, parentLocalKey, isParentExported) {
|
|
308
|
+
const body = fnNode.body;
|
|
309
|
+
// Extract inner function declarations defined inside the factory body (private helpers).
|
|
310
|
+
// Use line number as disambiguator so two factories in the same file can have
|
|
311
|
+
// identically-named helpers without a localKey collision.
|
|
312
|
+
const blockBody = body && "statements" in body ? body : null;
|
|
313
|
+
if (blockBody) {
|
|
314
|
+
for (const stmt of blockBody.statements) {
|
|
315
|
+
if (typescript_1.default.isFunctionDeclaration(stmt) && stmt.name) {
|
|
316
|
+
const name = stmt.name.text;
|
|
317
|
+
const { line, col } = getLineCol(sourceFile, stmt.getStart(sourceFile));
|
|
318
|
+
const end = getLineCol(sourceFile, stmt.getEnd());
|
|
319
|
+
const uniqueName = `${name}@${line}`;
|
|
320
|
+
symbols.push({
|
|
321
|
+
localKey: (0, shared_js_1.buildLocalSymbolKey)(file.path, "function", uniqueName),
|
|
322
|
+
stableKey: (0, shared_js_1.buildStableSymbolKey)(file.path, "function", uniqueName, line),
|
|
323
|
+
displayName: name,
|
|
324
|
+
kind: "function",
|
|
325
|
+
language: file.language,
|
|
326
|
+
signature: getSignature(stmt),
|
|
327
|
+
returnType: getReturnType(stmt),
|
|
328
|
+
doc: getJSDoc(stmt),
|
|
329
|
+
isExported: false,
|
|
330
|
+
isDefaultExport: false,
|
|
331
|
+
line,
|
|
332
|
+
col,
|
|
333
|
+
endLine: end.line,
|
|
334
|
+
endCol: end.col,
|
|
335
|
+
parentSymbolLocalKey: parentLocalKey,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
const obj = getReturnedObjectLiteral(body);
|
|
341
|
+
if (!obj)
|
|
342
|
+
return;
|
|
343
|
+
const allowedNames = getAllowedNamesFromReturnType(fnNode);
|
|
344
|
+
for (const prop of obj.properties) {
|
|
345
|
+
let name = null;
|
|
346
|
+
let methodNode = prop;
|
|
347
|
+
let isMethod = false;
|
|
348
|
+
if (typescript_1.default.isMethodDeclaration(prop) && typescript_1.default.isIdentifier(prop.name)) {
|
|
349
|
+
name = prop.name.text;
|
|
350
|
+
isMethod = true;
|
|
351
|
+
}
|
|
352
|
+
else if (typescript_1.default.isPropertyAssignment(prop) && typescript_1.default.isIdentifier(prop.name)) {
|
|
353
|
+
const init = prop.initializer;
|
|
354
|
+
if (typescript_1.default.isArrowFunction(init) || typescript_1.default.isFunctionExpression(init)) {
|
|
355
|
+
name = prop.name.text;
|
|
356
|
+
methodNode = prop;
|
|
357
|
+
isMethod = true;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
else if (typescript_1.default.isShorthandPropertyAssignment(prop)) {
|
|
361
|
+
// { foo } — reference to existing symbol, not a new method definition, skip
|
|
362
|
+
}
|
|
363
|
+
if (!name || !isMethod)
|
|
364
|
+
continue;
|
|
365
|
+
// If type annotation present, only index names that appear in it
|
|
366
|
+
if (allowedNames && !allowedNames.has(name))
|
|
367
|
+
continue;
|
|
368
|
+
const { line, col } = getLineCol(sourceFile, methodNode.getStart(sourceFile));
|
|
369
|
+
const end = getLineCol(sourceFile, methodNode.getEnd());
|
|
370
|
+
symbols.push({
|
|
371
|
+
localKey: (0, shared_js_1.buildLocalSymbolKey)(file.path, "method", name),
|
|
372
|
+
stableKey: (0, shared_js_1.buildStableSymbolKey)(file.path, "method", name, line),
|
|
373
|
+
displayName: name,
|
|
374
|
+
kind: "method",
|
|
375
|
+
language: file.language,
|
|
376
|
+
signature: getSignature(methodNode),
|
|
377
|
+
returnType: getReturnType(typescript_1.default.isMethodDeclaration(prop)
|
|
378
|
+
? prop
|
|
379
|
+
: typescript_1.default.isPropertyAssignment(prop)
|
|
380
|
+
? prop.initializer
|
|
381
|
+
: prop),
|
|
382
|
+
doc: getJSDoc(prop),
|
|
383
|
+
isExported: isParentExported,
|
|
384
|
+
isDefaultExport: false,
|
|
385
|
+
line,
|
|
386
|
+
col,
|
|
387
|
+
endLine: end.line,
|
|
388
|
+
endCol: end.col,
|
|
389
|
+
parentSymbolLocalKey: parentLocalKey,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
394
|
+
function visitTopLevel(node) {
|
|
395
|
+
if (typescript_1.default.isExportAssignment(node)) {
|
|
396
|
+
const expr = node.expression;
|
|
397
|
+
if ((typescript_1.default.isFunctionExpression(expr) || typescript_1.default.isArrowFunction(expr)) &&
|
|
398
|
+
typescript_1.default.isIdentifier(expr.name ?? null)) {
|
|
399
|
+
const name = expr.name.text;
|
|
400
|
+
pushSymbol(name, "function", node, true, true);
|
|
401
|
+
}
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
if (!typescript_1.default.isStatement(node))
|
|
405
|
+
return;
|
|
406
|
+
const modifiers = typescript_1.default.canHaveModifiers(node) ? typescript_1.default.getModifiers(node) : undefined;
|
|
407
|
+
const isExported = modifiers?.some((m) => m.kind === typescript_1.default.SyntaxKind.ExportKeyword) ?? false;
|
|
408
|
+
const isDefault = modifiers?.some((m) => m.kind === typescript_1.default.SyntaxKind.DefaultKeyword) ?? false;
|
|
409
|
+
if (typescript_1.default.isFunctionDeclaration(node) && node.name) {
|
|
410
|
+
const name = node.name.text;
|
|
411
|
+
pushSymbol(name, "function", node, isExported, isDefault);
|
|
412
|
+
const localKey = (0, shared_js_1.buildLocalSymbolKey)(file.path, "function", name);
|
|
413
|
+
extractReturnObjectMethods(node, localKey, isExported);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (typescript_1.default.isClassDeclaration(node) && node.name) {
|
|
417
|
+
const name = node.name.text;
|
|
418
|
+
pushSymbol(name, "class", node, isExported, isDefault);
|
|
419
|
+
const localKey = (0, shared_js_1.buildLocalSymbolKey)(file.path, "class", name);
|
|
420
|
+
for (const clause of node.heritageClauses ?? []) {
|
|
421
|
+
const kind = clause.token === typescript_1.default.SyntaxKind.ExtendsKeyword ? "extends" : "implements";
|
|
422
|
+
for (const type of clause.types) {
|
|
423
|
+
const targetName = type.expression.getText(sourceFile);
|
|
424
|
+
relationships.push({ fromSymbolLocalKey: localKey, toSymbolName: targetName, relationshipKind: kind });
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (typescript_1.default.isInterfaceDeclaration(node)) {
|
|
430
|
+
const name = node.name.text;
|
|
431
|
+
pushSymbol(name, "interface", node, isExported, false);
|
|
432
|
+
const localKey = (0, shared_js_1.buildLocalSymbolKey)(file.path, "interface", name);
|
|
433
|
+
for (const clause of node.heritageClauses ?? []) {
|
|
434
|
+
for (const type of clause.types) {
|
|
435
|
+
const targetName = type.expression.getText(sourceFile);
|
|
436
|
+
relationships.push({ fromSymbolLocalKey: localKey, toSymbolName: targetName, relationshipKind: "extends" });
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
if (typescript_1.default.isTypeAliasDeclaration(node)) {
|
|
442
|
+
pushSymbol(node.name.text, "type_alias", node, isExported, false);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (typescript_1.default.isEnumDeclaration(node)) {
|
|
446
|
+
pushSymbol(node.name.text, "enum", node, isExported, false);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
if (typescript_1.default.isVariableStatement(node) && isExported) {
|
|
450
|
+
for (const decl of node.declarationList.declarations) {
|
|
451
|
+
if (!typescript_1.default.isIdentifier(decl.name))
|
|
452
|
+
continue;
|
|
453
|
+
const name = decl.name.text;
|
|
454
|
+
const isPascalCase = /^[A-Z]/.test(name);
|
|
455
|
+
let kind = "variable";
|
|
456
|
+
if (isPascalCase) {
|
|
457
|
+
kind = "component";
|
|
458
|
+
}
|
|
459
|
+
else if (decl.initializer &&
|
|
460
|
+
(typescript_1.default.isArrowFunction(decl.initializer) || typescript_1.default.isFunctionExpression(decl.initializer))) {
|
|
461
|
+
kind = "function";
|
|
462
|
+
}
|
|
463
|
+
pushSymbol(name, kind, decl, isExported, false);
|
|
464
|
+
if (decl.initializer &&
|
|
465
|
+
(typescript_1.default.isArrowFunction(decl.initializer) || typescript_1.default.isFunctionExpression(decl.initializer))) {
|
|
466
|
+
const localKey = (0, shared_js_1.buildLocalSymbolKey)(file.path, kind, name);
|
|
467
|
+
extractReturnObjectMethods(decl.initializer, localKey, isExported);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
typescript_1.default.forEachChild(sourceFile, visitTopLevel);
|
|
473
|
+
return { symbols, relationships };
|
|
474
|
+
}
|
|
475
|
+
// ─── Call extraction ─────────────────────────────────────────────────────────
|
|
476
|
+
function extractCallsWithAst(file, sourceFile) {
|
|
477
|
+
const calls = [];
|
|
478
|
+
function walk(node) {
|
|
479
|
+
if (typescript_1.default.isCallExpression(node)) {
|
|
480
|
+
const expr = node.expression;
|
|
481
|
+
let calleeName = null;
|
|
482
|
+
if (typescript_1.default.isIdentifier(expr)) {
|
|
483
|
+
calleeName = expr.text;
|
|
484
|
+
}
|
|
485
|
+
else if (typescript_1.default.isPropertyAccessExpression(expr) && typescript_1.default.isIdentifier(expr.name)) {
|
|
486
|
+
calleeName = expr.name.text;
|
|
487
|
+
}
|
|
488
|
+
if (calleeName && calleeName !== "require") {
|
|
489
|
+
const { line, col } = getLineCol(sourceFile, expr.getStart(sourceFile));
|
|
490
|
+
const namespaceName = typescript_1.default.isPropertyAccessExpression(expr) && typescript_1.default.isIdentifier(expr.expression)
|
|
491
|
+
? expr.expression.text
|
|
492
|
+
: undefined;
|
|
493
|
+
calls.push({ calleeName, namespaceName, line, col, endCol: col + calleeName.length });
|
|
494
|
+
}
|
|
495
|
+
// don't skip children — call args may contain more references
|
|
496
|
+
}
|
|
497
|
+
else if (typescript_1.default.isPropertyAccessExpression(node) && typescript_1.default.isIdentifier(node.expression)) {
|
|
498
|
+
// standalone property access: CACHE_NAMESPACES.foo (not the expression of a call — already handled above)
|
|
499
|
+
const objectName = node.expression.text;
|
|
500
|
+
const { line, col } = getLineCol(sourceFile, node.expression.getStart(sourceFile));
|
|
501
|
+
calls.push({ calleeName: objectName, namespaceName: undefined, line, col, endCol: col + objectName.length });
|
|
502
|
+
}
|
|
503
|
+
else if (typescript_1.default.isIdentifier(node) &&
|
|
504
|
+
!typescript_1.default.isPropertyAccessExpression(node.parent) &&
|
|
505
|
+
!typescript_1.default.isCallExpression(node.parent) &&
|
|
506
|
+
!typescript_1.default.isImportDeclaration(node.parent) &&
|
|
507
|
+
!typescript_1.default.isImportSpecifier(node.parent) &&
|
|
508
|
+
!typescript_1.default.isVariableDeclaration(node.parent) &&
|
|
509
|
+
!typescript_1.default.isParameter(node.parent) &&
|
|
510
|
+
!typescript_1.default.isPropertyAssignment(node.parent) &&
|
|
511
|
+
!typescript_1.default.isShorthandPropertyAssignment(node.parent) &&
|
|
512
|
+
!typescript_1.default.isTypeReferenceNode(node.parent) &&
|
|
513
|
+
!typescript_1.default.isExportSpecifier(node.parent)) {
|
|
514
|
+
// bare identifier reference: CACHE_PREFIX used standalone (array element, argument, etc.)
|
|
515
|
+
const { line, col } = getLineCol(sourceFile, node.getStart(sourceFile));
|
|
516
|
+
calls.push({ calleeName: node.text, namespaceName: undefined, line, col, endCol: col + node.text.length });
|
|
517
|
+
}
|
|
518
|
+
typescript_1.default.forEachChild(node, walk);
|
|
519
|
+
}
|
|
520
|
+
typescript_1.default.forEachChild(sourceFile, walk);
|
|
521
|
+
return calls;
|
|
522
|
+
}
|
|
523
|
+
// ─── Entry point ──────────────────────────────────────────────────────────────
|
|
524
|
+
function parseTypeScriptOrJavaScriptFile(file, filePathSet, projectImportId, workspacePath, resolverConfigs) {
|
|
525
|
+
const sourceFile = createSourceFile(file);
|
|
526
|
+
const { imports, exports, issues, externalSymbols } = extractImportsWithAst(file, sourceFile, filePathSet, projectImportId, workspacePath, resolverConfigs);
|
|
527
|
+
const { symbols: astSymbols, relationships } = extractSymbolsWithAst(file, sourceFile);
|
|
528
|
+
const calls = extractCallsWithAst(file, sourceFile);
|
|
529
|
+
const symbolExports = astSymbols
|
|
530
|
+
.filter((s) => s.isExported)
|
|
531
|
+
.map((s) => ({
|
|
532
|
+
exportName: s.isDefaultExport ? "default" : s.displayName,
|
|
533
|
+
exportKind: s.isDefaultExport ? "default" : "named",
|
|
534
|
+
line: s.line,
|
|
535
|
+
col: s.col,
|
|
536
|
+
endLine: s.endLine,
|
|
537
|
+
endCol: s.endCol,
|
|
538
|
+
symbolLocalKey: s.localKey,
|
|
539
|
+
}));
|
|
540
|
+
return {
|
|
541
|
+
symbols: astSymbols,
|
|
542
|
+
imports,
|
|
543
|
+
exports: [...exports, ...symbolExports],
|
|
544
|
+
relationships,
|
|
545
|
+
calls,
|
|
546
|
+
issues,
|
|
547
|
+
externalSymbols,
|
|
548
|
+
};
|
|
549
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface TypeScriptPathAliasPattern {
|
|
2
|
+
pattern: string;
|
|
3
|
+
hasWildcard: boolean;
|
|
4
|
+
prefix: string;
|
|
5
|
+
suffix: string;
|
|
6
|
+
targets: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface TypeScriptResolverConfig {
|
|
9
|
+
configPath: string;
|
|
10
|
+
configDirPath: string;
|
|
11
|
+
configDirRelativePath: string;
|
|
12
|
+
baseUrlPath: string;
|
|
13
|
+
pathAliases: TypeScriptPathAliasPattern[];
|
|
14
|
+
}
|
|
15
|
+
export declare function normalizeWorkspaceRelativePath(workspacePath: string, targetPath: string): string;
|
|
16
|
+
export declare function loadTypeScriptResolverConfigs(workspacePath: string): Promise<TypeScriptResolverConfig[]>;
|
|
17
|
+
//# sourceMappingURL=ts-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ts-resolver.d.ts","sourceRoot":"","sources":["../src/ts-resolver.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,0BAA0B,EAAE,CAAC;CAC3C;AAOD,wBAAgB,8BAA8B,CAC5C,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,UAKnB;AA2CD,wBAAsB,6BAA6B,CACjD,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAwErC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.normalizeWorkspaceRelativePath = normalizeWorkspaceRelativePath;
|
|
7
|
+
exports.loadTypeScriptResolverConfigs = loadTypeScriptResolverConfigs;
|
|
8
|
+
const promises_1 = require("node:fs/promises");
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
11
|
+
const file_discovery_js_1 = require("./file-discovery.js");
|
|
12
|
+
function normalizeWorkspaceRelativePath(workspacePath, targetPath) {
|
|
13
|
+
const relativePath = node_path_1.default.relative(workspacePath, targetPath);
|
|
14
|
+
if (!relativePath)
|
|
15
|
+
return "";
|
|
16
|
+
return (0, file_discovery_js_1.normalizeRepositoryFilePath)(relativePath.split(node_path_1.default.sep).join("/"));
|
|
17
|
+
}
|
|
18
|
+
function readTsConfigFile(configPath) {
|
|
19
|
+
const readResult = typescript_1.default.readConfigFile(configPath, typescript_1.default.sys.readFile);
|
|
20
|
+
if (readResult.error)
|
|
21
|
+
return null;
|
|
22
|
+
return readResult.config;
|
|
23
|
+
}
|
|
24
|
+
async function readTypeScriptCompilerOptionsConfig(configPath, visited = new Set()) {
|
|
25
|
+
if (visited.has(configPath))
|
|
26
|
+
return null;
|
|
27
|
+
visited.add(configPath);
|
|
28
|
+
try {
|
|
29
|
+
const parsed = readTsConfigFile(configPath);
|
|
30
|
+
const configDirPath = node_path_1.default.dirname(configPath);
|
|
31
|
+
let inheritedConfig = null;
|
|
32
|
+
if (parsed?.extends && parsed?.extends.startsWith(".")) {
|
|
33
|
+
const extendsPath = parsed.extends.endsWith(".json")
|
|
34
|
+
? parsed.extends
|
|
35
|
+
: `${parsed.extends}.json`;
|
|
36
|
+
inheritedConfig = await readTypeScriptCompilerOptionsConfig(node_path_1.default.resolve(configDirPath, extendsPath), visited);
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
baseUrl: parsed?.compilerOptions?.baseUrl ?? inheritedConfig?.baseUrl,
|
|
40
|
+
paths: parsed?.compilerOptions?.paths ?? inheritedConfig?.paths,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.log(error);
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function loadTypeScriptResolverConfigs(workspacePath) {
|
|
49
|
+
const configRelativePaths = ["tsconfig.json", "jsconfig.json"];
|
|
50
|
+
const discoveredConfigs = new Set();
|
|
51
|
+
async function visit(absolutePath) {
|
|
52
|
+
const entryStats = await (0, promises_1.lstat)(absolutePath);
|
|
53
|
+
if (entryStats.isSymbolicLink())
|
|
54
|
+
return;
|
|
55
|
+
const name = node_path_1.default.basename(absolutePath);
|
|
56
|
+
if (entryStats.isDirectory()) {
|
|
57
|
+
if (file_discovery_js_1.IGNORED_NAMES.has(name))
|
|
58
|
+
return;
|
|
59
|
+
const entries = await (0, promises_1.readdir)(absolutePath, { withFileTypes: true });
|
|
60
|
+
for (const entry of entries) {
|
|
61
|
+
if (entry.isDirectory()) {
|
|
62
|
+
await visit(node_path_1.default.join(absolutePath, entry.name));
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (entry.isFile() && (entry.name === "tsconfig.json" || entry.name === "jsconfig.json")) {
|
|
66
|
+
discoveredConfigs.add(node_path_1.default.join(absolutePath, entry.name));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
for (const relativePath of configRelativePaths) {
|
|
72
|
+
discoveredConfigs.add(node_path_1.default.join(workspacePath, relativePath));
|
|
73
|
+
}
|
|
74
|
+
await visit(workspacePath);
|
|
75
|
+
const resolverConfigs = [];
|
|
76
|
+
for (const configPath of discoveredConfigs) {
|
|
77
|
+
try {
|
|
78
|
+
const compilerOptions = await readTypeScriptCompilerOptionsConfig(configPath);
|
|
79
|
+
if (!compilerOptions?.paths)
|
|
80
|
+
continue;
|
|
81
|
+
const configDirPath = node_path_1.default.dirname(configPath);
|
|
82
|
+
const baseUrlPath = node_path_1.default.resolve(configDirPath, compilerOptions.baseUrl ?? ".");
|
|
83
|
+
const pathAliases = Object.entries(compilerOptions.paths ?? {})
|
|
84
|
+
.filter(([, targets]) => Array.isArray(targets) && targets.length > 0)
|
|
85
|
+
.map(([pattern, targets]) => {
|
|
86
|
+
const wildcardIndex = pattern.indexOf("*");
|
|
87
|
+
return {
|
|
88
|
+
pattern,
|
|
89
|
+
hasWildcard: wildcardIndex >= 0,
|
|
90
|
+
prefix: wildcardIndex >= 0 ? pattern.slice(0, wildcardIndex) : pattern,
|
|
91
|
+
suffix: wildcardIndex >= 0 ? pattern.slice(wildcardIndex + 1) : "",
|
|
92
|
+
targets,
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
if (pathAliases.length === 0)
|
|
96
|
+
continue;
|
|
97
|
+
resolverConfigs.push({
|
|
98
|
+
configPath,
|
|
99
|
+
configDirPath,
|
|
100
|
+
configDirRelativePath: normalizeWorkspaceRelativePath(workspacePath, configDirPath),
|
|
101
|
+
baseUrlPath,
|
|
102
|
+
pathAliases,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.log(`Failed to read TypeScript resolver config at ${configPath}`, error);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return resolverConfigs.sort((left, right) => right.configDirPath.length - left.configDirPath.length);
|
|
110
|
+
}
|