lsp-intelligence 0.1.2 → 0.2.1
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 +48 -15
- package/dist/analysis/pattern/collectSearchFiles.d.ts +6 -0
- package/dist/analysis/pattern/collectSearchFiles.js +38 -0
- package/dist/analysis/pattern/collectSearchFiles.js.map +1 -0
- package/dist/analysis/pattern/runPatternSearch.d.ts +17 -0
- package/dist/analysis/pattern/runPatternSearch.js +65 -0
- package/dist/analysis/pattern/runPatternSearch.js.map +1 -0
- package/dist/analysis/ts/extractDeclarations.d.ts +7 -0
- package/dist/analysis/ts/extractDeclarations.js +93 -0
- package/dist/analysis/ts/extractDeclarations.js.map +1 -0
- package/dist/analysis/ts/extractUsages.d.ts +7 -0
- package/dist/analysis/ts/extractUsages.js +139 -0
- package/dist/analysis/ts/extractUsages.js.map +1 -0
- package/dist/analysis/ts/parseSourceFile.d.ts +11 -0
- package/dist/analysis/ts/parseSourceFile.js +24 -0
- package/dist/analysis/ts/parseSourceFile.js.map +1 -0
- package/dist/analysis/ts/reactState.d.ts +9 -0
- package/dist/analysis/ts/reactState.js +57 -0
- package/dist/analysis/ts/reactState.js.map +1 -0
- package/dist/analysis/ts/snippets.d.ts +14 -0
- package/dist/analysis/ts/snippets.js +29 -0
- package/dist/analysis/ts/snippets.js.map +1 -0
- package/dist/analysis/ts/structuralPredicates.d.ts +10 -0
- package/dist/analysis/ts/structuralPredicates.js +199 -0
- package/dist/analysis/ts/structuralPredicates.js.map +1 -0
- package/dist/ast/diffDeclarationShapes.d.ts +16 -0
- package/dist/ast/diffDeclarationShapes.js +179 -0
- package/dist/ast/diffDeclarationShapes.js.map +1 -0
- package/dist/ast/extractExportDeclarations.d.ts +21 -0
- package/dist/ast/extractExportDeclarations.js +218 -0
- package/dist/ast/extractExportDeclarations.js.map +1 -0
- package/dist/ast/findNodeAtPosition.d.ts +12 -0
- package/dist/ast/findNodeAtPosition.js +75 -0
- package/dist/ast/findNodeAtPosition.js.map +1 -0
- package/dist/ast/parseFile.d.ts +11 -0
- package/dist/ast/parseFile.js +46 -0
- package/dist/ast/parseFile.js.map +1 -0
- package/dist/engine/LspEngine.js +23 -1
- package/dist/engine/LspEngine.js.map +1 -1
- package/dist/engine/waitForDiagnostics.d.ts +7 -0
- package/dist/engine/waitForDiagnostics.js +16 -0
- package/dist/engine/waitForDiagnostics.js.map +1 -0
- package/dist/git/getBaseFileContent.d.ts +6 -0
- package/dist/git/getBaseFileContent.js +23 -0
- package/dist/git/getBaseFileContent.js.map +1 -0
- package/dist/git/getChangedFiles.d.ts +5 -0
- package/dist/git/getChangedFiles.js +27 -0
- package/dist/git/getChangedFiles.js.map +1 -0
- package/dist/git/getChangedHunks.d.ts +14 -0
- package/dist/git/getChangedHunks.js +56 -0
- package/dist/git/getChangedHunks.js.map +1 -0
- package/dist/git/getGitRoot.d.ts +15 -0
- package/dist/git/getGitRoot.js +33 -0
- package/dist/git/getGitRoot.js.map +1 -0
- package/dist/git/getMergeBase.d.ts +5 -0
- package/dist/git/getMergeBase.js +23 -0
- package/dist/git/getMergeBase.js.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/resolve/searchScope.d.ts +17 -0
- package/dist/resolve/searchScope.js +64 -0
- package/dist/resolve/searchScope.js.map +1 -0
- package/dist/resolve/targetResolver.d.ts +33 -0
- package/dist/resolve/targetResolver.js +84 -0
- package/dist/resolve/targetResolver.js.map +1 -0
- package/dist/search/adapters/config.d.ts +5 -0
- package/dist/search/adapters/config.js +56 -0
- package/dist/search/adapters/config.js.map +1 -0
- package/dist/search/adapters/http.d.ts +6 -0
- package/dist/search/adapters/http.js +58 -0
- package/dist/search/adapters/http.js.map +1 -0
- package/dist/search/adapters/react.d.ts +12 -0
- package/dist/search/adapters/react.js +92 -0
- package/dist/search/adapters/react.js.map +1 -0
- package/dist/search/adapters/registry.d.ts +6 -0
- package/dist/search/adapters/registry.js +20 -0
- package/dist/search/adapters/registry.js.map +1 -0
- package/dist/search/adapters/types.d.ts +5 -0
- package/dist/search/adapters/types.js +2 -0
- package/dist/search/adapters/types.js.map +1 -0
- package/dist/search/debug/trace.d.ts +6 -0
- package/dist/search/debug/trace.js +25 -0
- package/dist/search/debug/trace.js.map +1 -0
- package/dist/search/expand/graphExpansion.d.ts +16 -0
- package/dist/search/expand/graphExpansion.js +168 -0
- package/dist/search/expand/graphExpansion.js.map +1 -0
- package/dist/search/families/behaviorFamilies.d.ts +22 -0
- package/dist/search/families/behaviorFamilies.js +90 -0
- package/dist/search/families/behaviorFamilies.js.map +1 -0
- package/dist/search/fileKinds.d.ts +13 -0
- package/dist/search/fileKinds.js +74 -0
- package/dist/search/fileKinds.js.map +1 -0
- package/dist/search/index/configIndex.d.ts +6 -0
- package/dist/search/index/configIndex.js +196 -0
- package/dist/search/index/configIndex.js.map +1 -0
- package/dist/search/index/declarationIndex.d.ts +6 -0
- package/dist/search/index/declarationIndex.js +13 -0
- package/dist/search/index/declarationIndex.js.map +1 -0
- package/dist/search/index/docIndex.d.ts +7 -0
- package/dist/search/index/docIndex.js +116 -0
- package/dist/search/index/docIndex.js.map +1 -0
- package/dist/search/index/types.d.ts +1 -0
- package/dist/search/index/types.js +2 -0
- package/dist/search/index/types.js.map +1 -0
- package/dist/search/index/usageIndex.d.ts +6 -0
- package/dist/search/index/usageIndex.js +13 -0
- package/dist/search/index/usageIndex.js.map +1 -0
- package/dist/search/index/workspaceIndex.d.ts +14 -0
- package/dist/search/index/workspaceIndex.js +127 -0
- package/dist/search/index/workspaceIndex.js.map +1 -0
- package/dist/search/query/compileEffectiveSearchSpec.d.ts +24 -0
- package/dist/search/query/compileEffectiveSearchSpec.js +67 -0
- package/dist/search/query/compileEffectiveSearchSpec.js.map +1 -0
- package/dist/search/query/parseQuery.d.ts +10 -0
- package/dist/search/query/parseQuery.js +284 -0
- package/dist/search/query/parseQuery.js.map +1 -0
- package/dist/search/query/phraseRules.d.ts +10 -0
- package/dist/search/query/phraseRules.js +58 -0
- package/dist/search/query/phraseRules.js.map +1 -0
- package/dist/search/query/planQuery.d.ts +10 -0
- package/dist/search/query/planQuery.js +91 -0
- package/dist/search/query/planQuery.js.map +1 -0
- package/dist/search/ranking/assessConfidence.d.ts +6 -0
- package/dist/search/ranking/assessConfidence.js +31 -0
- package/dist/search/ranking/assessConfidence.js.map +1 -0
- package/dist/search/ranking/candidateIdentity.d.ts +21 -0
- package/dist/search/ranking/candidateIdentity.js +29 -0
- package/dist/search/ranking/candidateIdentity.js.map +1 -0
- package/dist/search/ranking/coalesceCandidates.d.ts +9 -0
- package/dist/search/ranking/coalesceCandidates.js +48 -0
- package/dist/search/ranking/coalesceCandidates.js.map +1 -0
- package/dist/search/ranking/fieldedTextRanker.d.ts +26 -0
- package/dist/search/ranking/fieldedTextRanker.js +79 -0
- package/dist/search/ranking/fieldedTextRanker.js.map +1 -0
- package/dist/search/ranking/mergeCandidates.d.ts +13 -0
- package/dist/search/ranking/mergeCandidates.js +85 -0
- package/dist/search/ranking/mergeCandidates.js.map +1 -0
- package/dist/search/ranking/rankCandidates.d.ts +12 -0
- package/dist/search/ranking/rankCandidates.js +106 -0
- package/dist/search/ranking/rankCandidates.js.map +1 -0
- package/dist/search/retrievers/behaviorRetriever.d.ts +7 -0
- package/dist/search/retrievers/behaviorRetriever.js +86 -0
- package/dist/search/retrievers/behaviorRetriever.js.map +1 -0
- package/dist/search/retrievers/configRetriever.d.ts +7 -0
- package/dist/search/retrievers/configRetriever.js +46 -0
- package/dist/search/retrievers/configRetriever.js.map +1 -0
- package/dist/search/retrievers/docRetriever.d.ts +7 -0
- package/dist/search/retrievers/docRetriever.js +46 -0
- package/dist/search/retrievers/docRetriever.js.map +1 -0
- package/dist/search/retrievers/identifierRetriever.d.ts +7 -0
- package/dist/search/retrievers/identifierRetriever.js +67 -0
- package/dist/search/retrievers/identifierRetriever.js.map +1 -0
- package/dist/search/retrievers/structuralRetriever.d.ts +9 -0
- package/dist/search/retrievers/structuralRetriever.js +104 -0
- package/dist/search/retrievers/structuralRetriever.js.map +1 -0
- package/dist/search/retrievers/textPatternRetriever.d.ts +7 -0
- package/dist/search/retrievers/textPatternRetriever.js +90 -0
- package/dist/search/retrievers/textPatternRetriever.js.map +1 -0
- package/dist/search/structural/locators/callLocator.d.ts +6 -0
- package/dist/search/structural/locators/callLocator.js +57 -0
- package/dist/search/structural/locators/callLocator.js.map +1 -0
- package/dist/search/structural/locators/declarationLocator.d.ts +6 -0
- package/dist/search/structural/locators/declarationLocator.js +57 -0
- package/dist/search/structural/locators/declarationLocator.js.map +1 -0
- package/dist/search/structural/locators/statementLocator.d.ts +6 -0
- package/dist/search/structural/locators/statementLocator.js +65 -0
- package/dist/search/structural/locators/statementLocator.js.map +1 -0
- package/dist/search/structural/locators/types.d.ts +12 -0
- package/dist/search/structural/locators/types.js +2 -0
- package/dist/search/structural/locators/types.js.map +1 -0
- package/dist/search/structural/selectLocators.d.ts +7 -0
- package/dist/search/structural/selectLocators.js +21 -0
- package/dist/search/structural/selectLocators.js.map +1 -0
- package/dist/search/types.d.ts +191 -0
- package/dist/search/types.js +11 -0
- package/dist/search/types.js.map +1 -0
- package/dist/tools/composites/apiGuard.d.ts +1 -0
- package/dist/tools/composites/apiGuard.js +172 -0
- package/dist/tools/composites/apiGuard.js.map +1 -0
- package/dist/tools/composites/explainError.js +3 -2
- package/dist/tools/composites/explainError.js.map +1 -1
- package/dist/tools/composites/findCode.d.ts +1 -0
- package/dist/tools/composites/findCode.js +145 -0
- package/dist/tools/composites/findCode.js.map +1 -0
- package/dist/tools/composites/findPattern.d.ts +5 -0
- package/dist/tools/composites/findPattern.js +41 -0
- package/dist/tools/composites/findPattern.js.map +1 -0
- package/dist/tools/composites/rootCauseTrace.d.ts +1 -0
- package/dist/tools/composites/rootCauseTrace.js +213 -0
- package/dist/tools/composites/rootCauseTrace.js.map +1 -0
- package/dist/tools/composites/semanticDiff.js +24 -77
- package/dist/tools/composites/semanticDiff.js.map +1 -1
- package/dist/tools/live/clearIndex.d.ts +1 -0
- package/dist/tools/live/clearIndex.js +19 -0
- package/dist/tools/live/clearIndex.js.map +1 -0
- package/dist/tools/live/liveDiagnostics.js +3 -2
- package/dist/tools/live/liveDiagnostics.js.map +1 -1
- package/dist/tools/primitives/callHierarchy.js +1 -1
- package/dist/tools/primitives/callHierarchy.js.map +1 -1
- package/dist/tools/primitives/diagnostics.js +2 -2
- package/dist/tools/primitives/diagnostics.js.map +1 -1
- package/dist/tools/registry.d.ts +10 -2
- package/dist/tools/registry.js +7 -2
- package/dist/tools/registry.js.map +1 -1
- package/package.json +11 -5
- package/dist/tools/primitives/typeHierarchy.d.ts +0 -1
- package/dist/tools/primitives/typeHierarchy.js +0 -55
- package/dist/tools/primitives/typeHierarchy.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# lsp-intelligence
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Local code intelligence for real engineering workflows.
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**Find** implementations, API usage, structural patterns, configs, and routes. **Explain** why something broke. **Guard** the API contract before merging.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
29 MCP tools across 5 layers. Supports **TypeScript and JavaScript** (TS, TSX, JS, JSX, MJS, CJS). Local-only — no paid API, no external calls.
|
|
8
8
|
|
|
9
9
|
## Why this exists
|
|
10
10
|
|
|
@@ -78,7 +78,7 @@ Use npm source in `marketplace.json` for version pinning and built-in dependency
|
|
|
78
78
|
"source": {
|
|
79
79
|
"source": "npm",
|
|
80
80
|
"package": "lsp-intelligence",
|
|
81
|
-
"version": "0.
|
|
81
|
+
"version": "0.2.0"
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
```
|
|
@@ -142,15 +142,19 @@ Direct LSP wrappers. Every tool accepts **symbol names** — agents never need t
|
|
|
142
142
|
| `file_imports` | List all imports of a file. |
|
|
143
143
|
| `file_exports` | List a file's public API including re-exports. |
|
|
144
144
|
|
|
145
|
-
### Layer 2:
|
|
145
|
+
### Layer 2: Intelligence Tools (10 tools)
|
|
146
146
|
|
|
147
|
-
Combine
|
|
147
|
+
Combine LSP, AST, and Git substrates into high-level operations.
|
|
148
148
|
|
|
149
149
|
| Tool | Description |
|
|
150
150
|
|------|-------------|
|
|
151
|
+
| `api_guard` | Detect public API contract changes — export diffs, structural classification, consumer impact, semver summary. |
|
|
152
|
+
| `root_cause_trace` | Trace the root cause of a TypeScript error — find the originating declaration change, not just the symptom. |
|
|
153
|
+
| `find_code` | Unified code search: behavior discovery, identifier/API usage, structural queries, config/route lookup, and implementation-root discovery. Routes automatically. |
|
|
154
|
+
| `find_pattern` | AST structural search — find code by pattern (e.g. `useEffect($$$)`, `try { $$$ } catch ($E) { $$$ }`). |
|
|
151
155
|
| `inspect_symbol` | Hover + definition + references in one call. Full context about any symbol. |
|
|
152
156
|
| `batch_query` | Look up multiple symbols at once. Saves round-trips when exploring. |
|
|
153
|
-
| `impact_trace` | Follow a symbol through type aliases and re-exports to find ALL transitive usages.
|
|
157
|
+
| `impact_trace` | Follow a symbol through type aliases and re-exports to find ALL transitive usages. |
|
|
154
158
|
| `semantic_diff` | Analyze git diff semantically: identify changed symbols and their blast radius. |
|
|
155
159
|
| `find_test_files` | Find all test/spec/stories files that reference a symbol. |
|
|
156
160
|
| `explain_error` | Turn a TypeScript error into actionable context: expected type, actual type, and fix suggestion. |
|
|
@@ -174,6 +178,18 @@ Post-edit verification.
|
|
|
174
178
|
| `find_unused_exports` | Find exported symbols with zero cross-package importers. |
|
|
175
179
|
| `auto_import` | Resolve the correct import path for a symbol name. |
|
|
176
180
|
|
|
181
|
+
## find_code query classes
|
|
182
|
+
|
|
183
|
+
`find_code` supports five query classes, routed automatically:
|
|
184
|
+
|
|
185
|
+
| Class | Example query | What happens |
|
|
186
|
+
|-------|--------------|-------------|
|
|
187
|
+
| **Identifier / API usage** | `useEffect`, `Promise.all` | Usage index → exact call/import sites with enclosing context |
|
|
188
|
+
| **Structural** | `useEffect that returns cleanup conditionally` | Identifier + structural predicates → AST evaluation on located nodes |
|
|
189
|
+
| **Behavior / entrypoint** | `where do we validate permissions` | Fielded BM25 over declarations + JSDoc/comments + family hints |
|
|
190
|
+
| **Config / route / flag** | `where is the feature flag configured` | Config index → JSON/YAML/package.json + env usage in code |
|
|
191
|
+
| **Implementation root** | `where is this actually implemented` | Graph expansion → wrapper detection → root promotion |
|
|
192
|
+
|
|
177
193
|
## Architecture
|
|
178
194
|
|
|
179
195
|
```
|
|
@@ -184,7 +200,8 @@ Post-edit verification.
|
|
|
184
200
|
│ Layer 3: Context Engine [read-only] │
|
|
185
201
|
│ gather_context, outline │
|
|
186
202
|
├─────────────────────────────────────────────────────────┤
|
|
187
|
-
│ Layer 2:
|
|
203
|
+
│ Layer 2: Intelligence Tools [read-only] │
|
|
204
|
+
│ find_code, find_pattern, root_cause_trace, api_guard, │
|
|
188
205
|
│ impact_trace, semantic_diff, inspect_symbol, │
|
|
189
206
|
│ batch_query, find_test_files, explain_error │
|
|
190
207
|
├─────────────────────────────────────────────────────────┤
|
|
@@ -192,9 +209,13 @@ Post-edit verification.
|
|
|
192
209
|
│ find_references, hover, definition, call_hierarchy, │
|
|
193
210
|
│ rename, diagnostics, symbols, imports, exports │
|
|
194
211
|
├─────────────────────────────────────────────────────────┤
|
|
195
|
-
│ Layer 0:
|
|
196
|
-
│ TypeScript Server
|
|
197
|
-
│
|
|
212
|
+
│ Layer 0: Analysis Substrates [infrastructure] │
|
|
213
|
+
│ LSP Engine (TypeScript Server, symbol resolver) │
|
|
214
|
+
│ TypeScript AST (declarations, usages, predicates) │
|
|
215
|
+
│ Local text/regex search (recipe-compiled patterns) │
|
|
216
|
+
│ Config/doc indexes (JSON, YAML, env, JSDoc, comments) │
|
|
217
|
+
│ Graph expansion (wrapper detection, root promotion) │
|
|
218
|
+
│ Git integration (semantic diff, base comparison) │
|
|
198
219
|
└─────────────────────────────────────────────────────────┘
|
|
199
220
|
```
|
|
200
221
|
|
|
@@ -220,7 +241,7 @@ Requires **Node.js 20+**.
|
|
|
220
241
|
|
|
221
242
|
### Option 1: Claude Code plugin (recommended)
|
|
222
243
|
|
|
223
|
-
Installs the MCP server, hooks, and skills (`/impact`, `/context`, `/
|
|
244
|
+
Installs the MCP server, hooks, and skills (`/find`, `/why`, `/api-check`, `/verify`, `/check`, `/impact`, `/context`, `/diff`) as a single package.
|
|
224
245
|
|
|
225
246
|
```bash
|
|
226
247
|
claude plugin add perilevy/lsp-intelligence
|
|
@@ -280,18 +301,30 @@ Then in `.mcp.json`:
|
|
|
280
301
|
git clone https://github.com/perilevy/lsp-intelligence.git
|
|
281
302
|
cd lsp-intelligence
|
|
282
303
|
yarn install
|
|
283
|
-
yarn build #
|
|
304
|
+
yarn build # clean build (rm -rf dist && tsc)
|
|
284
305
|
yarn test # vitest
|
|
285
306
|
yarn typecheck # TypeScript strict mode — no emit
|
|
307
|
+
yarn bench # search quality benchmarks
|
|
286
308
|
```
|
|
287
309
|
|
|
288
310
|
### Testing
|
|
289
311
|
|
|
290
|
-
Tests verify cross-package reference resolution, symbol-name lookup, type alias tracing, impact trace traversal, context building, and output formatting — all against
|
|
312
|
+
Tests verify cross-package reference resolution, symbol-name lookup, type alias tracing, impact trace traversal, search quality, context building, and output formatting — all against self-contained fixture repos at `test-fixtures/`. No external dependencies needed.
|
|
313
|
+
|
|
314
|
+
### Benchmarks
|
|
315
|
+
|
|
316
|
+
`benchmarks/` contains reproducible quality cases for `find_code`, `root_cause_trace`, and `api_guard`. Every serious real-world failure should become a benchmark case.
|
|
317
|
+
|
|
318
|
+
## What this is not
|
|
319
|
+
|
|
320
|
+
- **Not a universal semantic search engine.** Strong on code structure, API usage, configs, and known patterns. Does not understand arbitrary business logic.
|
|
321
|
+
- **Not a replacement for full-text search.** Use grep for literal string matching. `find_code` uses text patterns internally but optimizes for code-aware ranking.
|
|
322
|
+
- **Not an AI model.** All intelligence is local: AST analysis, LSP queries, fielded text ranking, adapter recipes. No paid API calls, no external services.
|
|
323
|
+
- **Does not index secret-bearing `.env` files.** Env variable usage in code (`process.env.X`, `import.meta.env.X`) is indexed and searchable. Non-secret template/example files (`.env.example`, `.env.template`) are indexed. Real `.env` files are excluded by default.
|
|
291
324
|
|
|
292
325
|
## Dependencies
|
|
293
326
|
|
|
294
|
-
All dependencies are installed automatically. Under the hood: [`typescript-language-server`](https://github.com/typescript-language-server/typescript-language-server) for LSP, [`@modelcontextprotocol/sdk`](https://github.com/modelcontextprotocol/typescript-sdk) for MCP,
|
|
327
|
+
All dependencies are installed automatically. Under the hood: [`typescript-language-server`](https://github.com/typescript-language-server/typescript-language-server) for LSP, [`@modelcontextprotocol/sdk`](https://github.com/modelcontextprotocol/typescript-sdk) for MCP, [`@ast-grep/napi`](https://github.com/ast-grep/ast-grep) for structural patterns. Uses your project's own TypeScript version.
|
|
295
328
|
|
|
296
329
|
## License
|
|
297
330
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SearchScope } from '../../search/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Collect files matching a set of extensions within a search scope.
|
|
4
|
+
* Skips dot-prefixed dirs, build output, minified/bundled files, and oversized files.
|
|
5
|
+
*/
|
|
6
|
+
export declare function collectSearchFiles(scope: SearchScope, extensions: string[], maxFiles: number): string[];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { isTestFile, shouldSkipDir, shouldSkipFile } from '../../search/fileKinds.js';
|
|
4
|
+
/**
|
|
5
|
+
* Collect files matching a set of extensions within a search scope.
|
|
6
|
+
* Skips dot-prefixed dirs, build output, minified/bundled files, and oversized files.
|
|
7
|
+
*/
|
|
8
|
+
export function collectSearchFiles(scope, extensions, maxFiles) {
|
|
9
|
+
const files = [];
|
|
10
|
+
for (const root of scope.roots) {
|
|
11
|
+
walkDir(root, files, extensions, scope.includeTests, maxFiles, 0);
|
|
12
|
+
if (files.length >= maxFiles)
|
|
13
|
+
break;
|
|
14
|
+
}
|
|
15
|
+
return files;
|
|
16
|
+
}
|
|
17
|
+
function walkDir(dir, files, extensions, includeTests, maxFiles, depth) {
|
|
18
|
+
if (depth > 8 || files.length >= maxFiles)
|
|
19
|
+
return;
|
|
20
|
+
try {
|
|
21
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
22
|
+
if (shouldSkipDir(entry))
|
|
23
|
+
continue;
|
|
24
|
+
const full = path.join(dir, entry);
|
|
25
|
+
const stat = fs.statSync(full);
|
|
26
|
+
if (stat.isDirectory()) {
|
|
27
|
+
walkDir(full, files, extensions, includeTests, maxFiles, depth + 1);
|
|
28
|
+
}
|
|
29
|
+
else if (extensions.some((e) => entry.endsWith(e)) && !shouldSkipFile(full, stat.size)) {
|
|
30
|
+
if (!includeTests && isTestFile(full))
|
|
31
|
+
continue;
|
|
32
|
+
files.push(full);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch { }
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=collectSearchFiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collectSearchFiles.js","sourceRoot":"","sources":["../../../src/analysis/pattern/collectSearchFiles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEtF;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAAkB,EAClB,UAAoB,EACpB,QAAgB;IAEhB,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,YAAY,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;QAClE,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ;YAAE,MAAM;IACtC,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,OAAO,CACd,GAAW,EACX,KAAe,EACf,UAAoB,EACpB,YAAqB,EACrB,QAAgB,EAChB,KAAa;IAEb,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO;IAClD,IAAI,CAAC;QACH,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;YACxC,IAAI,aAAa,CAAC,KAAK,CAAC;gBAAE,SAAS;YACnC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACnC,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC/B,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;gBACvB,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;YACtE,CAAC;iBAAM,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzF,IAAI,CAAC,YAAY,IAAI,UAAU,CAAC,IAAI,CAAC;oBAAE,SAAS;gBAChD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SearchScope, PatternMatch } from '../../search/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Run an ast-grep pattern search across files in scope.
|
|
4
|
+
* This is the engine behind find_pattern. Structural retrievers may also use it.
|
|
5
|
+
*/
|
|
6
|
+
export declare function runPatternSearch(input: {
|
|
7
|
+
pattern: string;
|
|
8
|
+
language: 'typescript' | 'tsx' | 'javascript' | 'jsx';
|
|
9
|
+
scope: SearchScope;
|
|
10
|
+
maxResults: number;
|
|
11
|
+
contextLines: number;
|
|
12
|
+
workspaceRoot: string;
|
|
13
|
+
}): {
|
|
14
|
+
filesScanned: number;
|
|
15
|
+
matches: PatternMatch[];
|
|
16
|
+
warnings: string[];
|
|
17
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import { parse, Lang } from '@ast-grep/napi';
|
|
3
|
+
import { collectSearchFiles } from './collectSearchFiles.js';
|
|
4
|
+
import { relativePath } from '../../engine/positions.js';
|
|
5
|
+
const LANG_MAP = {
|
|
6
|
+
typescript: Lang.TypeScript,
|
|
7
|
+
tsx: Lang.Tsx,
|
|
8
|
+
javascript: Lang.JavaScript,
|
|
9
|
+
jsx: Lang.Tsx, // ast-grep uses Tsx parser for JSX
|
|
10
|
+
};
|
|
11
|
+
const EXT_MAP = {
|
|
12
|
+
typescript: ['.ts', '.mjs', '.cjs'],
|
|
13
|
+
tsx: ['.tsx', '.ts'],
|
|
14
|
+
javascript: ['.js', '.mjs', '.cjs'],
|
|
15
|
+
jsx: ['.jsx', '.js'],
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Run an ast-grep pattern search across files in scope.
|
|
19
|
+
* This is the engine behind find_pattern. Structural retrievers may also use it.
|
|
20
|
+
*/
|
|
21
|
+
export function runPatternSearch(input) {
|
|
22
|
+
const lang = LANG_MAP[input.language];
|
|
23
|
+
if (!lang)
|
|
24
|
+
return { filesScanned: 0, matches: [], warnings: [`Unsupported language: ${input.language}`] };
|
|
25
|
+
const extensions = EXT_MAP[input.language];
|
|
26
|
+
const files = collectSearchFiles(input.scope, extensions, 500);
|
|
27
|
+
const matches = [];
|
|
28
|
+
const warnings = [];
|
|
29
|
+
for (const file of files) {
|
|
30
|
+
if (matches.length >= input.maxResults)
|
|
31
|
+
break;
|
|
32
|
+
try {
|
|
33
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
34
|
+
const root = parse(lang, content).root();
|
|
35
|
+
const found = root.findAll(input.pattern);
|
|
36
|
+
for (const match of found) {
|
|
37
|
+
if (matches.length >= input.maxResults)
|
|
38
|
+
break;
|
|
39
|
+
const range = match.range();
|
|
40
|
+
const line = range.start.line + 1;
|
|
41
|
+
matches.push({
|
|
42
|
+
filePath: relativePath(file, input.workspaceRoot),
|
|
43
|
+
line,
|
|
44
|
+
column: range.start.column,
|
|
45
|
+
text: match.text().substring(0, 200),
|
|
46
|
+
context: getContextLines(content, range.start.line, input.contextLines),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
warnings.push(`Parse failed for ${relativePath(file, input.workspaceRoot)}: ${err instanceof Error ? err.message : String(err)}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return { filesScanned: files.length, matches, warnings };
|
|
55
|
+
}
|
|
56
|
+
function getContextLines(content, line0, ctx) {
|
|
57
|
+
const lines = content.split('\n');
|
|
58
|
+
const start = Math.max(0, line0 - ctx);
|
|
59
|
+
const end = Math.min(lines.length - 1, line0 + ctx);
|
|
60
|
+
return lines
|
|
61
|
+
.slice(start, end + 1)
|
|
62
|
+
.map((l, i) => `${start + i + 1}| ${l}`)
|
|
63
|
+
.join('\n');
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=runPatternSearch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runPatternSearch.js","sourceRoot":"","sources":["../../../src/analysis/pattern/runPatternSearch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import type { DeclarationIndexEntry } from '../../search/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Extract all declarations from a TypeScript source file using the TS compiler API.
|
|
5
|
+
* Returns top-level and exported declarations with symbol tokens for searching.
|
|
6
|
+
*/
|
|
7
|
+
export declare function extractDeclarations(sf: ts.SourceFile): DeclarationIndexEntry[];
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* Tokenize a symbol name: split camelCase, PascalCase, snake_case.
|
|
5
|
+
*/
|
|
6
|
+
function tokenize(name) {
|
|
7
|
+
return name
|
|
8
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
9
|
+
.split(/[^a-zA-Z0-9]+/)
|
|
10
|
+
.map((t) => t.toLowerCase())
|
|
11
|
+
.filter((t) => t.length > 1);
|
|
12
|
+
}
|
|
13
|
+
function pathTokenize(filePath) {
|
|
14
|
+
const rel = path.basename(filePath, path.extname(filePath));
|
|
15
|
+
return tokenize(rel);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Extract all declarations from a TypeScript source file using the TS compiler API.
|
|
19
|
+
* Returns top-level and exported declarations with symbol tokens for searching.
|
|
20
|
+
*/
|
|
21
|
+
export function extractDeclarations(sf) {
|
|
22
|
+
const entries = [];
|
|
23
|
+
const filePath = sf.fileName;
|
|
24
|
+
const pathToks = pathTokenize(filePath);
|
|
25
|
+
function visit(node) {
|
|
26
|
+
// Function declarations
|
|
27
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
28
|
+
entries.push(makeEntry(node.name.text, 'function', node, sf, filePath, pathToks));
|
|
29
|
+
}
|
|
30
|
+
// Variable statements: export const/let/var
|
|
31
|
+
else if (ts.isVariableStatement(node)) {
|
|
32
|
+
const isExported = hasExportModifier(node);
|
|
33
|
+
for (const decl of node.declarationList.declarations) {
|
|
34
|
+
if (ts.isIdentifier(decl.name)) {
|
|
35
|
+
const kind = decl.initializer && (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer))
|
|
36
|
+
? 'function' : 'variable';
|
|
37
|
+
entries.push({
|
|
38
|
+
symbol: decl.name.text,
|
|
39
|
+
kind,
|
|
40
|
+
filePath,
|
|
41
|
+
line: sf.getLineAndCharacterOfPosition(decl.getStart(sf)).line + 1,
|
|
42
|
+
column: sf.getLineAndCharacterOfPosition(decl.getStart(sf)).character,
|
|
43
|
+
isExported,
|
|
44
|
+
pathTokens: pathToks,
|
|
45
|
+
symbolTokens: tokenize(decl.name.text),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Class declarations
|
|
51
|
+
else if (ts.isClassDeclaration(node) && node.name) {
|
|
52
|
+
entries.push(makeEntry(node.name.text, 'class', node, sf, filePath, pathToks));
|
|
53
|
+
}
|
|
54
|
+
// Interface declarations
|
|
55
|
+
else if (ts.isInterfaceDeclaration(node)) {
|
|
56
|
+
entries.push(makeEntry(node.name.text, 'interface', node, sf, filePath, pathToks));
|
|
57
|
+
}
|
|
58
|
+
// Type alias declarations
|
|
59
|
+
else if (ts.isTypeAliasDeclaration(node)) {
|
|
60
|
+
entries.push(makeEntry(node.name.text, 'type', node, sf, filePath, pathToks));
|
|
61
|
+
}
|
|
62
|
+
// Enum declarations
|
|
63
|
+
else if (ts.isEnumDeclaration(node)) {
|
|
64
|
+
entries.push(makeEntry(node.name.text, 'enum', node, sf, filePath, pathToks));
|
|
65
|
+
}
|
|
66
|
+
// Only visit top-level children
|
|
67
|
+
if (node === sf) {
|
|
68
|
+
ts.forEachChild(node, visit);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
visit(sf);
|
|
72
|
+
return entries;
|
|
73
|
+
}
|
|
74
|
+
function makeEntry(name, kind, node, sf, filePath, pathToks) {
|
|
75
|
+
const pos = sf.getLineAndCharacterOfPosition(node.getStart(sf));
|
|
76
|
+
return {
|
|
77
|
+
symbol: name,
|
|
78
|
+
kind,
|
|
79
|
+
filePath,
|
|
80
|
+
line: pos.line + 1,
|
|
81
|
+
column: pos.character,
|
|
82
|
+
isExported: hasExportModifier(node),
|
|
83
|
+
pathTokens: pathToks,
|
|
84
|
+
symbolTokens: tokenize(name),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function hasExportModifier(node) {
|
|
88
|
+
if (!ts.canHaveModifiers(node))
|
|
89
|
+
return false;
|
|
90
|
+
const mods = ts.getModifiers(node);
|
|
91
|
+
return mods?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=extractDeclarations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractDeclarations.js","sourceRoot":"","sources":["../../../src/analysis/ts/extractDeclarations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B;;GAEG;AACH,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,IAAI;SACR,OAAO,CAAC,iBAAiB,EAAE,OAAO,CAAC;SACnC,KAAK,CAAC,eAAe,CAAC;SACtB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;SAC3B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,YAAY,CAAC,QAAgB;IACpC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC5D,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAAiB;IACnD,MAAM,OAAO,GAA4B,EAAE,CAAC;IAC5C,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC;IAC7B,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IAExC,SAAS,KAAK,CAAC,IAAa;QAC1B,wBAAwB;QACxB,IAAI,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAChD,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;QACpF,CAAC;QACD,4CAA4C;aACvC,IAAI,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,MAAM,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAC3C,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,CAAC;gBACrD,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;wBAClH,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC;oBAC5B,OAAO,CAAC,IAAI,CAAC;wBACX,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI;wBACtB,IAAI;wBACJ,QAAQ;wBACR,IAAI,EAAE,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;wBAClE,MAAM,EAAE,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS;wBACrE,UAAU;wBACV,UAAU,EAAE,QAAQ;wBACpB,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;qBACvC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QACD,qBAAqB;aAChB,IAAI,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAClD,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;QACjF,CAAC;QACD,yBAAyB;aACpB,IAAI,EAAE,CAAC,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC;YACzC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;QACrF,CAAC;QACD,0BAA0B;aACrB,IAAI,EAAE,CAAC,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC;YACzC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;QAChF,CAAC;QACD,oBAAoB;aACf,IAAI,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;QAChF,CAAC;QAED,gCAAgC;QAChC,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YAChB,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,SAAS,CAChB,IAAY,EACZ,IAAY,EACZ,IAAa,EACb,EAAiB,EACjB,QAAgB,EAChB,QAAkB;IAElB,MAAM,GAAG,GAAG,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IAChE,OAAO;QACL,MAAM,EAAE,IAAI;QACZ,IAAI;QACJ,QAAQ;QACR,IAAI,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC;QAClB,MAAM,EAAE,GAAG,CAAC,SAAS;QACrB,UAAU,EAAE,iBAAiB,CAAC,IAAI,CAAC;QACnC,UAAU,EAAE,QAAQ;QACpB,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC;KAC7B,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAa;IACtC,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACnC,OAAO,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC;AAC5E,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import type { UsageIndexEntry } from '../../search/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Extract all identifier usage sites from a TypeScript source file.
|
|
5
|
+
* Indexes: call expressions, member calls, import specifiers, JSX tags.
|
|
6
|
+
*/
|
|
7
|
+
export declare function extractUsages(sf: ts.SourceFile): UsageIndexEntry[];
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
function pathTokenize(filePath) {
|
|
4
|
+
return path.basename(filePath, path.extname(filePath))
|
|
5
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
6
|
+
.split(/[^a-zA-Z0-9]+/)
|
|
7
|
+
.map((t) => t.toLowerCase())
|
|
8
|
+
.filter((t) => t.length > 1);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Extract all identifier usage sites from a TypeScript source file.
|
|
12
|
+
* Indexes: call expressions, member calls, import specifiers, JSX tags.
|
|
13
|
+
*/
|
|
14
|
+
export function extractUsages(sf) {
|
|
15
|
+
const entries = [];
|
|
16
|
+
const filePath = sf.fileName;
|
|
17
|
+
const pathToks = pathTokenize(filePath);
|
|
18
|
+
// Track enclosing symbol for context
|
|
19
|
+
let enclosingSymbol;
|
|
20
|
+
let enclosingKind;
|
|
21
|
+
function visit(node) {
|
|
22
|
+
// Save enclosing context so it restores correctly on recursion unwind
|
|
23
|
+
const prevSymbol = enclosingSymbol;
|
|
24
|
+
const prevKind = enclosingKind;
|
|
25
|
+
// Track enclosing function/class/method for context
|
|
26
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
27
|
+
enclosingSymbol = node.name.text;
|
|
28
|
+
enclosingKind = 'function';
|
|
29
|
+
}
|
|
30
|
+
else if (ts.isVariableStatement(node)) {
|
|
31
|
+
for (const decl of node.declarationList.declarations) {
|
|
32
|
+
if (ts.isIdentifier(decl.name) && decl.initializer &&
|
|
33
|
+
(ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer))) {
|
|
34
|
+
enclosingSymbol = decl.name.text;
|
|
35
|
+
enclosingKind = 'function';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else if (ts.isClassDeclaration(node) && node.name) {
|
|
40
|
+
enclosingSymbol = node.name.text;
|
|
41
|
+
enclosingKind = 'class';
|
|
42
|
+
}
|
|
43
|
+
else if (ts.isMethodDeclaration(node) && ts.isIdentifier(node.name)) {
|
|
44
|
+
enclosingSymbol = node.name.text;
|
|
45
|
+
enclosingKind = 'method';
|
|
46
|
+
}
|
|
47
|
+
// Call expressions: foo(), bar.baz()
|
|
48
|
+
if (ts.isCallExpression(node)) {
|
|
49
|
+
const expr = node.expression;
|
|
50
|
+
// Simple call: useEffect(...)
|
|
51
|
+
if (ts.isIdentifier(expr)) {
|
|
52
|
+
entries.push(makeUsage(expr.text, expr.text, 'call', node, sf, filePath, pathToks, enclosingSymbol, enclosingKind));
|
|
53
|
+
}
|
|
54
|
+
// Member call: Promise.all(...), sdk.Items.get(...)
|
|
55
|
+
else if (ts.isPropertyAccessExpression(expr)) {
|
|
56
|
+
const fullName = getPropertyAccessText(expr);
|
|
57
|
+
const leafName = expr.name.text;
|
|
58
|
+
entries.push(makeUsage(fullName, leafName, 'member-call', node, sf, filePath, pathToks, enclosingSymbol, enclosingKind));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Import declarations: import { X } from "module"
|
|
62
|
+
if (ts.isImportDeclaration(node) && node.importClause) {
|
|
63
|
+
const clause = node.importClause;
|
|
64
|
+
// Default import
|
|
65
|
+
if (clause.name) {
|
|
66
|
+
entries.push(makeUsage(clause.name.text, clause.name.text, 'import', clause.name, sf, filePath, pathToks, undefined, undefined));
|
|
67
|
+
}
|
|
68
|
+
// Named imports: { A, B }
|
|
69
|
+
if (clause.namedBindings && ts.isNamedImports(clause.namedBindings)) {
|
|
70
|
+
for (const spec of clause.namedBindings.elements) {
|
|
71
|
+
entries.push(makeUsage(spec.name.text, spec.name.text, 'import', spec, sf, filePath, pathToks, undefined, undefined));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Namespace import: * as X
|
|
75
|
+
if (clause.namedBindings && ts.isNamespaceImport(clause.namedBindings)) {
|
|
76
|
+
entries.push(makeUsage(clause.namedBindings.name.text, clause.namedBindings.name.text, 'import', clause.namedBindings, sf, filePath, pathToks, undefined, undefined));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// JSX elements: <Component ... />
|
|
80
|
+
if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
|
|
81
|
+
const tagName = node.tagName;
|
|
82
|
+
if (ts.isIdentifier(tagName) && /^[A-Z]/.test(tagName.text)) {
|
|
83
|
+
entries.push(makeUsage(tagName.text, tagName.text, 'jsx-tag', node, sf, filePath, pathToks, enclosingSymbol, enclosingKind));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Env usage: process.env.X, import.meta.env.X
|
|
87
|
+
// Only match the exact env access (3 parts for process.env.X, 4 for import.meta.env.X)
|
|
88
|
+
// Skip if parent is also a PropertyAccessExpression (e.g. process.env.X.includes → skip, let process.env.X match)
|
|
89
|
+
if (ts.isPropertyAccessExpression(node) && !ts.isPropertyAccessExpression(node.parent)) {
|
|
90
|
+
const text = getPropertyAccessText(node);
|
|
91
|
+
const parts = text.split('.');
|
|
92
|
+
if (parts[0] === 'process' && parts[1] === 'env' && parts.length === 3) {
|
|
93
|
+
const envKey = parts[2];
|
|
94
|
+
const entry = makeUsage(`process.env.${envKey}`, envKey.toLowerCase(), 'env-usage', node, sf, filePath, pathToks, enclosingSymbol, enclosingKind);
|
|
95
|
+
entry.metadata = { envKey };
|
|
96
|
+
entries.push(entry);
|
|
97
|
+
}
|
|
98
|
+
else if (parts[0] === 'import' && parts[1] === 'meta' && parts[2] === 'env' && parts.length === 4) {
|
|
99
|
+
const envKey = parts[3];
|
|
100
|
+
const entry = makeUsage(`import.meta.env.${envKey}`, envKey.toLowerCase(), 'env-usage', node, sf, filePath, pathToks, enclosingSymbol, enclosingKind);
|
|
101
|
+
entry.metadata = { envKey };
|
|
102
|
+
entries.push(entry);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
ts.forEachChild(node, visit);
|
|
106
|
+
// Restore enclosing context on unwind
|
|
107
|
+
enclosingSymbol = prevSymbol;
|
|
108
|
+
enclosingKind = prevKind;
|
|
109
|
+
}
|
|
110
|
+
visit(sf);
|
|
111
|
+
return entries;
|
|
112
|
+
}
|
|
113
|
+
function makeUsage(identifier, normalizedIdentifier, kind, node, sf, filePath, pathToks, enclosingSymbol, enclosingKind) {
|
|
114
|
+
const pos = sf.getLineAndCharacterOfPosition(node.getStart(sf));
|
|
115
|
+
return {
|
|
116
|
+
identifier,
|
|
117
|
+
normalizedIdentifier,
|
|
118
|
+
kind,
|
|
119
|
+
filePath,
|
|
120
|
+
line: pos.line + 1,
|
|
121
|
+
column: pos.character,
|
|
122
|
+
enclosingSymbol,
|
|
123
|
+
enclosingKind,
|
|
124
|
+
pathTokens: pathToks,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function getPropertyAccessText(node) {
|
|
128
|
+
const parts = [node.name.text];
|
|
129
|
+
let current = node.expression;
|
|
130
|
+
while (ts.isPropertyAccessExpression(current)) {
|
|
131
|
+
parts.unshift(current.name.text);
|
|
132
|
+
current = current.expression;
|
|
133
|
+
}
|
|
134
|
+
if (ts.isIdentifier(current)) {
|
|
135
|
+
parts.unshift(current.text);
|
|
136
|
+
}
|
|
137
|
+
return parts.join('.');
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=extractUsages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractUsages.js","sourceRoot":"","sources":["../../../src/analysis/ts/extractUsages.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* Parse a TypeScript/JavaScript file into a ts.SourceFile using the TypeScript compiler API.
|
|
4
|
+
* Supports: .ts, .tsx, .js, .jsx, .mjs, .cjs
|
|
5
|
+
* Returns null if the file doesn't exist or can't be parsed.
|
|
6
|
+
*/
|
|
7
|
+
export declare function parseSourceFile(filePath: string): ts.SourceFile | null;
|
|
8
|
+
/**
|
|
9
|
+
* Parse source content directly (for base-version comparison).
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseSourceContent(content: string, fileName: string): ts.SourceFile;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import { scriptKindForFile } from '../../search/fileKinds.js';
|
|
4
|
+
/**
|
|
5
|
+
* Parse a TypeScript/JavaScript file into a ts.SourceFile using the TypeScript compiler API.
|
|
6
|
+
* Supports: .ts, .tsx, .js, .jsx, .mjs, .cjs
|
|
7
|
+
* Returns null if the file doesn't exist or can't be parsed.
|
|
8
|
+
*/
|
|
9
|
+
export function parseSourceFile(filePath) {
|
|
10
|
+
try {
|
|
11
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
12
|
+
return ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true, scriptKindForFile(filePath));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Parse source content directly (for base-version comparison).
|
|
20
|
+
*/
|
|
21
|
+
export function parseSourceContent(content, fileName) {
|
|
22
|
+
return ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true, scriptKindForFile(fileName));
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=parseSourceFile.js.map
|
|
@@ -0,0 +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;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACnD,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,9 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* Detect if a node contains a functional state updater pattern.
|
|
4
|
+
* Matches: setState(prev => ...), setCount(current => current + 1), etc.
|
|
5
|
+
*
|
|
6
|
+
* Only matches when the updater parameter is actually used in the body.
|
|
7
|
+
* Does NOT match: setState(() => 1) — that's a constant updater.
|
|
8
|
+
*/
|
|
9
|
+
export declare function hasFunctionalStateUpdater(node: ts.Node): boolean;
|