@saluzi/codegraph 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/codegraph.d.ts +25 -0
- package/dist/bin/node-version-check.d.ts +37 -0
- package/dist/bin/uninstall.d.ts +14 -0
- package/dist/context/formatter.d.ts +33 -0
- package/dist/context/formatter.js +244 -0
- package/dist/context/index.d.ts +117 -0
- package/dist/context/index.js +1050 -0
- package/dist/db/index.d.ts +101 -0
- package/dist/db/index.js +250 -0
- package/dist/db/migrations.d.ts +47 -0
- package/dist/db/migrations.js +131 -0
- package/dist/db/queries.d.ts +291 -0
- package/dist/db/queries.js +1349 -0
- package/dist/db/schema.sql +151 -0
- package/dist/db/sqlite-adapter.d.ts +49 -0
- package/dist/db/sqlite-adapter.js +141 -0
- package/dist/directory.d.ts +62 -0
- package/dist/directory.js +264 -0
- package/dist/errors.d.ts +149 -0
- package/dist/errors.js +219 -0
- package/dist/extraction/dfm-extractor.d.ts +31 -0
- package/dist/extraction/dfm-extractor.js +151 -0
- package/dist/extraction/grammars.d.ts +94 -0
- package/dist/extraction/grammars.js +357 -0
- package/dist/extraction/index.d.ts +148 -0
- package/dist/extraction/index.js +1286 -0
- package/dist/extraction/languages/c-cpp.d.ts +4 -0
- package/dist/extraction/languages/c-cpp.js +126 -0
- package/dist/extraction/languages/csharp.d.ts +3 -0
- package/dist/extraction/languages/csharp.js +72 -0
- package/dist/extraction/languages/dart.d.ts +3 -0
- package/dist/extraction/languages/dart.js +192 -0
- package/dist/extraction/languages/go.d.ts +3 -0
- package/dist/extraction/languages/go.js +58 -0
- package/dist/extraction/languages/index.d.ts +10 -0
- package/dist/extraction/languages/index.js +49 -0
- package/dist/extraction/languages/java.d.ts +3 -0
- package/dist/extraction/languages/java.js +64 -0
- package/dist/extraction/languages/javascript.d.ts +3 -0
- package/dist/extraction/languages/javascript.js +90 -0
- package/dist/extraction/languages/kotlin.d.ts +3 -0
- package/dist/extraction/languages/kotlin.js +253 -0
- package/dist/extraction/languages/lua.d.ts +3 -0
- package/dist/extraction/languages/lua.js +150 -0
- package/dist/extraction/languages/luau.d.ts +3 -0
- package/dist/extraction/languages/luau.js +37 -0
- package/dist/extraction/languages/pascal.d.ts +3 -0
- package/dist/extraction/languages/pascal.js +66 -0
- package/dist/extraction/languages/php.d.ts +3 -0
- package/dist/extraction/languages/php.js +107 -0
- package/dist/extraction/languages/python.d.ts +3 -0
- package/dist/extraction/languages/python.js +56 -0
- package/dist/extraction/languages/ruby.d.ts +3 -0
- package/dist/extraction/languages/ruby.js +114 -0
- package/dist/extraction/languages/rust.d.ts +3 -0
- package/dist/extraction/languages/rust.js +109 -0
- package/dist/extraction/languages/scala.d.ts +3 -0
- package/dist/extraction/languages/scala.js +139 -0
- package/dist/extraction/languages/swift.d.ts +3 -0
- package/dist/extraction/languages/swift.js +91 -0
- package/dist/extraction/languages/typescript.d.ts +3 -0
- package/dist/extraction/languages/typescript.js +129 -0
- package/dist/extraction/liquid-extractor.d.ts +52 -0
- package/dist/extraction/liquid-extractor.js +313 -0
- package/dist/extraction/parse-worker.d.ts +8 -0
- package/dist/extraction/parse-worker.js +94 -0
- package/dist/extraction/svelte-extractor.d.ts +56 -0
- package/dist/extraction/svelte-extractor.js +272 -0
- package/dist/extraction/tree-sitter-helpers.d.ts +39 -0
- package/dist/extraction/tree-sitter-helpers.js +103 -0
- package/dist/extraction/tree-sitter-types.d.ts +191 -0
- package/dist/extraction/tree-sitter-types.js +10 -0
- package/dist/extraction/tree-sitter.d.ts +238 -0
- package/dist/extraction/tree-sitter.js +2430 -0
- package/dist/extraction/vue-extractor.d.ts +36 -0
- package/dist/extraction/vue-extractor.js +163 -0
- package/dist/extraction/wasm/tree-sitter-c.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-cpp.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-dart.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-go.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-java.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-javascript.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-kotlin.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-lua.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-luau.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-php.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-python.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-ruby.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-rust.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-swift.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-tsx.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-typescript.wasm +0 -0
- package/dist/extraction/wasm-runtime-flags.d.ts +46 -0
- package/dist/extraction/wasm-runtime-flags.js +105 -0
- package/dist/graph/index.d.ts +8 -0
- package/dist/graph/index.js +13 -0
- package/dist/graph/queries.d.ts +109 -0
- package/dist/graph/queries.js +366 -0
- package/dist/graph/traversal.d.ts +137 -0
- package/dist/graph/traversal.js +528 -0
- package/dist/index.d.ts +509 -0
- package/dist/index.js +800 -0
- package/dist/installer/claude-md-template.d.ts +19 -0
- package/dist/installer/config-writer.d.ts +29 -0
- package/dist/installer/index.d.ts +140 -0
- package/dist/installer/instructions-template.d.ts +30 -0
- package/dist/installer/targets/claude.d.ts +55 -0
- package/dist/installer/targets/codex.d.ts +18 -0
- package/dist/installer/targets/cursor.d.ts +35 -0
- package/dist/installer/targets/hermes.d.ts +18 -0
- package/dist/installer/targets/opencode.d.ts +30 -0
- package/dist/installer/targets/registry.d.ts +38 -0
- package/dist/installer/targets/shared.d.ts +92 -0
- package/dist/installer/targets/toml.d.ts +64 -0
- package/dist/installer/targets/types.d.ts +122 -0
- package/dist/mcp/index.d.ts +98 -0
- package/dist/mcp/server-instructions.d.ts +20 -0
- package/dist/mcp/tools.d.ts +269 -0
- package/dist/mcp/transport.d.ts +117 -0
- package/dist/resolution/frameworks/cargo-workspace.d.ts +20 -0
- package/dist/resolution/frameworks/cargo-workspace.js +225 -0
- package/dist/resolution/frameworks/csharp.d.ts +8 -0
- package/dist/resolution/frameworks/csharp.js +213 -0
- package/dist/resolution/frameworks/drupal.d.ts +51 -0
- package/dist/resolution/frameworks/drupal.js +335 -0
- package/dist/resolution/frameworks/express.d.ts +8 -0
- package/dist/resolution/frameworks/express.js +225 -0
- package/dist/resolution/frameworks/go.d.ts +8 -0
- package/dist/resolution/frameworks/go.js +158 -0
- package/dist/resolution/frameworks/index.d.ts +52 -0
- package/dist/resolution/frameworks/index.js +137 -0
- package/dist/resolution/frameworks/java.d.ts +8 -0
- package/dist/resolution/frameworks/java.js +177 -0
- package/dist/resolution/frameworks/laravel.d.ts +13 -0
- package/dist/resolution/frameworks/laravel.js +248 -0
- package/dist/resolution/frameworks/nestjs.d.ts +26 -0
- package/dist/resolution/frameworks/nestjs.js +374 -0
- package/dist/resolution/frameworks/python.d.ts +10 -0
- package/dist/resolution/frameworks/python.js +278 -0
- package/dist/resolution/frameworks/react.d.ts +8 -0
- package/dist/resolution/frameworks/react.js +272 -0
- package/dist/resolution/frameworks/ruby.d.ts +8 -0
- package/dist/resolution/frameworks/ruby.js +198 -0
- package/dist/resolution/frameworks/rust.d.ts +8 -0
- package/dist/resolution/frameworks/rust.js +207 -0
- package/dist/resolution/frameworks/svelte.d.ts +9 -0
- package/dist/resolution/frameworks/svelte.js +249 -0
- package/dist/resolution/frameworks/swift.d.ts +10 -0
- package/dist/resolution/frameworks/swift.js +376 -0
- package/dist/resolution/frameworks/vue.d.ts +9 -0
- package/dist/resolution/frameworks/vue.js +306 -0
- package/dist/resolution/import-resolver.d.ts +61 -0
- package/dist/resolution/import-resolver.js +663 -0
- package/dist/resolution/index.d.ts +118 -0
- package/dist/resolution/index.js +744 -0
- package/dist/resolution/lru-cache.d.ts +24 -0
- package/dist/resolution/lru-cache.js +62 -0
- package/dist/resolution/name-matcher.d.ts +50 -0
- package/dist/resolution/name-matcher.js +384 -0
- package/dist/resolution/path-aliases.d.ts +72 -0
- package/dist/resolution/path-aliases.js +238 -0
- package/dist/resolution/strip-comments.d.ts +40 -0
- package/dist/resolution/strip-comments.js +441 -0
- package/dist/resolution/types.d.ts +181 -0
- package/dist/resolution/types.js +8 -0
- package/dist/search/query-parser.d.ts +61 -0
- package/dist/search/query-parser.js +177 -0
- package/dist/search/query-utils.d.ts +59 -0
- package/dist/search/query-utils.js +383 -0
- package/dist/sync/git-hooks.d.ts +54 -0
- package/dist/sync/git-hooks.js +223 -0
- package/dist/sync/index.d.ts +25 -0
- package/dist/sync/index.js +28 -0
- package/dist/sync/watch-policy.d.ts +51 -0
- package/dist/sync/watch-policy.js +124 -0
- package/dist/sync/watcher.d.ts +83 -0
- package/dist/sync/watcher.js +192 -0
- package/dist/types.d.ts +433 -0
- package/dist/types.js +75 -0
- package/dist/ui/glyphs.d.ts +42 -0
- package/dist/ui/shimmer-progress.d.ts +11 -0
- package/dist/ui/shimmer-worker.d.ts +2 -0
- package/dist/ui/types.d.ts +20 -0
- package/dist/utils.d.ts +231 -0
- package/dist/utils.js +549 -0
- package/package.json +24 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Laravel Framework Resolver
|
|
4
|
+
*
|
|
5
|
+
* Handles Laravel-specific patterns for reference resolution.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.laravelResolver = exports.FACADE_MAPPINGS = void 0;
|
|
9
|
+
const strip_comments_1 = require("../strip-comments");
|
|
10
|
+
/**
|
|
11
|
+
* Laravel facade mappings to underlying classes
|
|
12
|
+
* Exported for potential use in facade resolution
|
|
13
|
+
*/
|
|
14
|
+
exports.FACADE_MAPPINGS = {
|
|
15
|
+
Auth: 'Illuminate\\Auth\\AuthManager',
|
|
16
|
+
Cache: 'Illuminate\\Cache\\CacheManager',
|
|
17
|
+
Config: 'Illuminate\\Config\\Repository',
|
|
18
|
+
DB: 'Illuminate\\Database\\DatabaseManager',
|
|
19
|
+
Event: 'Illuminate\\Events\\Dispatcher',
|
|
20
|
+
File: 'Illuminate\\Filesystem\\Filesystem',
|
|
21
|
+
Gate: 'Illuminate\\Auth\\Access\\Gate',
|
|
22
|
+
Hash: 'Illuminate\\Hashing\\HashManager',
|
|
23
|
+
Log: 'Illuminate\\Log\\LogManager',
|
|
24
|
+
Mail: 'Illuminate\\Mail\\Mailer',
|
|
25
|
+
Queue: 'Illuminate\\Queue\\QueueManager',
|
|
26
|
+
Redis: 'Illuminate\\Redis\\RedisManager',
|
|
27
|
+
Request: 'Illuminate\\Http\\Request',
|
|
28
|
+
Response: 'Illuminate\\Http\\Response',
|
|
29
|
+
Route: 'Illuminate\\Routing\\Router',
|
|
30
|
+
Session: 'Illuminate\\Session\\SessionManager',
|
|
31
|
+
Storage: 'Illuminate\\Filesystem\\FilesystemManager',
|
|
32
|
+
URL: 'Illuminate\\Routing\\UrlGenerator',
|
|
33
|
+
Validator: 'Illuminate\\Validation\\Factory',
|
|
34
|
+
View: 'Illuminate\\View\\Factory',
|
|
35
|
+
};
|
|
36
|
+
exports.laravelResolver = {
|
|
37
|
+
name: 'laravel',
|
|
38
|
+
languages: ['php'],
|
|
39
|
+
detect(context) {
|
|
40
|
+
// Check for artisan file (Laravel signature)
|
|
41
|
+
return context.fileExists('artisan') || context.fileExists('app/Http/Kernel.php');
|
|
42
|
+
},
|
|
43
|
+
resolve(ref, context) {
|
|
44
|
+
// Pattern 1: Model::method() - Eloquent static calls
|
|
45
|
+
const modelMatch = ref.referenceName.match(/^([A-Z][a-zA-Z]+)::(\w+)$/);
|
|
46
|
+
if (modelMatch) {
|
|
47
|
+
const [, className, methodName] = modelMatch;
|
|
48
|
+
const result = resolveModelCall(className, methodName, context);
|
|
49
|
+
if (result) {
|
|
50
|
+
return {
|
|
51
|
+
original: ref,
|
|
52
|
+
targetNodeId: result,
|
|
53
|
+
confidence: 0.85,
|
|
54
|
+
resolvedBy: 'framework',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Pattern 2: Facade calls - Auth::user(), Cache::get()
|
|
59
|
+
const facadeMatch = ref.referenceName.match(/^(Auth|Cache|DB|Log|Mail|Queue|Session|Storage|Validator|Route|Request|Response)::(\w+)$/);
|
|
60
|
+
if (facadeMatch) {
|
|
61
|
+
// Facades typically resolve to external Laravel code
|
|
62
|
+
// Mark as external but note the facade
|
|
63
|
+
return null; // External, can't resolve to local node
|
|
64
|
+
}
|
|
65
|
+
// Pattern 3: Helper function calls - route(), view(), config()
|
|
66
|
+
if (['route', 'view', 'config', 'env', 'app', 'abort', 'redirect', 'response', 'request', 'session', 'url', 'asset', 'mix'].includes(ref.referenceName)) {
|
|
67
|
+
// These are Laravel helpers - external
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
// Pattern 4: Controller method references
|
|
71
|
+
const controllerMatch = ref.referenceName.match(/^([A-Z][a-zA-Z]+Controller)@(\w+)$/);
|
|
72
|
+
if (controllerMatch) {
|
|
73
|
+
const [, controller, method] = controllerMatch;
|
|
74
|
+
const result = resolveControllerMethod(controller, method, context);
|
|
75
|
+
if (result) {
|
|
76
|
+
return {
|
|
77
|
+
original: ref,
|
|
78
|
+
targetNodeId: result,
|
|
79
|
+
confidence: 0.9,
|
|
80
|
+
resolvedBy: 'framework',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
},
|
|
86
|
+
extract(filePath, content) {
|
|
87
|
+
if (!filePath.endsWith('.php'))
|
|
88
|
+
return { nodes: [], references: [] };
|
|
89
|
+
const nodes = [];
|
|
90
|
+
const references = [];
|
|
91
|
+
const now = Date.now();
|
|
92
|
+
const safe = (0, strip_comments_1.stripCommentsForRegex)(content, 'php');
|
|
93
|
+
// Route::METHOD('/path', handler-expr)
|
|
94
|
+
// handler-expr can be: [Class::class, 'method'] | 'Controller@method' | Closure | Class::class
|
|
95
|
+
const routeRegex = /Route::(get|post|put|patch|delete|options|any)\s*\(\s*['"]([^'"]+)['"]\s*,\s*([^)]+)\)/g;
|
|
96
|
+
let match;
|
|
97
|
+
while ((match = routeRegex.exec(safe)) !== null) {
|
|
98
|
+
const [, method, routePath, handlerExpr] = match;
|
|
99
|
+
const line = safe.slice(0, match.index).split('\n').length;
|
|
100
|
+
const upper = method.toUpperCase();
|
|
101
|
+
const routeNode = {
|
|
102
|
+
id: `route:${filePath}:${line}:${upper}:${routePath}`,
|
|
103
|
+
kind: 'route',
|
|
104
|
+
name: `${upper} ${routePath}`,
|
|
105
|
+
qualifiedName: `${filePath}::route:${routePath}`,
|
|
106
|
+
filePath,
|
|
107
|
+
startLine: line,
|
|
108
|
+
endLine: line,
|
|
109
|
+
startColumn: 0,
|
|
110
|
+
endColumn: match[0].length,
|
|
111
|
+
language: 'php',
|
|
112
|
+
updatedAt: now,
|
|
113
|
+
};
|
|
114
|
+
nodes.push(routeNode);
|
|
115
|
+
const handlerName = extractLaravelHandler(handlerExpr);
|
|
116
|
+
if (handlerName) {
|
|
117
|
+
references.push({
|
|
118
|
+
fromNodeId: routeNode.id,
|
|
119
|
+
referenceName: handlerName,
|
|
120
|
+
referenceKind: 'references',
|
|
121
|
+
line,
|
|
122
|
+
column: 0,
|
|
123
|
+
filePath,
|
|
124
|
+
language: 'php',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Route::resource('name', Controller::class) / Route::apiResource('name', Controller::class)
|
|
129
|
+
const resourceRegex = /Route::(resource|apiResource)\s*\(\s*['"]([^'"]+)['"]\s*(?:,\s*([^)]+))?\)/g;
|
|
130
|
+
while ((match = resourceRegex.exec(safe)) !== null) {
|
|
131
|
+
const [, _fn, resourceName, handlerExpr] = match;
|
|
132
|
+
const line = safe.slice(0, match.index).split('\n').length;
|
|
133
|
+
const routeNode = {
|
|
134
|
+
id: `route:${filePath}:${line}:RESOURCE:${resourceName}`,
|
|
135
|
+
kind: 'route',
|
|
136
|
+
name: `resource:${resourceName}`,
|
|
137
|
+
qualifiedName: `${filePath}::route:${resourceName}`,
|
|
138
|
+
filePath,
|
|
139
|
+
startLine: line,
|
|
140
|
+
endLine: line,
|
|
141
|
+
startColumn: 0,
|
|
142
|
+
endColumn: match[0].length,
|
|
143
|
+
language: 'php',
|
|
144
|
+
updatedAt: now,
|
|
145
|
+
};
|
|
146
|
+
nodes.push(routeNode);
|
|
147
|
+
if (handlerExpr) {
|
|
148
|
+
const controllerName = extractLaravelHandler(handlerExpr);
|
|
149
|
+
if (controllerName) {
|
|
150
|
+
references.push({
|
|
151
|
+
fromNodeId: routeNode.id,
|
|
152
|
+
referenceName: controllerName,
|
|
153
|
+
referenceKind: 'imports',
|
|
154
|
+
line,
|
|
155
|
+
column: 0,
|
|
156
|
+
filePath,
|
|
157
|
+
language: 'php',
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return { nodes, references };
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Parse a Laravel route handler expression and return the symbol to link.
|
|
167
|
+
* - `[Class::class, 'method']` -> `method`
|
|
168
|
+
* - `'Controller@method'` -> `method`
|
|
169
|
+
* - `Class::class` -> `Class`
|
|
170
|
+
* - anything else (closure etc) -> null
|
|
171
|
+
*/
|
|
172
|
+
function extractLaravelHandler(expr) {
|
|
173
|
+
const trimmed = expr.trim();
|
|
174
|
+
// [Class::class, 'method'] — grab the string literal
|
|
175
|
+
const tupleMatch = trimmed.match(/^\[\s*[^,]+,\s*['"]([^'"]+)['"]\s*\]/);
|
|
176
|
+
if (tupleMatch)
|
|
177
|
+
return tupleMatch[1];
|
|
178
|
+
// 'Controller@method'
|
|
179
|
+
const atMatch = trimmed.match(/^['"]([^'"@]+)@([^'"]+)['"]$/);
|
|
180
|
+
if (atMatch)
|
|
181
|
+
return atMatch[2];
|
|
182
|
+
// Controller::class
|
|
183
|
+
const classMatch = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)::class/);
|
|
184
|
+
if (classMatch)
|
|
185
|
+
return classMatch[1];
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Resolve a Model::method() call
|
|
190
|
+
*/
|
|
191
|
+
function resolveModelCall(className, methodName, context) {
|
|
192
|
+
// Try app/Models/ first (Laravel 8+)
|
|
193
|
+
let modelPath = `app/Models/${className}.php`;
|
|
194
|
+
if (context.fileExists(modelPath)) {
|
|
195
|
+
const nodes = context.getNodesInFile(modelPath);
|
|
196
|
+
// Look for the method in this class
|
|
197
|
+
const methodNode = nodes.find((n) => n.kind === 'method' && n.name === methodName);
|
|
198
|
+
if (methodNode) {
|
|
199
|
+
return methodNode.id;
|
|
200
|
+
}
|
|
201
|
+
// Return the class itself if method not found
|
|
202
|
+
const classNode = nodes.find((n) => n.kind === 'class' && n.name === className);
|
|
203
|
+
if (classNode) {
|
|
204
|
+
return classNode.id;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// Try app/ (Laravel 7 and below)
|
|
208
|
+
modelPath = `app/${className}.php`;
|
|
209
|
+
if (context.fileExists(modelPath)) {
|
|
210
|
+
const nodes = context.getNodesInFile(modelPath);
|
|
211
|
+
const methodNode = nodes.find((n) => n.kind === 'method' && n.name === methodName);
|
|
212
|
+
if (methodNode) {
|
|
213
|
+
return methodNode.id;
|
|
214
|
+
}
|
|
215
|
+
const classNode = nodes.find((n) => n.kind === 'class' && n.name === className);
|
|
216
|
+
if (classNode) {
|
|
217
|
+
return classNode.id;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Resolve a Controller@method reference
|
|
224
|
+
*/
|
|
225
|
+
function resolveControllerMethod(controller, method, context) {
|
|
226
|
+
// Try app/Http/Controllers/
|
|
227
|
+
const controllerPath = `app/Http/Controllers/${controller}.php`;
|
|
228
|
+
if (context.fileExists(controllerPath)) {
|
|
229
|
+
const nodes = context.getNodesInFile(controllerPath);
|
|
230
|
+
const methodNode = nodes.find((n) => n.kind === 'method' && n.name === method);
|
|
231
|
+
if (methodNode) {
|
|
232
|
+
return methodNode.id;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// Try name-based lookup for namespaced controllers
|
|
236
|
+
const controllerCandidates = context.getNodesByName(controller);
|
|
237
|
+
for (const ctrl of controllerCandidates) {
|
|
238
|
+
if (ctrl.kind === 'class' && ctrl.filePath.includes('Controllers')) {
|
|
239
|
+
const nodesInFile = context.getNodesInFile(ctrl.filePath);
|
|
240
|
+
const methodNode = nodesInFile.find((n) => n.kind === 'method' && n.name === method);
|
|
241
|
+
if (methodNode) {
|
|
242
|
+
return methodNode.id;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=laravel.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NestJS Framework Resolver
|
|
3
|
+
*
|
|
4
|
+
* Handles NestJS decorator-based routing across its transport layers:
|
|
5
|
+
* - HTTP: @Controller(prefix) + @Get/@Post/@Put/@Patch/@Delete/@Head/@Options/@All
|
|
6
|
+
* - GraphQL: @Resolver + @Query/@Mutation/@Subscription
|
|
7
|
+
* - Microservices: @MessagePattern / @EventPattern
|
|
8
|
+
* - WebSockets: @WebSocketGateway(namespace) + @SubscribeMessage(event)
|
|
9
|
+
*
|
|
10
|
+
* Like the other framework extractors this is regex-over-source (comment-
|
|
11
|
+
* stripped), not AST traversal. NestJS differs from Spring/ASP.NET in two ways
|
|
12
|
+
* that this resolver has to account for:
|
|
13
|
+
*
|
|
14
|
+
* 1. An HTTP route's path is split across TWO decorators — the class-level
|
|
15
|
+
* `@Controller` prefix and the method-level `@Get`/`@Post` path — and both
|
|
16
|
+
* are frequently empty (`@Controller()`, `@Get()`). We pair each method
|
|
17
|
+
* decorator with its enclosing class and join the two paths.
|
|
18
|
+
*
|
|
19
|
+
* 2. `@Query()` is overloaded: it's a GraphQL *method* decorator (from
|
|
20
|
+
* `@nestjs/graphql`) AND a REST *parameter* decorator (from
|
|
21
|
+
* `@nestjs/common`). We only treat it as GraphQL when it sits inside an
|
|
22
|
+
* `@Resolver` class, which is what disambiguates the two.
|
|
23
|
+
*/
|
|
24
|
+
import { FrameworkResolver } from '../types'
|
|
25
|
+
export declare const nestjsResolver: FrameworkResolver
|
|
26
|
+
//# sourceMappingURL=nestjs.d.ts.map
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* NestJS Framework Resolver
|
|
4
|
+
*
|
|
5
|
+
* Handles NestJS decorator-based routing across its transport layers:
|
|
6
|
+
* - HTTP: @Controller(prefix) + @Get/@Post/@Put/@Patch/@Delete/@Head/@Options/@All
|
|
7
|
+
* - GraphQL: @Resolver + @Query/@Mutation/@Subscription
|
|
8
|
+
* - Microservices: @MessagePattern / @EventPattern
|
|
9
|
+
* - WebSockets: @WebSocketGateway(namespace) + @SubscribeMessage(event)
|
|
10
|
+
*
|
|
11
|
+
* Like the other framework extractors this is regex-over-source (comment-
|
|
12
|
+
* stripped), not AST traversal. NestJS differs from Spring/ASP.NET in two ways
|
|
13
|
+
* that this resolver has to account for:
|
|
14
|
+
*
|
|
15
|
+
* 1. An HTTP route's path is split across TWO decorators — the class-level
|
|
16
|
+
* `@Controller` prefix and the method-level `@Get`/`@Post` path — and both
|
|
17
|
+
* are frequently empty (`@Controller()`, `@Get()`). We pair each method
|
|
18
|
+
* decorator with its enclosing class and join the two paths.
|
|
19
|
+
*
|
|
20
|
+
* 2. `@Query()` is overloaded: it's a GraphQL *method* decorator (from
|
|
21
|
+
* `@nestjs/graphql`) AND a REST *parameter* decorator (from
|
|
22
|
+
* `@nestjs/common`). We only treat it as GraphQL when it sits inside an
|
|
23
|
+
* `@Resolver` class, which is what disambiguates the two.
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.nestjsResolver = void 0;
|
|
27
|
+
const strip_comments_1 = require("../strip-comments");
|
|
28
|
+
const HTTP_METHODS = ['Get', 'Post', 'Put', 'Patch', 'Delete', 'Head', 'Options', 'All'];
|
|
29
|
+
const GQL_OPS = ['Query', 'Mutation', 'Subscription'];
|
|
30
|
+
exports.nestjsResolver = {
|
|
31
|
+
name: 'nestjs',
|
|
32
|
+
languages: ['typescript', 'javascript'],
|
|
33
|
+
detect(context) {
|
|
34
|
+
// Primary, fast path: any @nestjs/* dependency in package.json.
|
|
35
|
+
const packageJson = context.readFile('package.json');
|
|
36
|
+
if (packageJson) {
|
|
37
|
+
try {
|
|
38
|
+
const pkg = JSON.parse(packageJson);
|
|
39
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
40
|
+
if (Object.keys(deps).some((k) => k.startsWith('@nestjs/'))) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Invalid JSON — fall through to the source scan.
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Fallback: NestJS-specific decorators in conventionally named files.
|
|
49
|
+
const allFiles = context.getAllFiles();
|
|
50
|
+
for (const file of allFiles) {
|
|
51
|
+
if (file.endsWith('.controller.ts') ||
|
|
52
|
+
file.endsWith('.controller.js') ||
|
|
53
|
+
file.endsWith('.module.ts') ||
|
|
54
|
+
file.endsWith('.resolver.ts') ||
|
|
55
|
+
file.endsWith('.gateway.ts')) {
|
|
56
|
+
const content = context.readFile(file);
|
|
57
|
+
if (content &&
|
|
58
|
+
(content.includes('@nestjs/') ||
|
|
59
|
+
content.includes('@Controller') ||
|
|
60
|
+
content.includes('@Module(') ||
|
|
61
|
+
content.includes('@Resolver(') ||
|
|
62
|
+
content.includes('@WebSocketGateway('))) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
},
|
|
69
|
+
resolve(ref, context) {
|
|
70
|
+
// Resolve provider/controller references (e.g. constructor-injected
|
|
71
|
+
// `UsersService`) to their class, preferring the Nest file-name
|
|
72
|
+
// convention (`*.service.ts`, `*.controller.ts`, …).
|
|
73
|
+
for (const [suffix, convention] of PROVIDER_CONVENTIONS) {
|
|
74
|
+
if (!suffix.test(ref.referenceName))
|
|
75
|
+
continue;
|
|
76
|
+
const candidates = context
|
|
77
|
+
.getNodesByName(ref.referenceName)
|
|
78
|
+
.filter((n) => n.kind === 'class');
|
|
79
|
+
if (candidates.length === 0)
|
|
80
|
+
return null;
|
|
81
|
+
const preferred = candidates.find((n) => n.filePath.includes(convention));
|
|
82
|
+
const target = preferred ?? candidates[0];
|
|
83
|
+
return {
|
|
84
|
+
original: ref,
|
|
85
|
+
targetNodeId: target.id,
|
|
86
|
+
confidence: preferred ? 0.85 : 0.7,
|
|
87
|
+
resolvedBy: 'framework',
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
},
|
|
92
|
+
extract(filePath, content) {
|
|
93
|
+
if (!/\.(m?js|tsx?|cjs)$/.test(filePath))
|
|
94
|
+
return { nodes: [], references: [] };
|
|
95
|
+
const nodes = [];
|
|
96
|
+
const references = [];
|
|
97
|
+
const now = Date.now();
|
|
98
|
+
const lang = detectLanguage(filePath);
|
|
99
|
+
const safe = (0, strip_comments_1.stripCommentsForRegex)(content, lang);
|
|
100
|
+
const addRoute = (index, method, path, length, handler) => {
|
|
101
|
+
const line = lineAt(safe, index);
|
|
102
|
+
const node = {
|
|
103
|
+
id: `route:${filePath}:${line}:${method}:${path}`,
|
|
104
|
+
kind: 'route',
|
|
105
|
+
name: `${method} ${path}`,
|
|
106
|
+
qualifiedName: `${filePath}::${method}:${path}`,
|
|
107
|
+
filePath,
|
|
108
|
+
startLine: line,
|
|
109
|
+
endLine: line,
|
|
110
|
+
startColumn: 0,
|
|
111
|
+
endColumn: length,
|
|
112
|
+
language: lang,
|
|
113
|
+
updatedAt: now,
|
|
114
|
+
};
|
|
115
|
+
nodes.push(node);
|
|
116
|
+
if (handler) {
|
|
117
|
+
references.push({
|
|
118
|
+
fromNodeId: node.id,
|
|
119
|
+
referenceName: handler,
|
|
120
|
+
referenceKind: 'references',
|
|
121
|
+
line,
|
|
122
|
+
column: 0,
|
|
123
|
+
filePath,
|
|
124
|
+
language: lang,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const scopes = buildClassScopes(safe);
|
|
129
|
+
// HTTP routes: method decorator path joined onto the enclosing controller's prefix.
|
|
130
|
+
for (const hit of findDecorators(safe, HTTP_METHODS)) {
|
|
131
|
+
const scope = scopeFor(scopes, hit.index);
|
|
132
|
+
const prefix = scope && scope.kind === 'controller' ? scope.prefix : '';
|
|
133
|
+
const path = joinHttpPath(prefix, parseStringArg(hit.args));
|
|
134
|
+
addRoute(hit.index, hit.name.toUpperCase(), path, hit.length, methodNameAfter(safe, hit.end));
|
|
135
|
+
}
|
|
136
|
+
// GraphQL operations: only inside an @Resolver class (disambiguates the
|
|
137
|
+
// REST `@Query()` parameter decorator, which lives inside @Controller classes).
|
|
138
|
+
for (const hit of findDecorators(safe, GQL_OPS)) {
|
|
139
|
+
const scope = scopeFor(scopes, hit.index);
|
|
140
|
+
if (!scope || scope.kind !== 'resolver')
|
|
141
|
+
continue;
|
|
142
|
+
const handler = methodNameAfter(safe, hit.end);
|
|
143
|
+
const name = parseGraphqlName(hit.args, handler);
|
|
144
|
+
addRoute(hit.index, hit.name.toUpperCase(), name, hit.length, handler);
|
|
145
|
+
}
|
|
146
|
+
// Microservice message/event handlers.
|
|
147
|
+
for (const hit of findDecorators(safe, ['MessagePattern', 'EventPattern'])) {
|
|
148
|
+
const verb = hit.name === 'EventPattern' ? 'EVENT' : 'MESSAGE';
|
|
149
|
+
const handler = methodNameAfter(safe, hit.end);
|
|
150
|
+
addRoute(hit.index, verb, parseStringArg(hit.args) || handler || '', hit.length, handler);
|
|
151
|
+
}
|
|
152
|
+
// WebSocket message handlers, prefixed with the gateway namespace when present.
|
|
153
|
+
for (const hit of findDecorators(safe, ['SubscribeMessage'])) {
|
|
154
|
+
const scope = scopeFor(scopes, hit.index);
|
|
155
|
+
const namespace = scope && scope.kind === 'gateway' ? scope.prefix : '';
|
|
156
|
+
const handler = methodNameAfter(safe, hit.end);
|
|
157
|
+
const event = parseStringArg(hit.args) || handler || '';
|
|
158
|
+
addRoute(hit.index, 'WS', namespace ? `${namespace}:${event}` : event, hit.length, handler);
|
|
159
|
+
}
|
|
160
|
+
return { nodes, references };
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
// Provider resolution conventions
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
const PROVIDER_CONVENTIONS = [
|
|
167
|
+
[/Service$/, '.service.'],
|
|
168
|
+
[/Controller$/, '.controller.'],
|
|
169
|
+
[/Resolver$/, '.resolver.'],
|
|
170
|
+
[/Gateway$/, '.gateway.'],
|
|
171
|
+
[/Repository$/, '.repository.'],
|
|
172
|
+
[/Guard$/, '.guard.'],
|
|
173
|
+
[/Interceptor$/, '.interceptor.'],
|
|
174
|
+
[/Pipe$/, '.pipe.'],
|
|
175
|
+
[/Module$/, '.module.'],
|
|
176
|
+
];
|
|
177
|
+
/**
|
|
178
|
+
* Find every `@Name(...)` decorator whose name is in `names`. Uses a
|
|
179
|
+
* string-aware balanced-paren reader for the argument list so type thunks
|
|
180
|
+
* like `@Query(() => [User])` are captured whole rather than truncated at the
|
|
181
|
+
* inner `()`.
|
|
182
|
+
*/
|
|
183
|
+
function findDecorators(safe, names) {
|
|
184
|
+
const hits = [];
|
|
185
|
+
const re = new RegExp(`@(${names.join('|')})\\s*\\(`, 'g');
|
|
186
|
+
let m;
|
|
187
|
+
while ((m = re.exec(safe)) !== null) {
|
|
188
|
+
const openIndex = m.index + m[0].length - 1; // position of '('
|
|
189
|
+
const parsed = readArgs(safe, openIndex);
|
|
190
|
+
if (!parsed)
|
|
191
|
+
continue;
|
|
192
|
+
hits.push({
|
|
193
|
+
name: m[1],
|
|
194
|
+
args: parsed.args,
|
|
195
|
+
index: m.index,
|
|
196
|
+
end: parsed.end,
|
|
197
|
+
length: parsed.end - m.index,
|
|
198
|
+
});
|
|
199
|
+
re.lastIndex = parsed.end; // resume past the args so nested text isn't re-scanned
|
|
200
|
+
}
|
|
201
|
+
return hits;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Read a balanced `(...)` starting at `openIndex` (which must point at `(`).
|
|
205
|
+
* String-aware, so parens inside string literals don't unbalance the count.
|
|
206
|
+
* Returns the inner text and the index just past the closing `)`.
|
|
207
|
+
*/
|
|
208
|
+
function readArgs(s, openIndex) {
|
|
209
|
+
if (s[openIndex] !== '(')
|
|
210
|
+
return null;
|
|
211
|
+
let depth = 0;
|
|
212
|
+
let inStr = null;
|
|
213
|
+
for (let i = openIndex; i < s.length; i++) {
|
|
214
|
+
const ch = s[i];
|
|
215
|
+
if (inStr) {
|
|
216
|
+
if (ch === '\\') {
|
|
217
|
+
i++;
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (ch === inStr)
|
|
221
|
+
inStr = null;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
225
|
+
inStr = ch;
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (ch === '(')
|
|
229
|
+
depth++;
|
|
230
|
+
else if (ch === ')') {
|
|
231
|
+
depth--;
|
|
232
|
+
if (depth === 0)
|
|
233
|
+
return { args: s.slice(openIndex + 1, i), end: i + 1 };
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Starting just after a method decorator's `)`, return the name of the method
|
|
240
|
+
* it decorates. Skips any further stacked decorators (`@UseGuards(...)`,
|
|
241
|
+
* `@HttpCode(204)`, …) and access/async modifiers in between.
|
|
242
|
+
*/
|
|
243
|
+
function methodNameAfter(safe, start) {
|
|
244
|
+
let i = start;
|
|
245
|
+
const ws = /\s*/y;
|
|
246
|
+
const decoName = /@[\w.]+/y;
|
|
247
|
+
const modifier = /(?:public|private|protected|async|static)\b/y;
|
|
248
|
+
const ident = /([A-Za-z_$][\w$]*)\s*\(/y;
|
|
249
|
+
const eatWs = () => {
|
|
250
|
+
ws.lastIndex = i;
|
|
251
|
+
if (ws.exec(safe))
|
|
252
|
+
i = ws.lastIndex;
|
|
253
|
+
};
|
|
254
|
+
// Skip stacked decorators.
|
|
255
|
+
for (;;) {
|
|
256
|
+
eatWs();
|
|
257
|
+
if (safe[i] !== '@')
|
|
258
|
+
break;
|
|
259
|
+
decoName.lastIndex = i;
|
|
260
|
+
if (!decoName.exec(safe))
|
|
261
|
+
break;
|
|
262
|
+
i = decoName.lastIndex;
|
|
263
|
+
eatWs();
|
|
264
|
+
if (safe[i] === '(') {
|
|
265
|
+
const parsed = readArgs(safe, i);
|
|
266
|
+
if (!parsed)
|
|
267
|
+
return null;
|
|
268
|
+
i = parsed.end;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// Skip access/async/static modifiers.
|
|
272
|
+
for (;;) {
|
|
273
|
+
eatWs();
|
|
274
|
+
modifier.lastIndex = i;
|
|
275
|
+
if (modifier.exec(safe) && modifier.lastIndex > i) {
|
|
276
|
+
i = modifier.lastIndex;
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
eatWs();
|
|
282
|
+
ident.lastIndex = i;
|
|
283
|
+
const m = ident.exec(safe);
|
|
284
|
+
return m ? m[1] : null;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Build the list of class-level decorator scopes, sorted by position. Each
|
|
288
|
+
* scope runs from its decorator up to the next class decorator (of any kind),
|
|
289
|
+
* which lets a method decorator find its enclosing class regardless of how
|
|
290
|
+
* many classes share a file.
|
|
291
|
+
*/
|
|
292
|
+
function buildClassScopes(safe) {
|
|
293
|
+
const defs = [
|
|
294
|
+
{ kind: 'controller', name: 'Controller', prefixOf: parseControllerPrefix },
|
|
295
|
+
{ kind: 'resolver', name: 'Resolver', prefixOf: () => '' },
|
|
296
|
+
{ kind: 'gateway', name: 'WebSocketGateway', prefixOf: parseGatewayNamespace },
|
|
297
|
+
{ kind: 'other', name: 'Injectable', prefixOf: () => '' },
|
|
298
|
+
{ kind: 'other', name: 'Module', prefixOf: () => '' },
|
|
299
|
+
{ kind: 'other', name: 'Catch', prefixOf: () => '' },
|
|
300
|
+
];
|
|
301
|
+
const raw = [];
|
|
302
|
+
for (const def of defs) {
|
|
303
|
+
for (const hit of findDecorators(safe, [def.name])) {
|
|
304
|
+
raw.push({ kind: def.kind, prefix: def.prefixOf(hit.args), index: hit.index });
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
raw.sort((a, b) => a.index - b.index);
|
|
308
|
+
return raw.map((r, i) => ({
|
|
309
|
+
kind: r.kind,
|
|
310
|
+
prefix: r.prefix,
|
|
311
|
+
start: r.index,
|
|
312
|
+
end: i + 1 < raw.length ? raw[i + 1].index : safe.length,
|
|
313
|
+
}));
|
|
314
|
+
}
|
|
315
|
+
function scopeFor(scopes, index) {
|
|
316
|
+
for (const s of scopes) {
|
|
317
|
+
if (index >= s.start && index < s.end)
|
|
318
|
+
return s;
|
|
319
|
+
}
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
// ---------------------------------------------------------------------------
|
|
323
|
+
// Argument parsing
|
|
324
|
+
// ---------------------------------------------------------------------------
|
|
325
|
+
/** First string literal anywhere in the args, or '' (covers `'x'`, `{ k: 'x' }`). */
|
|
326
|
+
function parseStringArg(args) {
|
|
327
|
+
const m = args.match(/['"`]([^'"`]*)['"`]/);
|
|
328
|
+
return m ? m[1] : '';
|
|
329
|
+
}
|
|
330
|
+
/** `@Controller('users')` | `@Controller({ path: 'users', host })` | `@Controller(['a','b'])` | `@Controller()`. */
|
|
331
|
+
function parseControllerPrefix(args) {
|
|
332
|
+
const obj = args.match(/path\s*:\s*['"`]([^'"`]*)['"`]/);
|
|
333
|
+
if (obj)
|
|
334
|
+
return obj[1];
|
|
335
|
+
return parseStringArg(args);
|
|
336
|
+
}
|
|
337
|
+
/** `@WebSocketGateway({ namespace: 'chat' })` | `@WebSocketGateway(81, { namespace: '/chat' })` | `@WebSocketGateway()`. */
|
|
338
|
+
function parseGatewayNamespace(args) {
|
|
339
|
+
const m = args.match(/namespace\s*:\s*['"`]([^'"`]*)['"`]/);
|
|
340
|
+
return m ? m[1] : '';
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* GraphQL operation name. Prefers an explicit `{ name: 'x' }` or a leading
|
|
344
|
+
* string literal (`@Query('users')`); otherwise the field name defaults to the
|
|
345
|
+
* handler method name. Avoids mistaking a `description` string for the name.
|
|
346
|
+
*/
|
|
347
|
+
function parseGraphqlName(args, handler) {
|
|
348
|
+
const named = args.match(/name\s*:\s*['"`]([^'"`]*)['"`]/);
|
|
349
|
+
if (named)
|
|
350
|
+
return named[1];
|
|
351
|
+
const lead = args.match(/^\s*['"`]([^'"`]*)['"`]/);
|
|
352
|
+
if (lead)
|
|
353
|
+
return lead[1];
|
|
354
|
+
return handler ?? '';
|
|
355
|
+
}
|
|
356
|
+
// ---------------------------------------------------------------------------
|
|
357
|
+
// Path helpers
|
|
358
|
+
// ---------------------------------------------------------------------------
|
|
359
|
+
/** Join a controller prefix and method path into a single normalised `/path`. */
|
|
360
|
+
function joinHttpPath(prefix, sub) {
|
|
361
|
+
const parts = [prefix, sub]
|
|
362
|
+
.map((p) => p.trim().replace(/^\/+|\/+$/g, ''))
|
|
363
|
+
.filter((p) => p.length > 0);
|
|
364
|
+
return '/' + parts.join('/');
|
|
365
|
+
}
|
|
366
|
+
function lineAt(safe, index) {
|
|
367
|
+
return safe.slice(0, index).split('\n').length;
|
|
368
|
+
}
|
|
369
|
+
function detectLanguage(filePath) {
|
|
370
|
+
if (filePath.endsWith('.ts') || filePath.endsWith('.tsx'))
|
|
371
|
+
return 'typescript';
|
|
372
|
+
return 'javascript';
|
|
373
|
+
}
|
|
374
|
+
//# sourceMappingURL=nestjs.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python Framework Resolver
|
|
3
|
+
*
|
|
4
|
+
* Handles Django, Flask, and FastAPI patterns.
|
|
5
|
+
*/
|
|
6
|
+
import { FrameworkResolver } from '../types'
|
|
7
|
+
export declare const djangoResolver: FrameworkResolver
|
|
8
|
+
export declare const flaskResolver: FrameworkResolver
|
|
9
|
+
export declare const fastapiResolver: FrameworkResolver
|
|
10
|
+
//# sourceMappingURL=python.d.ts.map
|