codesight 1.2.0 → 1.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/README.md +26 -2
- package/dist/ast/extract-components.d.ts +12 -0
- package/dist/ast/extract-components.js +180 -0
- package/dist/ast/extract-routes.d.ts +13 -0
- package/dist/ast/extract-routes.js +271 -0
- package/dist/ast/extract-schema.d.ts +15 -0
- package/dist/ast/extract-schema.js +302 -0
- package/dist/ast/loader.d.ts +20 -0
- package/dist/ast/loader.js +105 -0
- package/dist/detectors/components.js +11 -0
- package/dist/detectors/routes.js +91 -15
- package/dist/detectors/schema.js +20 -0
- package/dist/index.js +12 -2
- package/dist/types.d.ts +5 -0
- package/package.json +1 -1
package/dist/detectors/routes.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { relative, basename } from "node:path";
|
|
2
2
|
import { readFileSafe } from "../scanner.js";
|
|
3
|
+
import { loadTypeScript } from "../ast/loader.js";
|
|
4
|
+
import { extractRoutesAST } from "../ast/extract-routes.js";
|
|
3
5
|
const HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"];
|
|
4
6
|
const TAG_PATTERNS = [
|
|
5
7
|
["auth", [/auth/i, /jwt/i, /token/i, /session/i, /bearer/i, /passport/i, /clerk/i, /betterAuth/i, /better-auth/i]],
|
|
@@ -157,11 +159,22 @@ async function detectNextPagesApi(files, project) {
|
|
|
157
159
|
async function detectHonoRoutes(files, project) {
|
|
158
160
|
const tsFiles = files.filter((f) => f.match(/\.(ts|js|tsx|jsx|mjs)$/));
|
|
159
161
|
const routes = [];
|
|
162
|
+
const ts = loadTypeScript(project.root);
|
|
160
163
|
for (const file of tsFiles) {
|
|
161
164
|
const content = await readFileSafe(file);
|
|
162
165
|
if (!content.includes("hono") && !content.includes("Hono"))
|
|
163
166
|
continue;
|
|
164
167
|
const rel = relative(project.root, file);
|
|
168
|
+
const tags = detectTags(content);
|
|
169
|
+
// Try AST first
|
|
170
|
+
if (ts) {
|
|
171
|
+
const astRoutes = extractRoutesAST(ts, rel, content, "hono", tags);
|
|
172
|
+
if (astRoutes.length > 0) {
|
|
173
|
+
routes.push(...astRoutes);
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Regex fallback
|
|
165
178
|
const routePattern = /\.\s*(get|post|put|patch|delete|options|all)\s*\(\s*['"`]([^'"`]+)['"`]/gi;
|
|
166
179
|
let match;
|
|
167
180
|
while ((match = routePattern.exec(content)) !== null) {
|
|
@@ -172,8 +185,9 @@ async function detectHonoRoutes(files, project) {
|
|
|
172
185
|
method: match[1].toUpperCase(),
|
|
173
186
|
path,
|
|
174
187
|
file: rel,
|
|
175
|
-
tags
|
|
188
|
+
tags,
|
|
176
189
|
framework: "hono",
|
|
190
|
+
confidence: "regex",
|
|
177
191
|
});
|
|
178
192
|
}
|
|
179
193
|
}
|
|
@@ -183,11 +197,22 @@ async function detectHonoRoutes(files, project) {
|
|
|
183
197
|
async function detectExpressRoutes(files, project) {
|
|
184
198
|
const tsFiles = files.filter((f) => f.match(/\.(ts|js|mjs|cjs)$/));
|
|
185
199
|
const routes = [];
|
|
200
|
+
const ts = loadTypeScript(project.root);
|
|
186
201
|
for (const file of tsFiles) {
|
|
187
202
|
const content = await readFileSafe(file);
|
|
188
203
|
if (!content.includes("express") && !content.includes("Router"))
|
|
189
204
|
continue;
|
|
190
205
|
const rel = relative(project.root, file);
|
|
206
|
+
const tags = detectTags(content);
|
|
207
|
+
// Try AST first
|
|
208
|
+
if (ts) {
|
|
209
|
+
const astRoutes = extractRoutesAST(ts, rel, content, "express", tags);
|
|
210
|
+
if (astRoutes.length > 0) {
|
|
211
|
+
routes.push(...astRoutes);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// Regex fallback
|
|
191
216
|
const routePattern = /(?:app|router|server)\s*\.\s*(get|post|put|patch|delete|options|all)\s*\(\s*['"`]([^'"`]+)['"`]/gi;
|
|
192
217
|
let match;
|
|
193
218
|
while ((match = routePattern.exec(content)) !== null) {
|
|
@@ -195,8 +220,9 @@ async function detectExpressRoutes(files, project) {
|
|
|
195
220
|
method: match[1].toUpperCase(),
|
|
196
221
|
path: match[2],
|
|
197
222
|
file: rel,
|
|
198
|
-
tags
|
|
223
|
+
tags,
|
|
199
224
|
framework: "express",
|
|
225
|
+
confidence: "regex",
|
|
200
226
|
});
|
|
201
227
|
}
|
|
202
228
|
}
|
|
@@ -206,11 +232,22 @@ async function detectExpressRoutes(files, project) {
|
|
|
206
232
|
async function detectFastifyRoutes(files, project) {
|
|
207
233
|
const tsFiles = files.filter((f) => f.match(/\.(ts|js|mjs|cjs)$/));
|
|
208
234
|
const routes = [];
|
|
235
|
+
const ts = loadTypeScript(project.root);
|
|
209
236
|
for (const file of tsFiles) {
|
|
210
237
|
const content = await readFileSafe(file);
|
|
211
238
|
if (!content.includes("fastify"))
|
|
212
239
|
continue;
|
|
213
240
|
const rel = relative(project.root, file);
|
|
241
|
+
const tags = detectTags(content);
|
|
242
|
+
// Try AST first
|
|
243
|
+
if (ts) {
|
|
244
|
+
const astRoutes = extractRoutesAST(ts, rel, content, "fastify", tags);
|
|
245
|
+
if (astRoutes.length > 0) {
|
|
246
|
+
routes.push(...astRoutes);
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// Regex fallback
|
|
214
251
|
const routePattern = /(?:fastify|server|app)\s*\.\s*(get|post|put|patch|delete|options|all)\s*\(\s*['"`]([^'"`]+)['"`]/gi;
|
|
215
252
|
let match;
|
|
216
253
|
while ((match = routePattern.exec(content)) !== null) {
|
|
@@ -218,8 +255,9 @@ async function detectFastifyRoutes(files, project) {
|
|
|
218
255
|
method: match[1].toUpperCase(),
|
|
219
256
|
path: match[2],
|
|
220
257
|
file: rel,
|
|
221
|
-
tags
|
|
258
|
+
tags,
|
|
222
259
|
framework: "fastify",
|
|
260
|
+
confidence: "regex",
|
|
223
261
|
});
|
|
224
262
|
}
|
|
225
263
|
// Object-style route registration
|
|
@@ -229,8 +267,9 @@ async function detectFastifyRoutes(files, project) {
|
|
|
229
267
|
method: match[1].toUpperCase(),
|
|
230
268
|
path: match[2],
|
|
231
269
|
file: rel,
|
|
232
|
-
tags
|
|
270
|
+
tags,
|
|
233
271
|
framework: "fastify",
|
|
272
|
+
confidence: "regex",
|
|
234
273
|
});
|
|
235
274
|
}
|
|
236
275
|
}
|
|
@@ -240,11 +279,20 @@ async function detectFastifyRoutes(files, project) {
|
|
|
240
279
|
async function detectKoaRoutes(files, project) {
|
|
241
280
|
const tsFiles = files.filter((f) => f.match(/\.(ts|js|mjs|cjs)$/));
|
|
242
281
|
const routes = [];
|
|
282
|
+
const ts = loadTypeScript(project.root);
|
|
243
283
|
for (const file of tsFiles) {
|
|
244
284
|
const content = await readFileSafe(file);
|
|
245
285
|
if (!content.includes("koa") && !content.includes("Router"))
|
|
246
286
|
continue;
|
|
247
287
|
const rel = relative(project.root, file);
|
|
288
|
+
const tags = detectTags(content);
|
|
289
|
+
if (ts) {
|
|
290
|
+
const astRoutes = extractRoutesAST(ts, rel, content, "koa", tags);
|
|
291
|
+
if (astRoutes.length > 0) {
|
|
292
|
+
routes.push(...astRoutes);
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
248
296
|
const routePattern = /router\s*\.\s*(get|post|put|patch|delete|options|all)\s*\(\s*['"`]([^'"`]+)['"`]/gi;
|
|
249
297
|
let match;
|
|
250
298
|
while ((match = routePattern.exec(content)) !== null) {
|
|
@@ -252,8 +300,9 @@ async function detectKoaRoutes(files, project) {
|
|
|
252
300
|
method: match[1].toUpperCase(),
|
|
253
301
|
path: match[2],
|
|
254
302
|
file: rel,
|
|
255
|
-
tags
|
|
303
|
+
tags,
|
|
256
304
|
framework: "koa",
|
|
305
|
+
confidence: "regex",
|
|
257
306
|
});
|
|
258
307
|
}
|
|
259
308
|
}
|
|
@@ -263,15 +312,24 @@ async function detectKoaRoutes(files, project) {
|
|
|
263
312
|
async function detectNestJSRoutes(files, project) {
|
|
264
313
|
const tsFiles = files.filter((f) => f.match(/\.(ts|js)$/));
|
|
265
314
|
const routes = [];
|
|
315
|
+
const ts = loadTypeScript(project.root);
|
|
266
316
|
for (const file of tsFiles) {
|
|
267
317
|
const content = await readFileSafe(file);
|
|
268
318
|
if (!content.includes("@Controller") && !content.includes("@Get") && !content.includes("@Post"))
|
|
269
319
|
continue;
|
|
270
320
|
const rel = relative(project.root, file);
|
|
271
|
-
|
|
321
|
+
const tags = detectTags(content);
|
|
322
|
+
// Try AST — NestJS benefits most from AST (decorator + controller prefix combining)
|
|
323
|
+
if (ts) {
|
|
324
|
+
const astRoutes = extractRoutesAST(ts, rel, content, "nestjs", tags);
|
|
325
|
+
if (astRoutes.length > 0) {
|
|
326
|
+
routes.push(...astRoutes);
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// Regex fallback
|
|
272
331
|
const controllerMatch = content.match(/@Controller\s*\(\s*['"`]([^'"`]*)['"`]\s*\)/);
|
|
273
332
|
const basePath = controllerMatch ? "/" + controllerMatch[1].replace(/^\//, "") : "";
|
|
274
|
-
// Match method decorators: @Get(), @Post('/create'), @Put(':id'), etc.
|
|
275
333
|
const decoratorPattern = /@(Get|Post|Put|Patch|Delete|Options|Head|All)\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/gi;
|
|
276
334
|
let match;
|
|
277
335
|
while ((match = decoratorPattern.exec(content)) !== null) {
|
|
@@ -282,8 +340,9 @@ async function detectNestJSRoutes(files, project) {
|
|
|
282
340
|
method,
|
|
283
341
|
path: fullPath,
|
|
284
342
|
file: rel,
|
|
285
|
-
tags
|
|
343
|
+
tags,
|
|
286
344
|
framework: "nestjs",
|
|
345
|
+
confidence: "regex",
|
|
287
346
|
});
|
|
288
347
|
}
|
|
289
348
|
}
|
|
@@ -293,11 +352,20 @@ async function detectNestJSRoutes(files, project) {
|
|
|
293
352
|
async function detectElysiaRoutes(files, project) {
|
|
294
353
|
const tsFiles = files.filter((f) => f.match(/\.(ts|js|mjs)$/));
|
|
295
354
|
const routes = [];
|
|
355
|
+
const ts = loadTypeScript(project.root);
|
|
296
356
|
for (const file of tsFiles) {
|
|
297
357
|
const content = await readFileSafe(file);
|
|
298
358
|
if (!content.includes("elysia") && !content.includes("Elysia"))
|
|
299
359
|
continue;
|
|
300
360
|
const rel = relative(project.root, file);
|
|
361
|
+
const tags = detectTags(content);
|
|
362
|
+
if (ts) {
|
|
363
|
+
const astRoutes = extractRoutesAST(ts, rel, content, "elysia", tags);
|
|
364
|
+
if (astRoutes.length > 0) {
|
|
365
|
+
routes.push(...astRoutes);
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
301
369
|
const routePattern = /\.\s*(get|post|put|patch|delete|options|all)\s*\(\s*['"`]([^'"`]+)['"`]/gi;
|
|
302
370
|
let match;
|
|
303
371
|
while ((match = routePattern.exec(content)) !== null) {
|
|
@@ -308,8 +376,9 @@ async function detectElysiaRoutes(files, project) {
|
|
|
308
376
|
method: match[1].toUpperCase(),
|
|
309
377
|
path,
|
|
310
378
|
file: rel,
|
|
311
|
-
tags
|
|
379
|
+
tags,
|
|
312
380
|
framework: "elysia",
|
|
381
|
+
confidence: "regex",
|
|
313
382
|
});
|
|
314
383
|
}
|
|
315
384
|
}
|
|
@@ -341,6 +410,7 @@ async function detectAdonisRoutes(files, project) {
|
|
|
341
410
|
async function detectTRPCRoutes(files, project) {
|
|
342
411
|
const tsFiles = files.filter((f) => f.match(/\.(ts|js)$/));
|
|
343
412
|
const routes = [];
|
|
413
|
+
const ts = loadTypeScript(project.root);
|
|
344
414
|
for (const file of tsFiles) {
|
|
345
415
|
const content = await readFileSafe(file);
|
|
346
416
|
if (!content.includes("Procedure") && !content.includes("procedure") && !content.includes("router"))
|
|
@@ -348,11 +418,16 @@ async function detectTRPCRoutes(files, project) {
|
|
|
348
418
|
if (!content.includes("trpc") && !content.includes("TRPC") && !content.includes("createTRPCRouter") && !content.includes("publicProcedure") && !content.includes("protectedProcedure"))
|
|
349
419
|
continue;
|
|
350
420
|
const rel = relative(project.root, file);
|
|
351
|
-
|
|
352
|
-
//
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
421
|
+
const tags = detectTags(content);
|
|
422
|
+
// AST handles tRPC much better — properly parses router nesting and procedure chains
|
|
423
|
+
if (ts) {
|
|
424
|
+
const astRoutes = extractRoutesAST(ts, rel, content, "trpc", tags);
|
|
425
|
+
if (astRoutes.length > 0) {
|
|
426
|
+
routes.push(...astRoutes);
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
// Regex fallback
|
|
356
431
|
const lines = content.split("\n");
|
|
357
432
|
for (const line of lines) {
|
|
358
433
|
const queryMatch = line.match(/^\s*(\w+)\s*:\s*.*\.(query)\s*\(/);
|
|
@@ -366,8 +441,9 @@ async function detectTRPCRoutes(files, project) {
|
|
|
366
441
|
method: isQuery ? "QUERY" : "MUTATION",
|
|
367
442
|
path: procName,
|
|
368
443
|
file: rel,
|
|
369
|
-
tags
|
|
444
|
+
tags,
|
|
370
445
|
framework: "trpc",
|
|
446
|
+
confidence: "regex",
|
|
371
447
|
});
|
|
372
448
|
}
|
|
373
449
|
}
|
package/dist/detectors/schema.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { readFileSafe } from "../scanner.js";
|
|
3
|
+
import { loadTypeScript } from "../ast/loader.js";
|
|
4
|
+
import { extractDrizzleSchemaAST, extractTypeORMSchemaAST } from "../ast/extract-schema.js";
|
|
3
5
|
const AUDIT_FIELDS = new Set([
|
|
4
6
|
"createdAt",
|
|
5
7
|
"updatedAt",
|
|
@@ -35,10 +37,19 @@ async function detectDrizzleSchemas(files, project) {
|
|
|
35
37
|
f.match(/\.schema\.(ts|js)$/) ||
|
|
36
38
|
f.match(/\/db\/.*\.(ts|js)$/));
|
|
37
39
|
const models = [];
|
|
40
|
+
const ts = loadTypeScript(project.root);
|
|
38
41
|
for (const file of schemaFiles) {
|
|
39
42
|
const content = await readFileSafe(file);
|
|
40
43
|
if (!content.includes("pgTable") && !content.includes("mysqlTable") && !content.includes("sqliteTable"))
|
|
41
44
|
continue;
|
|
45
|
+
// Try AST first — much more accurate for Drizzle field chains
|
|
46
|
+
if (ts) {
|
|
47
|
+
const astModels = extractDrizzleSchemaAST(ts, file, content);
|
|
48
|
+
if (astModels.length > 0) {
|
|
49
|
+
models.push(...astModels);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
42
53
|
// Match: export const users = pgTable("users", { ... })
|
|
43
54
|
const tablePattern = /(?:export\s+)?const\s+(\w+)\s*=\s*(?:pgTable|mysqlTable|sqliteTable)\s*\(\s*['"`](\w+)['"`]\s*,\s*(?:\(\s*\)\s*=>\s*\(?\s*)?\{([\s\S]*?)\}\s*\)?\s*(?:,|\))/g;
|
|
44
55
|
let match;
|
|
@@ -185,10 +196,19 @@ async function detectPrismaSchemas(project) {
|
|
|
185
196
|
async function detectTypeORMSchemas(files, project) {
|
|
186
197
|
const entityFiles = files.filter((f) => f.match(/\.entity\.(ts|js)$/) || f.match(/entities\/.*\.(ts|js)$/));
|
|
187
198
|
const models = [];
|
|
199
|
+
const ts = loadTypeScript(project.root);
|
|
188
200
|
for (const file of entityFiles) {
|
|
189
201
|
const content = await readFileSafe(file);
|
|
190
202
|
if (!content.includes("@Entity") && !content.includes("@Column"))
|
|
191
203
|
continue;
|
|
204
|
+
// Try AST first — handles TypeORM decorators accurately
|
|
205
|
+
if (ts) {
|
|
206
|
+
const astModels = extractTypeORMSchemaAST(ts, file, content);
|
|
207
|
+
if (astModels.length > 0) {
|
|
208
|
+
models.push(...astModels);
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
192
212
|
// Extract entity name
|
|
193
213
|
const entityMatch = content.match(/@Entity\s*\(\s*(?:['"`](\w+)['"`])?\s*\)/);
|
|
194
214
|
const classMatch = content.match(/class\s+(\w+)/);
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ import { calculateTokenStats } from "./detectors/tokens.js";
|
|
|
14
14
|
import { writeOutput } from "./formatter.js";
|
|
15
15
|
import { generateAIConfigs } from "./generators/ai-config.js";
|
|
16
16
|
import { generateHtmlReport } from "./generators/html-report.js";
|
|
17
|
-
const VERSION = "1.
|
|
17
|
+
const VERSION = "1.3.0";
|
|
18
18
|
const BRAND = "codesight";
|
|
19
19
|
function printHelp() {
|
|
20
20
|
console.log(`
|
|
@@ -86,7 +86,17 @@ async function scan(root, outputDirName, maxDepth) {
|
|
|
86
86
|
]);
|
|
87
87
|
// Step 4: Enrich routes with contract info
|
|
88
88
|
const routes = await enrichRouteContracts(rawRoutes, project);
|
|
89
|
-
|
|
89
|
+
// Report AST vs regex detection
|
|
90
|
+
const astRoutes = routes.filter((r) => r.confidence === "ast").length;
|
|
91
|
+
const astSchemas = schemas.filter((s) => s.confidence === "ast").length;
|
|
92
|
+
const astComponents = components.filter((c) => c.confidence === "ast").length;
|
|
93
|
+
const totalAST = astRoutes + astSchemas + astComponents;
|
|
94
|
+
if (totalAST > 0) {
|
|
95
|
+
console.log(` done (AST: ${astRoutes} routes, ${astSchemas} models, ${astComponents} components)`);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
console.log(" done");
|
|
99
|
+
}
|
|
90
100
|
// Step 5: Write output
|
|
91
101
|
process.stdout.write(" Writing output...");
|
|
92
102
|
// Temporary result without token stats to generate output
|
package/dist/types.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export interface WorkspaceInfo {
|
|
|
17
17
|
frameworks: Framework[];
|
|
18
18
|
orms: ORM[];
|
|
19
19
|
}
|
|
20
|
+
export type DetectionMethod = "ast" | "regex";
|
|
20
21
|
export interface RouteInfo {
|
|
21
22
|
method: string;
|
|
22
23
|
path: string;
|
|
@@ -26,12 +27,15 @@ export interface RouteInfo {
|
|
|
26
27
|
requestType?: string;
|
|
27
28
|
responseType?: string;
|
|
28
29
|
params?: string[];
|
|
30
|
+
confidence?: DetectionMethod;
|
|
31
|
+
middleware?: string[];
|
|
29
32
|
}
|
|
30
33
|
export interface SchemaModel {
|
|
31
34
|
name: string;
|
|
32
35
|
fields: SchemaField[];
|
|
33
36
|
relations: string[];
|
|
34
37
|
orm: ORM;
|
|
38
|
+
confidence?: DetectionMethod;
|
|
35
39
|
}
|
|
36
40
|
export interface SchemaField {
|
|
37
41
|
name: string;
|
|
@@ -41,6 +45,7 @@ export interface SchemaField {
|
|
|
41
45
|
export interface ComponentInfo {
|
|
42
46
|
name: string;
|
|
43
47
|
file: string;
|
|
48
|
+
confidence?: DetectionMethod;
|
|
44
49
|
props: string[];
|
|
45
50
|
isClient: boolean;
|
|
46
51
|
isServer: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codesight",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "See your codebase clearly. Universal AI context generator that maps routes, schema, components, dependencies, and more for Claude Code, Cursor, Copilot, Codex, and any AI coding tool.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|