lsp-intelligence 0.2.1 → 0.3.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/dist/adapters/express/index.d.ts +9 -0
- package/dist/adapters/express/index.js +46 -0
- package/dist/adapters/express/index.js.map +1 -0
- package/dist/adapters/next/index.d.ts +10 -0
- package/dist/adapters/next/index.js +67 -0
- package/dist/adapters/next/index.js.map +1 -0
- package/dist/adapters/react/index.d.ts +11 -0
- package/dist/adapters/react/index.js +133 -0
- package/dist/adapters/react/index.js.map +1 -0
- package/dist/adapters/registry.d.ts +22 -0
- package/dist/adapters/registry.js +101 -0
- package/dist/adapters/registry.js.map +1 -0
- package/dist/adapters/types.d.ts +97 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/analysis/ts/applyVirtualEdit.d.ts +27 -0
- package/dist/analysis/ts/applyVirtualEdit.js +235 -0
- package/dist/analysis/ts/applyVirtualEdit.js.map +1 -0
- package/dist/analysis/ts/changeRecipes.d.ts +52 -0
- package/dist/analysis/ts/changeRecipes.js +14 -0
- package/dist/analysis/ts/changeRecipes.js.map +1 -0
- package/dist/analysis/ts/compatibility.d.ts +54 -0
- package/dist/analysis/ts/compatibility.js +113 -0
- package/dist/analysis/ts/compatibility.js.map +1 -0
- package/dist/analysis/ts/diffDeclarationShape.d.ts +26 -0
- package/dist/analysis/ts/diffDeclarationShape.js +114 -0
- package/dist/analysis/ts/diffDeclarationShape.js.map +1 -0
- package/dist/analysis/ts/exhaustiveness.d.ts +49 -0
- package/dist/analysis/ts/exhaustiveness.js +158 -0
- package/dist/analysis/ts/exhaustiveness.js.map +1 -0
- package/dist/analysis/ts/extractDeclarationShape.d.ts +30 -0
- package/dist/analysis/ts/extractDeclarationShape.js +127 -0
- package/dist/analysis/ts/extractDeclarationShape.js.map +1 -0
- package/dist/analysis/ts/extractExports.d.ts +19 -0
- package/dist/analysis/ts/extractExports.js +178 -0
- package/dist/analysis/ts/extractExports.js.map +1 -0
- package/dist/analysis/ts/extractRoutes.d.ts +12 -0
- package/dist/analysis/ts/extractRoutes.js +165 -0
- package/dist/analysis/ts/extractRoutes.js.map +1 -0
- package/dist/analysis/ts/parseSourceFile.d.ts +5 -1
- package/dist/analysis/ts/parseSourceFile.js +6 -2
- package/dist/analysis/ts/parseSourceFile.js.map +1 -1
- package/dist/analysis/ts/program/CheckerQueries.d.ts +56 -0
- package/dist/analysis/ts/program/CheckerQueries.js +187 -0
- package/dist/analysis/ts/program/CheckerQueries.js.map +1 -0
- package/dist/analysis/ts/program/ProgramManager.d.ts +27 -0
- package/dist/analysis/ts/program/ProgramManager.js +147 -0
- package/dist/analysis/ts/program/ProgramManager.js.map +1 -0
- package/dist/analysis/ts/program/TypeFacts.d.ts +58 -0
- package/dist/analysis/ts/program/TypeFacts.js +68 -0
- package/dist/analysis/ts/program/TypeFacts.js.map +1 -0
- package/dist/analysis/ts/typeState.d.ts +46 -0
- package/dist/analysis/ts/typeState.js +108 -0
- package/dist/analysis/ts/typeState.js.map +1 -0
- package/dist/ast/diffDeclarationShapes.js +25 -11
- package/dist/ast/diffDeclarationShapes.js.map +1 -1
- package/dist/ast/extractExportDeclarations.js +8 -3
- package/dist/ast/extractExportDeclarations.js.map +1 -1
- package/dist/cache/CacheSchema.d.ts +30 -0
- package/dist/cache/CacheSchema.js +9 -0
- package/dist/cache/CacheSchema.js.map +1 -0
- package/dist/cache/CacheStore.d.ts +22 -0
- package/dist/cache/CacheStore.js +111 -0
- package/dist/cache/CacheStore.js.map +1 -0
- package/dist/cache/SemanticCache.d.ts +38 -0
- package/dist/cache/SemanticCache.js +87 -0
- package/dist/cache/SemanticCache.js.map +1 -0
- package/dist/cache/SnapshotFingerprint.d.ts +17 -0
- package/dist/cache/SnapshotFingerprint.js +14 -0
- package/dist/cache/SnapshotFingerprint.js.map +1 -0
- package/dist/engine/DocumentManager.d.ts +16 -0
- package/dist/engine/DocumentManager.js +32 -0
- package/dist/engine/DocumentManager.js.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/search/adapters/http.d.ts +9 -3
- package/dist/search/adapters/http.js +7 -52
- package/dist/search/adapters/http.js.map +1 -1
- package/dist/search/adapters/react.d.ts +8 -8
- package/dist/search/adapters/react.js +3 -88
- package/dist/search/adapters/react.js.map +1 -1
- package/dist/search/adapters/registry.d.ts +7 -4
- package/dist/search/adapters/registry.js +7 -17
- package/dist/search/adapters/registry.js.map +1 -1
- package/dist/search/expand/graphExpansion.js +47 -14
- package/dist/search/expand/graphExpansion.js.map +1 -1
- package/dist/search/index/declarationIndex.d.ts +3 -1
- package/dist/search/index/declarationIndex.js +4 -2
- package/dist/search/index/declarationIndex.js.map +1 -1
- package/dist/search/index/routeIndex.d.ts +6 -0
- package/dist/search/index/routeIndex.js +19 -0
- package/dist/search/index/routeIndex.js.map +1 -0
- package/dist/search/index/usageIndex.d.ts +3 -1
- package/dist/search/index/usageIndex.js +4 -2
- package/dist/search/index/usageIndex.js.map +1 -1
- package/dist/search/index/workspaceIndex.d.ts +7 -1
- package/dist/search/index/workspaceIndex.js +54 -14
- package/dist/search/index/workspaceIndex.js.map +1 -1
- package/dist/search/query/compileEffectiveSearchSpec.js +9 -0
- package/dist/search/query/compileEffectiveSearchSpec.js.map +1 -1
- package/dist/search/query/parseQuery.js +5 -1
- package/dist/search/query/parseQuery.js.map +1 -1
- package/dist/search/query/planQuery.js +14 -4
- package/dist/search/query/planQuery.js.map +1 -1
- package/dist/search/ranking/mergeCandidates.d.ts +1 -0
- package/dist/search/ranking/mergeCandidates.js +16 -0
- package/dist/search/ranking/mergeCandidates.js.map +1 -1
- package/dist/search/retrievers/routeRetriever.d.ts +7 -0
- package/dist/search/retrievers/routeRetriever.js +64 -0
- package/dist/search/retrievers/routeRetriever.js.map +1 -0
- package/dist/search/types.d.ts +16 -4
- package/dist/session/OverlayStore.d.ts +15 -0
- package/dist/session/OverlayStore.js +46 -0
- package/dist/session/OverlayStore.js.map +1 -0
- package/dist/session/SnapshotResolver.d.ts +31 -0
- package/dist/session/SnapshotResolver.js +50 -0
- package/dist/session/SnapshotResolver.js.map +1 -0
- package/dist/session/WorkspaceSnapshot.d.ts +21 -0
- package/dist/session/WorkspaceSnapshot.js +2 -0
- package/dist/session/WorkspaceSnapshot.js.map +1 -0
- package/dist/tools/composites/apiGuard.d.ts +4 -0
- package/dist/tools/composites/apiGuard.js +158 -24
- package/dist/tools/composites/apiGuard.js.map +1 -1
- package/dist/tools/composites/findCode.js +12 -2
- package/dist/tools/composites/findCode.js.map +1 -1
- package/dist/tools/composites/rootCauseTrace.js +89 -13
- package/dist/tools/composites/rootCauseTrace.js.map +1 -1
- package/dist/workflows/simulateChange.d.ts +44 -0
- package/dist/workflows/simulateChange.js +194 -0
- package/dist/workflows/simulateChange.js.map +1 -0
- package/dist/workflows/verifyChangeSet.d.ts +58 -0
- package/dist/workflows/verifyChangeSet.js +300 -0
- package/dist/workflows/verifyChangeSet.js.map +1 -0
- package/package.json +6 -4
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
const HTTP_METHODS = new Set(['get', 'post', 'put', 'patch', 'delete', 'options', 'head', 'all', 'use']);
|
|
4
|
+
const NEXT_HANDLER_NAMES = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD']);
|
|
5
|
+
/**
|
|
6
|
+
* Extract route definitions from a TypeScript/JavaScript source file.
|
|
7
|
+
*
|
|
8
|
+
* Supports:
|
|
9
|
+
* - Express/Fastify: app.get('/path', handler), router.post(...)
|
|
10
|
+
* - Next.js App Router: exported GET/POST/etc in route.ts files
|
|
11
|
+
* - Next.js Pages API: default export in pages/api/** files
|
|
12
|
+
* - Route maps: object literals with path/method/handler fields
|
|
13
|
+
*/
|
|
14
|
+
export function extractRoutes(sf) {
|
|
15
|
+
const entries = [];
|
|
16
|
+
const filePath = sf.fileName;
|
|
17
|
+
const basename = path.basename(filePath, path.extname(filePath));
|
|
18
|
+
const isRouteFile = basename === 'route';
|
|
19
|
+
const isPagesApi = filePath.includes('/pages/api/') || filePath.includes('/pages\\api\\');
|
|
20
|
+
function visit(node) {
|
|
21
|
+
// A. Express/Fastify-style: app.get('/path', handler)
|
|
22
|
+
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
|
|
23
|
+
const method = node.expression.name.text.toLowerCase();
|
|
24
|
+
if (HTTP_METHODS.has(method) && node.arguments.length >= 1) {
|
|
25
|
+
const firstArg = node.arguments[0];
|
|
26
|
+
if (ts.isStringLiteral(firstArg) || ts.isNoSubstitutionTemplateLiteral(firstArg)) {
|
|
27
|
+
const routePath = firstArg.text;
|
|
28
|
+
const line = sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
|
|
29
|
+
const callee = getCalleeText(node.expression.expression);
|
|
30
|
+
entries.push({
|
|
31
|
+
filePath, line,
|
|
32
|
+
method: method === 'use' ? undefined : method.toUpperCase(),
|
|
33
|
+
path: routePath,
|
|
34
|
+
framework: callee?.includes('fastify') ? 'fastify' : 'express',
|
|
35
|
+
enclosingSymbol: callee,
|
|
36
|
+
tokens: tokenize(`${method} ${routePath} ${callee ?? ''}`),
|
|
37
|
+
text: `${method.toUpperCase()} ${routePath}`,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// B. Fastify route({ method, url, handler })
|
|
43
|
+
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
|
|
44
|
+
if (node.expression.name.text === 'route' && node.arguments.length >= 1) {
|
|
45
|
+
const arg = node.arguments[0];
|
|
46
|
+
if (ts.isObjectLiteralExpression(arg)) {
|
|
47
|
+
const method = getPropertyValue(arg, 'method');
|
|
48
|
+
const url = getPropertyValue(arg, 'url');
|
|
49
|
+
if (method && url) {
|
|
50
|
+
const line = sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
|
|
51
|
+
entries.push({
|
|
52
|
+
filePath, line,
|
|
53
|
+
method: method.toUpperCase(),
|
|
54
|
+
path: url,
|
|
55
|
+
framework: 'fastify',
|
|
56
|
+
tokens: tokenize(`${method} ${url} route`),
|
|
57
|
+
text: `${method.toUpperCase()} ${url}`,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// C. Next.js App Router: exported GET, POST, etc. in route.ts
|
|
64
|
+
if (isRouteFile && isExportedNamedDeclaration(node)) {
|
|
65
|
+
const name = getExportedName(node);
|
|
66
|
+
if (name && NEXT_HANDLER_NAMES.has(name)) {
|
|
67
|
+
const line = sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
|
|
68
|
+
const routePath = deriveRouteFromFilePath(filePath);
|
|
69
|
+
entries.push({
|
|
70
|
+
filePath, line,
|
|
71
|
+
method: name,
|
|
72
|
+
path: routePath,
|
|
73
|
+
framework: 'next-app-router',
|
|
74
|
+
enclosingSymbol: name,
|
|
75
|
+
tokens: tokenize(`${name} ${routePath ?? ''} next handler`),
|
|
76
|
+
text: `${name} ${routePath ?? filePath}`,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// D. Next.js Pages API: default export in pages/api/**
|
|
81
|
+
if (isPagesApi && ts.isExportAssignment(node)) {
|
|
82
|
+
const line = sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
|
|
83
|
+
const routePath = deriveRouteFromFilePath(filePath);
|
|
84
|
+
entries.push({
|
|
85
|
+
filePath, line,
|
|
86
|
+
path: routePath,
|
|
87
|
+
framework: 'next-pages-api',
|
|
88
|
+
tokens: tokenize(`${routePath ?? ''} api handler pages`),
|
|
89
|
+
text: `handler ${routePath ?? filePath}`,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
// E. Route map: object with path/route/method/handler keys
|
|
93
|
+
if (ts.isObjectLiteralExpression(node) && node.properties.length >= 2) {
|
|
94
|
+
const hasPath = hasProperty(node, 'path') || hasProperty(node, 'route');
|
|
95
|
+
const hasHandler = hasProperty(node, 'handler') || hasProperty(node, 'component') || hasProperty(node, 'action');
|
|
96
|
+
if (hasPath && hasHandler) {
|
|
97
|
+
const routePath = getPropertyValue(node, 'path') ?? getPropertyValue(node, 'route');
|
|
98
|
+
const method = getPropertyValue(node, 'method');
|
|
99
|
+
const line = sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
|
|
100
|
+
entries.push({
|
|
101
|
+
filePath, line,
|
|
102
|
+
method: method?.toUpperCase(),
|
|
103
|
+
path: routePath,
|
|
104
|
+
framework: 'route-map',
|
|
105
|
+
tokens: tokenize(`${method ?? ''} ${routePath ?? ''} route map`),
|
|
106
|
+
text: `${method?.toUpperCase() ?? 'ROUTE'} ${routePath ?? '?'}`,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
ts.forEachChild(node, visit);
|
|
111
|
+
}
|
|
112
|
+
visit(sf);
|
|
113
|
+
return entries;
|
|
114
|
+
}
|
|
115
|
+
function getCalleeText(expr) {
|
|
116
|
+
if (ts.isIdentifier(expr))
|
|
117
|
+
return expr.text;
|
|
118
|
+
if (ts.isPropertyAccessExpression(expr))
|
|
119
|
+
return getCalleeText(expr.expression);
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
function isExportedNamedDeclaration(node) {
|
|
123
|
+
if (ts.isFunctionDeclaration(node) || ts.isVariableStatement(node)) {
|
|
124
|
+
const mods = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
|
|
125
|
+
return mods?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
126
|
+
}
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
function getExportedName(node) {
|
|
130
|
+
if (ts.isFunctionDeclaration(node) && node.name)
|
|
131
|
+
return node.name.text;
|
|
132
|
+
if (ts.isVariableStatement(node)) {
|
|
133
|
+
for (const decl of node.declarationList.declarations) {
|
|
134
|
+
if (ts.isIdentifier(decl.name))
|
|
135
|
+
return decl.name.text;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
function hasProperty(obj, name) {
|
|
141
|
+
return obj.properties.some((p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === name);
|
|
142
|
+
}
|
|
143
|
+
function getPropertyValue(obj, name) {
|
|
144
|
+
for (const p of obj.properties) {
|
|
145
|
+
if (ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === name) {
|
|
146
|
+
if (ts.isStringLiteral(p.initializer))
|
|
147
|
+
return p.initializer.text;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
function deriveRouteFromFilePath(filePath) {
|
|
153
|
+
// Next.js: derive /api/foo from pages/api/foo.ts or app/api/foo/route.ts
|
|
154
|
+
const pagesMatch = filePath.match(/pages[/\\]api[/\\](.+?)(?:\/index)?\.\w+$/);
|
|
155
|
+
if (pagesMatch)
|
|
156
|
+
return `/api/${pagesMatch[1].replace(/\\/g, '/')}`;
|
|
157
|
+
const appMatch = filePath.match(/app[/\\](.+?)[/\\]route\.\w+$/);
|
|
158
|
+
if (appMatch)
|
|
159
|
+
return `/${appMatch[1].replace(/\\/g, '/')}`;
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
function tokenize(text) {
|
|
163
|
+
return text.toLowerCase().replace(/[^a-z0-9/\s-_]/g, ' ').split(/[\s-_/]+/).filter((t) => t.length > 1);
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=extractRoutes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractRoutes.js","sourceRoot":"","sources":["../../../src/analysis/ts/extractRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AACzG,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;AAEjG;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,EAAiB;IAC7C,MAAM,OAAO,GAAsB,EAAE,CAAC;IACtC,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC;IAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjE,MAAM,WAAW,GAAG,QAAQ,KAAK,OAAO,CAAC;IACzC,MAAM,UAAU,GAAG,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;IAE1F,SAAS,KAAK,CAAC,IAAa;QAC1B,sDAAsD;QACtD,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAChF,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACvD,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;gBAC3D,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;gBACnC,IAAI,EAAE,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,+BAA+B,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACjF,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC;oBAChC,MAAM,IAAI,GAAG,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;oBAC1E,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;oBACzD,OAAO,CAAC,IAAI,CAAC;wBACX,QAAQ,EAAE,IAAI;wBACd,MAAM,EAAE,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE;wBAC3D,IAAI,EAAE,SAAS;wBACf,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;wBAC9D,eAAe,EAAE,MAAM;wBACvB,MAAM,EAAE,QAAQ,CAAC,GAAG,MAAM,IAAI,SAAS,IAAI,MAAM,IAAI,EAAE,EAAE,CAAC;wBAC1D,IAAI,EAAE,GAAG,MAAM,CAAC,WAAW,EAAE,IAAI,SAAS,EAAE;qBAC7C,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,6CAA6C;QAC7C,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAChF,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;gBACxE,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;gBAC9B,IAAI,EAAE,CAAC,yBAAyB,CAAC,GAAG,CAAC,EAAE,CAAC;oBACtC,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;oBAC/C,MAAM,GAAG,GAAG,gBAAgB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;oBACzC,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;wBAClB,MAAM,IAAI,GAAG,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;wBAC1E,OAAO,CAAC,IAAI,CAAC;4BACX,QAAQ,EAAE,IAAI;4BACd,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE;4BAC5B,IAAI,EAAE,GAAG;4BACT,SAAS,EAAE,SAAS;4BACpB,MAAM,EAAE,QAAQ,CAAC,GAAG,MAAM,IAAI,GAAG,QAAQ,CAAC;4BAC1C,IAAI,EAAE,GAAG,MAAM,CAAC,WAAW,EAAE,IAAI,GAAG,EAAE;yBACvC,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,8DAA8D;QAC9D,IAAI,WAAW,IAAI,0BAA0B,CAAC,IAAI,CAAC,EAAE,CAAC;YACpD,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,IAAI,IAAI,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzC,MAAM,IAAI,GAAG,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;gBAC1E,MAAM,SAAS,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAC;gBACpD,OAAO,CAAC,IAAI,CAAC;oBACX,QAAQ,EAAE,IAAI;oBACd,MAAM,EAAE,IAAI;oBACZ,IAAI,EAAE,SAAS;oBACf,SAAS,EAAE,iBAAiB;oBAC5B,eAAe,EAAE,IAAI;oBACrB,MAAM,EAAE,QAAQ,CAAC,GAAG,IAAI,IAAI,SAAS,IAAI,EAAE,eAAe,CAAC;oBAC3D,IAAI,EAAE,GAAG,IAAI,IAAI,SAAS,IAAI,QAAQ,EAAE;iBACzC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,uDAAuD;QACvD,IAAI,UAAU,IAAI,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,GAAG,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;YAC1E,MAAM,SAAS,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAC;YACpD,OAAO,CAAC,IAAI,CAAC;gBACX,QAAQ,EAAE,IAAI;gBACd,IAAI,EAAE,SAAS;gBACf,SAAS,EAAE,gBAAgB;gBAC3B,MAAM,EAAE,QAAQ,CAAC,GAAG,SAAS,IAAI,EAAE,oBAAoB,CAAC;gBACxD,IAAI,EAAE,WAAW,SAAS,IAAI,QAAQ,EAAE;aACzC,CAAC,CAAC;QACL,CAAC;QAED,2DAA2D;QAC3D,IAAI,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YACtE,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACxE,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YACjH,IAAI,OAAO,IAAI,UAAU,EAAE,CAAC;gBAC1B,MAAM,SAAS,GAAG,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBACpF,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;gBAChD,MAAM,IAAI,GAAG,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;gBAC1E,OAAO,CAAC,IAAI,CAAC;oBACX,QAAQ,EAAE,IAAI;oBACd,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE;oBAC7B,IAAI,EAAE,SAAS;oBACf,SAAS,EAAE,WAAW;oBACtB,MAAM,EAAE,QAAQ,CAAC,GAAG,MAAM,IAAI,EAAE,IAAI,SAAS,IAAI,EAAE,YAAY,CAAC;oBAChE,IAAI,EAAE,GAAG,MAAM,EAAE,WAAW,EAAE,IAAI,OAAO,IAAI,SAAS,IAAI,GAAG,EAAE;iBAChE,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,aAAa,CAAC,IAAmB;IACxC,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IAC5C,IAAI,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC;QAAE,OAAO,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/E,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,0BAA0B,CAAC,IAAa;IAC/C,IAAI,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,GAAG,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3E,OAAO,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC;IAC5E,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,eAAe,CAAC,IAAa;IACpC,IAAI,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IACvE,IAAI,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,CAAC;YACrD,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QACxD,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,WAAW,CAAC,GAA+B,EAAE,IAAY;IAChE,OAAO,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAC/B,EAAE,CAAC,oBAAoB,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AACnF,CAAC;AAED,SAAS,gBAAgB,CAAC,GAA+B,EAAE,IAAY;IACrE,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;QAC/B,IAAI,EAAE,CAAC,oBAAoB,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YAClF,IAAI,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,WAAW,CAAC;gBAAE,OAAO,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC;QACnE,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,uBAAuB,CAAC,QAAgB;IAC/C,yEAAyE;IACzE,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/E,IAAI,UAAU;QAAE,OAAO,QAAQ,UAAU,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;IACnE,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACjE,IAAI,QAAQ;QAAE,OAAO,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;IAC3D,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAC1G,CAAC"}
|
|
@@ -2,9 +2,13 @@ import ts from 'typescript';
|
|
|
2
2
|
/**
|
|
3
3
|
* Parse a TypeScript/JavaScript file into a ts.SourceFile using the TypeScript compiler API.
|
|
4
4
|
* Supports: .ts, .tsx, .js, .jsx, .mjs, .cjs
|
|
5
|
+
*
|
|
6
|
+
* @param text - Optional overlay text. If provided, skips disk read and parses this content
|
|
7
|
+
* instead. Used by Phase 2A snapshot-aware indexing for unsaved-buffer support.
|
|
8
|
+
*
|
|
5
9
|
* Returns null if the file doesn't exist or can't be parsed.
|
|
6
10
|
*/
|
|
7
|
-
export declare function parseSourceFile(filePath: string): ts.SourceFile | null;
|
|
11
|
+
export declare function parseSourceFile(filePath: string, text?: string): ts.SourceFile | null;
|
|
8
12
|
/**
|
|
9
13
|
* Parse source content directly (for base-version comparison).
|
|
10
14
|
*/
|
|
@@ -4,11 +4,15 @@ import { scriptKindForFile } from '../../search/fileKinds.js';
|
|
|
4
4
|
/**
|
|
5
5
|
* Parse a TypeScript/JavaScript file into a ts.SourceFile using the TypeScript compiler API.
|
|
6
6
|
* Supports: .ts, .tsx, .js, .jsx, .mjs, .cjs
|
|
7
|
+
*
|
|
8
|
+
* @param text - Optional overlay text. If provided, skips disk read and parses this content
|
|
9
|
+
* instead. Used by Phase 2A snapshot-aware indexing for unsaved-buffer support.
|
|
10
|
+
*
|
|
7
11
|
* Returns null if the file doesn't exist or can't be parsed.
|
|
8
12
|
*/
|
|
9
|
-
export function parseSourceFile(filePath) {
|
|
13
|
+
export function parseSourceFile(filePath, text) {
|
|
10
14
|
try {
|
|
11
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
15
|
+
const content = text ?? fs.readFileSync(filePath, 'utf-8');
|
|
12
16
|
return ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true, scriptKindForFile(filePath));
|
|
13
17
|
}
|
|
14
18
|
catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseSourceFile.js","sourceRoot":"","sources":["../../../src/analysis/ts/parseSourceFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D
|
|
1
|
+
{"version":3,"file":"parseSourceFile.js","sourceRoot":"","sources":["../../../src/analysis/ts/parseSourceFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,IAAa;IAC7D,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC3D,OAAO,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC3G,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe,EAAE,QAAgB;IAClE,OAAO,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC3G,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* Focused TypeScript semantic query helpers built on a ts.Program.
|
|
4
|
+
*
|
|
5
|
+
* These answer questions that AST analysis alone cannot answer reliably.
|
|
6
|
+
* All methods are read-only and do not modify the program state.
|
|
7
|
+
*
|
|
8
|
+
* Keep integration points narrow: callers should ask for specific facts,
|
|
9
|
+
* not raw ts.Type objects.
|
|
10
|
+
*/
|
|
11
|
+
export declare class CheckerQueries {
|
|
12
|
+
private readonly program;
|
|
13
|
+
private checker;
|
|
14
|
+
constructor(program: ts.Program);
|
|
15
|
+
/**
|
|
16
|
+
* Get the string-valued members of an exported enum.
|
|
17
|
+
* Returns null if the enum cannot be found in the program.
|
|
18
|
+
*/
|
|
19
|
+
getEnumMembers(filePath: string, enumName: string): string[] | null;
|
|
20
|
+
/**
|
|
21
|
+
* Given a switch statement (by containing function / file and line),
|
|
22
|
+
* return which enum members are handled and which are missing.
|
|
23
|
+
*/
|
|
24
|
+
getSwitchExhaustiveness(filePath: string, enumFilePath: string, enumName: string): {
|
|
25
|
+
handled: string[];
|
|
26
|
+
missing: string[];
|
|
27
|
+
isExhaustive: boolean;
|
|
28
|
+
} | null;
|
|
29
|
+
/**
|
|
30
|
+
* Get the type text of an exported symbol in a file.
|
|
31
|
+
* Returns a human-readable type string, or null if not found.
|
|
32
|
+
*/
|
|
33
|
+
getExportedSymbolType(filePath: string, symbolName: string): string | null;
|
|
34
|
+
/**
|
|
35
|
+
* Get parameter facts for an exported function: name, type text, optional status.
|
|
36
|
+
*/
|
|
37
|
+
getFunctionParams(filePath: string, funcName: string): Array<{
|
|
38
|
+
name: string;
|
|
39
|
+
typeText: string;
|
|
40
|
+
optional: boolean;
|
|
41
|
+
rest: boolean;
|
|
42
|
+
}> | null;
|
|
43
|
+
/**
|
|
44
|
+
* Get the return type text for an exported function.
|
|
45
|
+
*/
|
|
46
|
+
getReturnType(filePath: string, funcName: string): string | null;
|
|
47
|
+
/**
|
|
48
|
+
* Get semantic diagnostics for a file.
|
|
49
|
+
* Lighter than running the full LSP — useful for quick checks.
|
|
50
|
+
*/
|
|
51
|
+
getSemanticDiagnostics(filePath: string): readonly ts.Diagnostic[];
|
|
52
|
+
/**
|
|
53
|
+
* Get all files in the program (resolved root names).
|
|
54
|
+
*/
|
|
55
|
+
getProgramFiles(): string[];
|
|
56
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* Focused TypeScript semantic query helpers built on a ts.Program.
|
|
4
|
+
*
|
|
5
|
+
* These answer questions that AST analysis alone cannot answer reliably.
|
|
6
|
+
* All methods are read-only and do not modify the program state.
|
|
7
|
+
*
|
|
8
|
+
* Keep integration points narrow: callers should ask for specific facts,
|
|
9
|
+
* not raw ts.Type objects.
|
|
10
|
+
*/
|
|
11
|
+
export class CheckerQueries {
|
|
12
|
+
program;
|
|
13
|
+
checker;
|
|
14
|
+
constructor(program) {
|
|
15
|
+
this.program = program;
|
|
16
|
+
this.checker = program.getTypeChecker();
|
|
17
|
+
}
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Enum queries
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
/**
|
|
22
|
+
* Get the string-valued members of an exported enum.
|
|
23
|
+
* Returns null if the enum cannot be found in the program.
|
|
24
|
+
*/
|
|
25
|
+
getEnumMembers(filePath, enumName) {
|
|
26
|
+
const sf = this.program.getSourceFile(filePath);
|
|
27
|
+
if (!sf)
|
|
28
|
+
return null;
|
|
29
|
+
let enumDecl;
|
|
30
|
+
ts.forEachChild(sf, (node) => {
|
|
31
|
+
if (ts.isEnumDeclaration(node) && node.name.text === enumName) {
|
|
32
|
+
enumDecl = node;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
if (!enumDecl)
|
|
36
|
+
return null;
|
|
37
|
+
return enumDecl.members.map((m) => ts.isIdentifier(m.name) ? m.name.text : m.name.getText(sf));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Given a switch statement (by containing function / file and line),
|
|
41
|
+
* return which enum members are handled and which are missing.
|
|
42
|
+
*/
|
|
43
|
+
getSwitchExhaustiveness(filePath, enumFilePath, enumName) {
|
|
44
|
+
const allMembers = this.getEnumMembers(enumFilePath, enumName);
|
|
45
|
+
if (!allMembers)
|
|
46
|
+
return null;
|
|
47
|
+
const sf = this.program.getSourceFile(filePath);
|
|
48
|
+
if (!sf)
|
|
49
|
+
return null;
|
|
50
|
+
const handled = new Set();
|
|
51
|
+
const visit = (node) => {
|
|
52
|
+
if (ts.isSwitchStatement(node)) {
|
|
53
|
+
for (const clause of node.caseBlock.clauses) {
|
|
54
|
+
if (ts.isCaseClause(clause)) {
|
|
55
|
+
// Check if the case expression is an enum member access
|
|
56
|
+
if (ts.isPropertyAccessExpression(clause.expression)) {
|
|
57
|
+
const memberName = clause.expression.name.text;
|
|
58
|
+
if (allMembers.includes(memberName)) {
|
|
59
|
+
handled.add(memberName);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
ts.forEachChild(node, visit);
|
|
66
|
+
};
|
|
67
|
+
visit(sf);
|
|
68
|
+
const missing = allMembers.filter((m) => !handled.has(m));
|
|
69
|
+
return { handled: [...handled], missing, isExhaustive: missing.length === 0 };
|
|
70
|
+
}
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Function / type queries
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
/**
|
|
75
|
+
* Get the type text of an exported symbol in a file.
|
|
76
|
+
* Returns a human-readable type string, or null if not found.
|
|
77
|
+
*/
|
|
78
|
+
getExportedSymbolType(filePath, symbolName) {
|
|
79
|
+
const sf = this.program.getSourceFile(filePath);
|
|
80
|
+
if (!sf)
|
|
81
|
+
return null;
|
|
82
|
+
let result = null;
|
|
83
|
+
const visit = (node) => {
|
|
84
|
+
if (result)
|
|
85
|
+
return;
|
|
86
|
+
// Function declarations
|
|
87
|
+
if (ts.isFunctionDeclaration(node) && node.name?.text === symbolName) {
|
|
88
|
+
const sym = this.checker.getSymbolAtLocation(node.name);
|
|
89
|
+
if (sym)
|
|
90
|
+
result = this.checker.typeToString(this.checker.getTypeOfSymbolAtLocation(sym, node));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// Variable declarations
|
|
94
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === symbolName) {
|
|
95
|
+
const sym = this.checker.getSymbolAtLocation(node.name);
|
|
96
|
+
if (sym)
|
|
97
|
+
result = this.checker.typeToString(this.checker.getTypeOfSymbolAtLocation(sym, node));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
ts.forEachChild(node, visit);
|
|
101
|
+
};
|
|
102
|
+
visit(sf);
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Get parameter facts for an exported function: name, type text, optional status.
|
|
107
|
+
*/
|
|
108
|
+
getFunctionParams(filePath, funcName) {
|
|
109
|
+
const sf = this.program.getSourceFile(filePath);
|
|
110
|
+
if (!sf)
|
|
111
|
+
return null;
|
|
112
|
+
let funcNode;
|
|
113
|
+
const visit = (node) => {
|
|
114
|
+
if (funcNode)
|
|
115
|
+
return;
|
|
116
|
+
if (ts.isFunctionDeclaration(node) && node.name?.text === funcName) {
|
|
117
|
+
funcNode = node;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === funcName) {
|
|
121
|
+
if (node.initializer && (ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer))) {
|
|
122
|
+
funcNode = node.initializer;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
ts.forEachChild(node, visit);
|
|
127
|
+
};
|
|
128
|
+
visit(sf);
|
|
129
|
+
if (!funcNode)
|
|
130
|
+
return null;
|
|
131
|
+
return funcNode.parameters.map((p) => ({
|
|
132
|
+
name: ts.isIdentifier(p.name) ? p.name.text : p.name.getText(sf),
|
|
133
|
+
typeText: p.type ? p.type.getText(sf) : this.checker.typeToString(this.checker.getTypeAtLocation(p)),
|
|
134
|
+
optional: !!p.questionToken || !!p.initializer,
|
|
135
|
+
rest: !!p.dotDotDotToken,
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get the return type text for an exported function.
|
|
140
|
+
*/
|
|
141
|
+
getReturnType(filePath, funcName) {
|
|
142
|
+
const sf = this.program.getSourceFile(filePath);
|
|
143
|
+
if (!sf)
|
|
144
|
+
return null;
|
|
145
|
+
let result = null;
|
|
146
|
+
const visit = (node) => {
|
|
147
|
+
if (result)
|
|
148
|
+
return;
|
|
149
|
+
if (ts.isFunctionDeclaration(node) && node.name?.text === funcName) {
|
|
150
|
+
const sig = this.checker.getSignatureFromDeclaration(node);
|
|
151
|
+
if (sig)
|
|
152
|
+
result = this.checker.typeToString(sig.getReturnType());
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
ts.forEachChild(node, visit);
|
|
156
|
+
};
|
|
157
|
+
visit(sf);
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
// Diagnostics
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
/**
|
|
164
|
+
* Get semantic diagnostics for a file.
|
|
165
|
+
* Lighter than running the full LSP — useful for quick checks.
|
|
166
|
+
*/
|
|
167
|
+
getSemanticDiagnostics(filePath) {
|
|
168
|
+
const sf = this.program.getSourceFile(filePath);
|
|
169
|
+
if (!sf)
|
|
170
|
+
return [];
|
|
171
|
+
try {
|
|
172
|
+
return this.program.getSemanticDiagnostics(sf);
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Get all files in the program (resolved root names).
|
|
180
|
+
*/
|
|
181
|
+
getProgramFiles() {
|
|
182
|
+
return this.program.getSourceFiles()
|
|
183
|
+
.map((sf) => sf.fileName)
|
|
184
|
+
.filter((f) => !f.includes('node_modules') && !f.endsWith('.d.ts'));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=CheckerQueries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CheckerQueries.js","sourceRoot":"","sources":["../../../../src/analysis/ts/program/CheckerQueries.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import type { SnapshotResolver } from '../../../session/SnapshotResolver.js';
|
|
3
|
+
/**
|
|
4
|
+
* Manages a cached TypeScript Program for semantic queries.
|
|
5
|
+
*
|
|
6
|
+
* This is NOT a replacement for the LSP server — it's a focused semantic
|
|
7
|
+
* accelerator for questions that AST analysis alone cannot answer reliably:
|
|
8
|
+
* enum member enumeration, type compatibility, exhaustiveness, param signatures.
|
|
9
|
+
*
|
|
10
|
+
* Integrates with the Phase 2A SnapshotResolver so the program operates on
|
|
11
|
+
* live (unsaved) buffer content when overlays are present.
|
|
12
|
+
*/
|
|
13
|
+
export declare class ProgramManager {
|
|
14
|
+
private cache;
|
|
15
|
+
/**
|
|
16
|
+
* Get or build a TypeScript Program for a workspace root.
|
|
17
|
+
*
|
|
18
|
+
* @param workspaceRoot - Absolute path to the workspace root.
|
|
19
|
+
* @param resolver - Optional Phase 2A snapshot resolver for overlay awareness.
|
|
20
|
+
* Different resolver states produce separate cached programs — overlay and
|
|
21
|
+
* disk programs never share a cache entry.
|
|
22
|
+
*/
|
|
23
|
+
getOrBuild(workspaceRoot: string, resolver?: SnapshotResolver): ts.Program;
|
|
24
|
+
/** Invalidate all cached programs for a workspace root. */
|
|
25
|
+
invalidate(workspaceRoot: string): void;
|
|
26
|
+
}
|
|
27
|
+
export declare const programManager: ProgramManager;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import * as crypto from 'crypto';
|
|
5
|
+
/**
|
|
6
|
+
* Manages a cached TypeScript Program for semantic queries.
|
|
7
|
+
*
|
|
8
|
+
* This is NOT a replacement for the LSP server — it's a focused semantic
|
|
9
|
+
* accelerator for questions that AST analysis alone cannot answer reliably:
|
|
10
|
+
* enum member enumeration, type compatibility, exhaustiveness, param signatures.
|
|
11
|
+
*
|
|
12
|
+
* Integrates with the Phase 2A SnapshotResolver so the program operates on
|
|
13
|
+
* live (unsaved) buffer content when overlays are present.
|
|
14
|
+
*/
|
|
15
|
+
export class ProgramManager {
|
|
16
|
+
// Keyed by full cache key (workspaceRoot + tsconfig hash + dirty files signature)
|
|
17
|
+
// so different overlay states produce independent program instances.
|
|
18
|
+
cache = new Map();
|
|
19
|
+
/**
|
|
20
|
+
* Get or build a TypeScript Program for a workspace root.
|
|
21
|
+
*
|
|
22
|
+
* @param workspaceRoot - Absolute path to the workspace root.
|
|
23
|
+
* @param resolver - Optional Phase 2A snapshot resolver for overlay awareness.
|
|
24
|
+
* Different resolver states produce separate cached programs — overlay and
|
|
25
|
+
* disk programs never share a cache entry.
|
|
26
|
+
*/
|
|
27
|
+
getOrBuild(workspaceRoot, resolver) {
|
|
28
|
+
const cacheKey = buildCacheKey(workspaceRoot, resolver);
|
|
29
|
+
const cached = this.cache.get(cacheKey);
|
|
30
|
+
if (cached)
|
|
31
|
+
return cached;
|
|
32
|
+
const program = buildProgram(workspaceRoot, resolver);
|
|
33
|
+
this.cache.set(cacheKey, program);
|
|
34
|
+
return program;
|
|
35
|
+
}
|
|
36
|
+
/** Invalidate all cached programs for a workspace root. */
|
|
37
|
+
invalidate(workspaceRoot) {
|
|
38
|
+
for (const key of this.cache.keys()) {
|
|
39
|
+
if (key.startsWith(workspaceRoot + ':'))
|
|
40
|
+
this.cache.delete(key);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Module-level singleton — one manager per process.
|
|
45
|
+
export const programManager = new ProgramManager();
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Internal helpers
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
function buildCacheKey(workspaceRoot, resolver) {
|
|
50
|
+
const tsConfigPath = findTsConfig(workspaceRoot);
|
|
51
|
+
const tsConfigHash = tsConfigPath ? hashFile(tsConfigPath) : 'no-tsconfig';
|
|
52
|
+
const dirtyKey = resolver ? resolver.getDirtyFiles().sort().join('|') : '';
|
|
53
|
+
return `${workspaceRoot}:${tsConfigHash}:${dirtyKey}`;
|
|
54
|
+
}
|
|
55
|
+
function buildProgram(workspaceRoot, resolver) {
|
|
56
|
+
const tsConfigPath = findTsConfig(workspaceRoot);
|
|
57
|
+
let compilerOptions = {
|
|
58
|
+
target: ts.ScriptTarget.ES2020,
|
|
59
|
+
module: ts.ModuleKind.CommonJS,
|
|
60
|
+
strict: false,
|
|
61
|
+
noEmit: true,
|
|
62
|
+
allowJs: true,
|
|
63
|
+
skipLibCheck: true,
|
|
64
|
+
skipDefaultLibCheck: true,
|
|
65
|
+
};
|
|
66
|
+
let rootNames = [];
|
|
67
|
+
if (tsConfigPath) {
|
|
68
|
+
try {
|
|
69
|
+
const { config, error } = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
|
|
70
|
+
if (!error) {
|
|
71
|
+
const parsed = ts.parseJsonConfigFileContent(config, ts.sys, path.dirname(tsConfigPath), {}, tsConfigPath);
|
|
72
|
+
compilerOptions = { ...parsed.options, noEmit: true, skipLibCheck: true, skipDefaultLibCheck: true };
|
|
73
|
+
rootNames = parsed.fileNames;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Fall back to defaults
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// If no files from tsconfig, collect manually
|
|
81
|
+
if (rootNames.length === 0) {
|
|
82
|
+
rootNames = collectTsFiles(workspaceRoot);
|
|
83
|
+
}
|
|
84
|
+
const host = createSnapshotHost(compilerOptions, resolver);
|
|
85
|
+
return ts.createProgram(rootNames, compilerOptions, host);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Create a CompilerHost that respects the SnapshotResolver for overlay content.
|
|
89
|
+
*/
|
|
90
|
+
function createSnapshotHost(options, resolver) {
|
|
91
|
+
const defaultHost = ts.createCompilerHost(options);
|
|
92
|
+
return {
|
|
93
|
+
...defaultHost,
|
|
94
|
+
getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile) {
|
|
95
|
+
// Use overlay text if available (Phase 2A integration)
|
|
96
|
+
const overlayText = resolver?.getText(path.normalize(fileName));
|
|
97
|
+
if (overlayText !== undefined) {
|
|
98
|
+
return ts.createSourceFile(fileName, overlayText, languageVersion, true);
|
|
99
|
+
}
|
|
100
|
+
return defaultHost.getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
|
|
101
|
+
},
|
|
102
|
+
fileExists(fileName) {
|
|
103
|
+
// Overlay files exist even if not on disk
|
|
104
|
+
if (resolver?.hasOverlay(path.normalize(fileName)))
|
|
105
|
+
return true;
|
|
106
|
+
return defaultHost.fileExists(fileName);
|
|
107
|
+
},
|
|
108
|
+
readFile(fileName) {
|
|
109
|
+
const overlayText = resolver?.getText(path.normalize(fileName));
|
|
110
|
+
if (overlayText !== undefined)
|
|
111
|
+
return overlayText;
|
|
112
|
+
return defaultHost.readFile(fileName);
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function findTsConfig(workspaceRoot) {
|
|
117
|
+
return ts.findConfigFile(workspaceRoot, ts.sys.fileExists, 'tsconfig.json') ?? undefined;
|
|
118
|
+
}
|
|
119
|
+
function collectTsFiles(dir, depth = 0) {
|
|
120
|
+
if (depth > 6)
|
|
121
|
+
return [];
|
|
122
|
+
const files = [];
|
|
123
|
+
try {
|
|
124
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
125
|
+
if (entry.isDirectory()) {
|
|
126
|
+
const skip = ['node_modules', 'dist', 'build', '.git', 'coverage'];
|
|
127
|
+
if (entry.name.startsWith('.') || skip.includes(entry.name))
|
|
128
|
+
continue;
|
|
129
|
+
files.push(...collectTsFiles(path.join(dir, entry.name), depth + 1));
|
|
130
|
+
}
|
|
131
|
+
else if (/\.(ts|tsx)$/.test(entry.name) && !entry.name.endsWith('.d.ts')) {
|
|
132
|
+
files.push(path.join(dir, entry.name));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch { }
|
|
137
|
+
return files;
|
|
138
|
+
}
|
|
139
|
+
function hashFile(filePath) {
|
|
140
|
+
try {
|
|
141
|
+
return crypto.createHash('sha1').update(fs.readFileSync(filePath)).digest('hex').slice(0, 8);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return 'unreadable';
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=ProgramManager.js.map
|