codesight 1.0.0 → 1.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/README.md +200 -104
- package/dist/detectors/contracts.js +7 -0
- package/dist/detectors/graph.js +87 -23
- package/dist/detectors/routes.js +519 -13
- package/dist/formatter.js +20 -7
- package/dist/index.js +37 -1
- package/dist/scanner.js +125 -6
- package/dist/types.d.ts +3 -3
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,113 +1,178 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
### Your AI assistant wastes thousands of tokens every conversation just figuring out your project. codesight fixes that in one command.
|
|
4
|
+
|
|
5
|
+
**Zero dependencies. 25+ framework detectors. 4 ORM parsers. MCP server. One `npx` call.**
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/codesight)
|
|
8
|
+
[](https://www.npmjs.com/package/codesight)
|
|
9
|
+
[](https://www.npmjs.com/package/codesight)
|
|
10
|
+
[](https://github.com/Houseofmvps/codesight/stargazers)
|
|
11
|
+
[](LICENSE)
|
|
12
|
+
[](https://github.com/sponsors/Houseofmvps)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
[](https://x.com/kaileskkhumar)
|
|
17
|
+
[](https://www.linkedin.com/in/kailesk-khumar)
|
|
18
|
+
[](https://houseofmvps.com)
|
|
19
|
+
|
|
20
|
+
**Built by [Kailesk Khumar](https://www.linkedin.com/in/kailesk-khumar), solo founder of [houseofmvps.com](https://houseofmvps.com)**
|
|
21
|
+
|
|
22
|
+
*Also: [ultraship](https://github.com/Houseofmvps/ultraship) (39 expert skills for Claude Code) · [claude-rank](https://github.com/Houseofmvps/claude-rank) (SEO/GEO/AEO plugin for Claude Code)*
|
|
23
|
+
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
---
|
|
4
27
|
|
|
5
|
-
```
|
|
6
|
-
|
|
28
|
+
```
|
|
29
|
+
0 dependencies · Node.js >= 18 · 27 tests · MIT
|
|
7
30
|
```
|
|
8
31
|
|
|
9
|
-
|
|
32
|
+
## Works With
|
|
10
33
|
|
|
11
|
-
|
|
34
|
+
**Claude Code, Cursor, GitHub Copilot, OpenAI Codex, Windsurf, Cline, Aider**, and anything that reads markdown.
|
|
12
35
|
|
|
13
|
-
|
|
36
|
+
## Install
|
|
14
37
|
|
|
15
|
-
|
|
38
|
+
```bash
|
|
39
|
+
npx codesight
|
|
40
|
+
```
|
|
16
41
|
|
|
17
|
-
|
|
42
|
+
That's it. Run it in any project root. No config, no setup, no API keys.
|
|
18
43
|
|
|
19
44
|
```bash
|
|
20
|
-
npx codesight # Scan and generate context
|
|
21
45
|
npx codesight --init # Also generate CLAUDE.md, .cursorrules, codex.md, AGENTS.md
|
|
22
46
|
npx codesight --open # Also open interactive HTML report in browser
|
|
47
|
+
npx codesight --mcp # Start as MCP server for Claude Code / Cursor
|
|
48
|
+
npx codesight --benchmark # Show detailed token savings breakdown
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## What It Does
|
|
52
|
+
|
|
53
|
+
Every AI coding conversation starts the same way. Your assistant reads files, greps for patterns, opens configs, just to understand the project. That exploration costs tens of thousands of tokens before it writes a single line of code.
|
|
54
|
+
|
|
55
|
+
codesight scans your codebase once and generates a structured context map. Routes, database schema, components, dependencies, environment variables, middleware, all condensed into ~3,000 to 5,000 tokens of structured markdown. Your AI reads one file and knows the entire project.
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
Output size: ~3,200 tokens
|
|
59
|
+
Exploration cost: ~52,000 tokens (without codesight)
|
|
60
|
+
Saved: ~48,800 tokens per conversation
|
|
23
61
|
```
|
|
24
62
|
|
|
25
63
|
## What It Generates
|
|
26
64
|
|
|
27
65
|
```
|
|
28
66
|
.codesight/
|
|
29
|
-
CODESIGHT.md
|
|
30
|
-
routes.md
|
|
31
|
-
schema.md
|
|
32
|
-
components.md
|
|
33
|
-
libs.md
|
|
34
|
-
config.md
|
|
35
|
-
middleware.md
|
|
36
|
-
graph.md
|
|
37
|
-
report.html
|
|
67
|
+
CODESIGHT.md Combined context map (one file, full project understanding)
|
|
68
|
+
routes.md Every API route with method, path, params, and what it touches
|
|
69
|
+
schema.md Every database model with fields, types, keys, and relations
|
|
70
|
+
components.md Every UI component with its props
|
|
71
|
+
libs.md Every library export with function signatures
|
|
72
|
+
config.md Every env var (required vs default), config files, key deps
|
|
73
|
+
middleware.md Auth, rate limiting, CORS, validation, logging, error handlers
|
|
74
|
+
graph.md Which files import what and which break the most things if changed
|
|
75
|
+
report.html Interactive visual dashboard (with --html or --open)
|
|
38
76
|
```
|
|
39
77
|
|
|
40
|
-
##
|
|
78
|
+
## Routes
|
|
41
79
|
|
|
42
|
-
|
|
80
|
+
Not just paths. Methods, URL parameters, what each route touches (auth, database, cache, payments, AI, email, queues), and where the handler lives. Detects routes across 25+ frameworks automatically.
|
|
43
81
|
|
|
44
82
|
```markdown
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
> **Token savings:** this file is ~4,041 tokens. Without it, AI exploration would cost ~68,640 tokens.
|
|
52
|
-
> **Saves ~64,599 tokens per conversation.**
|
|
53
|
-
|
|
54
|
-
---
|
|
55
|
-
|
|
56
|
-
# Routes
|
|
83
|
+
- `POST` `/auth/login` [auth, db, email]
|
|
84
|
+
- `GET` `/api/projects/:id/analytics` params(id) [auth, db, cache]
|
|
85
|
+
- `POST` `/api/billing/checkout` [auth, db, payment]
|
|
86
|
+
- `QUERY` `getUsers` [db] # tRPC procedures
|
|
87
|
+
- `MUTATION` `createProject` [db, ai] # tRPC mutations
|
|
88
|
+
```
|
|
57
89
|
|
|
58
|
-
|
|
59
|
-
- `POST` `/verify` [auth, db, cache, email]
|
|
60
|
-
- `GET` `/me` [auth, db, cache, email]
|
|
61
|
-
- `GET` `/overview` [auth, db, queue, payment, ai]
|
|
62
|
-
- `GET` `/shield` [auth, db, queue, payment, ai]
|
|
63
|
-
- `GET` `/rescue` [auth, db, queue, payment, ai]
|
|
64
|
-
- `POST` `/rescue/:id/send-now` params(id) [auth, db, queue, payment, ai]
|
|
65
|
-
...
|
|
90
|
+
## Schema
|
|
66
91
|
|
|
67
|
-
|
|
92
|
+
Models, fields, types, primary keys, foreign keys, unique constraints, relations. Parsed directly from your ORM definitions. No need to open migration files.
|
|
68
93
|
|
|
94
|
+
```markdown
|
|
69
95
|
### users
|
|
70
96
|
- id: uuid (pk, default)
|
|
71
97
|
- email: text (unique, required)
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
- settings: jsonb (required, default)
|
|
98
|
+
- plan: text (required, default)
|
|
99
|
+
- stripeCustomerId: text (fk)
|
|
75
100
|
|
|
76
|
-
###
|
|
101
|
+
### projects
|
|
77
102
|
- id: uuid (pk, default)
|
|
78
103
|
- userId: uuid (fk)
|
|
79
|
-
|
|
104
|
+
- name: text (required)
|
|
105
|
+
- domain: text (unique)
|
|
106
|
+
- _relations_: userId -> users.id
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Dependency Graph
|
|
80
110
|
|
|
81
|
-
|
|
111
|
+
The files imported the most are the ones that break the most things when changed. codesight finds them and tells your AI to be careful.
|
|
82
112
|
|
|
113
|
+
```markdown
|
|
83
114
|
## Most Imported Files (change these carefully)
|
|
84
|
-
- `packages/shared/src/index.ts` — imported by **
|
|
85
|
-
- `apps/api/src/lib/db.ts` — imported by **
|
|
86
|
-
- `apps/api/src/lib/auth.ts` — imported by **
|
|
115
|
+
- `packages/shared/src/index.ts` — imported by **14** files
|
|
116
|
+
- `apps/api/src/lib/db.ts` — imported by **9** files
|
|
117
|
+
- `apps/api/src/lib/auth.ts` — imported by **7** files
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Environment Audit
|
|
121
|
+
|
|
122
|
+
Every env var across your codebase, flagged as required or has default, with the exact file where it is referenced.
|
|
123
|
+
|
|
124
|
+
```markdown
|
|
125
|
+
- `DATABASE_URL` **required** — apps/api/src/lib/db.ts
|
|
126
|
+
- `JWT_SECRET` **required** — apps/api/src/lib/auth.ts
|
|
127
|
+
- `PORT` (has default) — apps/api/src/index.ts
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Token Benchmark
|
|
131
|
+
|
|
132
|
+
See exactly where your token savings come from:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npx codesight --benchmark
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
Token Savings Breakdown:
|
|
140
|
+
┌──────────────────────────────────────────────────┐
|
|
141
|
+
│ What codesight found │ Exploration cost │
|
|
142
|
+
├──────────────────────────────┼────────────────────┤
|
|
143
|
+
│ 65 routes │ ~26,000 tokens │
|
|
144
|
+
│ 18 schema models │ ~ 5,400 tokens │
|
|
145
|
+
│ 16 components │ ~ 4,000 tokens │
|
|
146
|
+
│ 36 library files │ ~ 7,200 tokens │
|
|
147
|
+
│ 22 env vars │ ~ 2,200 tokens │
|
|
148
|
+
│ 92 files (search overhead) │ ~ 4,000 tokens │
|
|
149
|
+
├──────────────────────────────┼────────────────────┤
|
|
150
|
+
│ codesight output │ ~ 4,041 tokens │
|
|
151
|
+
│ SAVED PER CONVERSATION │ ~64,599 tokens │
|
|
152
|
+
└──────────────────────────────┴────────────────────┘
|
|
87
153
|
```
|
|
88
154
|
|
|
89
|
-
##
|
|
155
|
+
## Supported Stacks
|
|
90
156
|
|
|
91
157
|
| Category | Supported |
|
|
92
158
|
|---|---|
|
|
93
|
-
| **Routes** | Hono, Express, Fastify, Next.js (App + Pages), Koa, FastAPI, Flask, Django, Go (net/http, Gin, Fiber) |
|
|
94
|
-
| **Schema** | Drizzle, Prisma, TypeORM, SQLAlchemy |
|
|
95
|
-
| **Components** | React, Vue, Svelte (filters
|
|
96
|
-
| **Libraries** | TypeScript
|
|
97
|
-
| **Config** | Environment variables (required vs defaults), config files, notable dependencies |
|
|
159
|
+
| **Routes** | Hono, Express, Fastify, Next.js (App + Pages), Koa, NestJS, tRPC, Elysia, AdonisJS, SvelteKit, Remix, Nuxt, FastAPI, Flask, Django, Go (net/http, Gin, Fiber, Echo, Chi), Rails, Phoenix, Spring Boot, Actix, Axum, raw http.createServer |
|
|
160
|
+
| **Schema** | Drizzle, Prisma, TypeORM, Mongoose, Sequelize, SQLAlchemy, ActiveRecord, Ecto |
|
|
161
|
+
| **Components** | React, Vue, Svelte (auto-filters shadcn/ui and Radix primitives) |
|
|
162
|
+
| **Libraries** | TypeScript, JavaScript, Python, Go, Ruby, Elixir, Java, Kotlin, Rust (exports with function signatures) |
|
|
98
163
|
| **Middleware** | Auth, rate limiting, CORS, validation, logging, error handlers |
|
|
99
164
|
| **Dependencies** | Import graph with hot file detection (most imported = highest blast radius) |
|
|
100
|
-
| **Contracts** | URL params, request types, response types
|
|
101
|
-
| **Monorepos** | pnpm, npm, yarn workspaces
|
|
102
|
-
| **Languages** | TypeScript, JavaScript, Python, Go |
|
|
165
|
+
| **Contracts** | URL params, request types, response types from route handlers |
|
|
166
|
+
| **Monorepos** | pnpm, npm, yarn workspaces (cross-workspace detection) |
|
|
167
|
+
| **Languages** | TypeScript, JavaScript, Python, Go, Ruby, Elixir, Java, Kotlin, Rust, PHP |
|
|
103
168
|
|
|
104
|
-
##
|
|
169
|
+
## AI Config Generation
|
|
105
170
|
|
|
106
171
|
```bash
|
|
107
172
|
npx codesight --init
|
|
108
173
|
```
|
|
109
174
|
|
|
110
|
-
Generates instruction files for every major AI coding tool:
|
|
175
|
+
Generates ready-to-use instruction files for every major AI coding tool at once:
|
|
111
176
|
|
|
112
177
|
| File | Tool |
|
|
113
178
|
|---|---|
|
|
@@ -115,25 +180,9 @@ Generates instruction files for every major AI coding tool:
|
|
|
115
180
|
| `.cursorrules` | Cursor |
|
|
116
181
|
| `.github/copilot-instructions.md` | GitHub Copilot |
|
|
117
182
|
| `codex.md` | OpenAI Codex CLI |
|
|
118
|
-
| `AGENTS.md` | OpenAI Codex |
|
|
119
|
-
|
|
120
|
-
Each file includes your project's stack, key architecture facts, hot files, and required env vars so your AI assistant knows the project from the first message.
|
|
183
|
+
| `AGENTS.md` | OpenAI Codex agents |
|
|
121
184
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
```bash
|
|
125
|
-
npx codesight --open
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Generates a visual dashboard with:
|
|
129
|
-
- Token savings hero metric
|
|
130
|
-
- Route table with methods, contracts, and tags
|
|
131
|
-
- Schema cards with fields and relations
|
|
132
|
-
- Dependency hot files with impact bars
|
|
133
|
-
- Environment variables (required vs defaults)
|
|
134
|
-
- Full middleware map
|
|
135
|
-
|
|
136
|
-
Screenshots of this report are what go viral on Twitter.
|
|
185
|
+
Each file is pre-filled with your project's stack, architecture, high-impact files, and required env vars. Your AI reads it on startup and starts with full context from the first message.
|
|
137
186
|
|
|
138
187
|
## MCP Server
|
|
139
188
|
|
|
@@ -141,7 +190,7 @@ Screenshots of this report are what go viral on Twitter.
|
|
|
141
190
|
npx codesight --mcp
|
|
142
191
|
```
|
|
143
192
|
|
|
144
|
-
Runs as a Model Context Protocol server
|
|
193
|
+
Runs as a Model Context Protocol server. Claude Code and Cursor call it directly to get project context on demand.
|
|
145
194
|
|
|
146
195
|
```json
|
|
147
196
|
{
|
|
@@ -154,23 +203,50 @@ Runs as a Model Context Protocol server that Claude Code and Cursor can connect
|
|
|
154
203
|
}
|
|
155
204
|
```
|
|
156
205
|
|
|
157
|
-
Your AI
|
|
206
|
+
Exposes one tool: `codesight_scan`. Your AI calls it whenever it needs to understand the project.
|
|
158
207
|
|
|
159
|
-
##
|
|
208
|
+
## Visual Report
|
|
160
209
|
|
|
161
210
|
```bash
|
|
162
|
-
npx codesight --
|
|
211
|
+
npx codesight --open
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Opens an interactive HTML dashboard in your browser. Routes table with method badges and tags. Schema cards with fields and relations. Dependency hot files with impact bars. Env var audit. Token savings breakdown. Useful for onboarding or just seeing your project from above.
|
|
215
|
+
|
|
216
|
+
## GitHub Action
|
|
217
|
+
|
|
218
|
+
Add to your CI pipeline to keep context fresh on every push:
|
|
219
|
+
|
|
220
|
+
```yaml
|
|
221
|
+
name: codesight
|
|
222
|
+
on: [push]
|
|
223
|
+
jobs:
|
|
224
|
+
scan:
|
|
225
|
+
runs-on: ubuntu-latest
|
|
226
|
+
steps:
|
|
227
|
+
- uses: actions/checkout@v4
|
|
228
|
+
- run: npx codesight
|
|
229
|
+
- uses: actions/upload-artifact@v4
|
|
230
|
+
with:
|
|
231
|
+
name: codesight
|
|
232
|
+
path: .codesight/
|
|
163
233
|
```
|
|
164
234
|
|
|
165
|
-
|
|
235
|
+
## Watch Mode and Git Hook
|
|
166
236
|
|
|
167
|
-
|
|
237
|
+
**Watch mode** re-scans when files change:
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
npx codesight --watch
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Git hook** regenerates context on every commit:
|
|
168
244
|
|
|
169
245
|
```bash
|
|
170
246
|
npx codesight --hook
|
|
171
247
|
```
|
|
172
248
|
|
|
173
|
-
|
|
249
|
+
Context stays fresh without thinking about it.
|
|
174
250
|
|
|
175
251
|
## All Options
|
|
176
252
|
|
|
@@ -178,36 +254,56 @@ Installs a git pre-commit hook that regenerates context on every commit. Context
|
|
|
178
254
|
npx codesight # Scan current directory
|
|
179
255
|
npx codesight ./my-project # Scan specific directory
|
|
180
256
|
npx codesight --init # Generate AI config files
|
|
181
|
-
npx codesight --open # Open
|
|
182
|
-
npx codesight --html # Generate HTML report
|
|
257
|
+
npx codesight --open # Open visual HTML report
|
|
258
|
+
npx codesight --html # Generate HTML report without opening
|
|
183
259
|
npx codesight --mcp # Start MCP server
|
|
184
260
|
npx codesight --watch # Watch mode
|
|
185
261
|
npx codesight --hook # Install git pre-commit hook
|
|
186
|
-
npx codesight --
|
|
262
|
+
npx codesight --benchmark # Detailed token savings breakdown
|
|
263
|
+
npx codesight --json # Output as JSON
|
|
187
264
|
npx codesight -o .ai-context # Custom output directory
|
|
188
265
|
npx codesight -d 5 # Limit directory depth
|
|
189
266
|
```
|
|
190
267
|
|
|
191
|
-
##
|
|
268
|
+
## How It Compares
|
|
192
269
|
|
|
193
|
-
|
|
270
|
+
Most AI context tools dump your entire codebase into one file. codesight takes a different approach: it **parses** your code to extract structured information.
|
|
194
271
|
|
|
195
|
-
| |
|
|
272
|
+
| | codesight | File concatenation tools |
|
|
196
273
|
|---|---|---|
|
|
197
|
-
| **
|
|
198
|
-
| **
|
|
199
|
-
| **
|
|
200
|
-
| **
|
|
201
|
-
| **
|
|
202
|
-
| **
|
|
203
|
-
| **
|
|
204
|
-
| **
|
|
205
|
-
| **Runtime deps** | 26 | **Zero** |
|
|
274
|
+
| **Output** | Structured routes, schema, components, deps | Raw file contents |
|
|
275
|
+
| **Token cost** | ~3,000-5,000 tokens | 50,000-500,000+ tokens |
|
|
276
|
+
| **Route detection** | 25+ frameworks auto-detected | None |
|
|
277
|
+
| **Schema parsing** | ORM-aware with relations | None |
|
|
278
|
+
| **Dependency graph** | Hot file detection | None |
|
|
279
|
+
| **AI config generation** | CLAUDE.md, .cursorrules, etc. | None |
|
|
280
|
+
| **MCP server** | Built-in | Varies |
|
|
281
|
+
| **Dependencies** | Zero | Varies |
|
|
206
282
|
|
|
207
|
-
##
|
|
283
|
+
## Contributing
|
|
208
284
|
|
|
209
|
-
|
|
285
|
+
```bash
|
|
286
|
+
git clone https://github.com/Houseofmvps/codesight.git
|
|
287
|
+
cd codesight
|
|
288
|
+
pnpm install
|
|
289
|
+
pnpm dev # Run locally
|
|
290
|
+
pnpm build # Compile TypeScript
|
|
291
|
+
pnpm test # Run 27 tests
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
PRs welcome. Open an issue first for large changes.
|
|
210
295
|
|
|
211
296
|
## License
|
|
212
297
|
|
|
213
298
|
MIT
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
<div align="center">
|
|
303
|
+
|
|
304
|
+
If codesight saves you tokens, [star it on GitHub](https://github.com/Houseofmvps/codesight) so others find it too.
|
|
305
|
+
|
|
306
|
+
[](https://github.com/Houseofmvps/codesight/stargazers)
|
|
307
|
+
[](https://github.com/sponsors/Houseofmvps)
|
|
308
|
+
|
|
309
|
+
</div>
|
|
@@ -36,9 +36,16 @@ export async function enrichRouteContracts(routes, project) {
|
|
|
36
36
|
case "express":
|
|
37
37
|
case "fastify":
|
|
38
38
|
case "koa":
|
|
39
|
+
case "nestjs":
|
|
40
|
+
case "elysia":
|
|
41
|
+
case "adonis":
|
|
42
|
+
case "raw-http":
|
|
39
43
|
enrichTSRoute(route, content);
|
|
40
44
|
break;
|
|
41
45
|
case "next-app":
|
|
46
|
+
case "sveltekit":
|
|
47
|
+
case "remix":
|
|
48
|
+
case "nuxt":
|
|
42
49
|
enrichNextRoute(route, content);
|
|
43
50
|
break;
|
|
44
51
|
case "fastapi":
|
package/dist/detectors/graph.js
CHANGED
|
@@ -3,7 +3,15 @@ import { readFileSafe } from "../scanner.js";
|
|
|
3
3
|
export async function detectDependencyGraph(files, project) {
|
|
4
4
|
const edges = [];
|
|
5
5
|
const importCount = new Map();
|
|
6
|
-
const codeFiles = files.filter((f) => f.match(/\.(ts|tsx|js|jsx|mjs|py|go)$/));
|
|
6
|
+
const codeFiles = files.filter((f) => f.match(/\.(ts|tsx|js|jsx|mjs|py|go|rb|ex|exs|java|kt|rs|php)$/));
|
|
7
|
+
// Build a lookup map for faster resolution: relative path -> true
|
|
8
|
+
const relPathSet = new Set();
|
|
9
|
+
const relPaths = [];
|
|
10
|
+
for (const file of files) {
|
|
11
|
+
const rel = relative(project.root, file);
|
|
12
|
+
relPathSet.add(rel);
|
|
13
|
+
relPaths.push(rel);
|
|
14
|
+
}
|
|
7
15
|
for (const file of codeFiles) {
|
|
8
16
|
const content = await readFileSafe(file);
|
|
9
17
|
if (!content)
|
|
@@ -16,8 +24,20 @@ export async function detectDependencyGraph(files, project) {
|
|
|
16
24
|
else if (ext === ".go") {
|
|
17
25
|
extractGoImports(content, rel, edges, importCount);
|
|
18
26
|
}
|
|
27
|
+
else if (ext === ".rb") {
|
|
28
|
+
extractRubyImports(content, rel, edges, importCount);
|
|
29
|
+
}
|
|
30
|
+
else if (ext === ".ex" || ext === ".exs") {
|
|
31
|
+
extractElixirImports(content, rel, edges, importCount);
|
|
32
|
+
}
|
|
33
|
+
else if (ext === ".java" || ext === ".kt") {
|
|
34
|
+
extractJavaImports(content, rel, edges, importCount, relPaths);
|
|
35
|
+
}
|
|
36
|
+
else if (ext === ".rs") {
|
|
37
|
+
extractRustImports(content, rel, edges, importCount);
|
|
38
|
+
}
|
|
19
39
|
else {
|
|
20
|
-
extractTSImports(content, rel, file, project,
|
|
40
|
+
extractTSImports(content, rel, file, project, relPathSet, edges, importCount);
|
|
21
41
|
}
|
|
22
42
|
}
|
|
23
43
|
// Sort by most imported
|
|
@@ -27,7 +47,7 @@ export async function detectDependencyGraph(files, project) {
|
|
|
27
47
|
.slice(0, 20);
|
|
28
48
|
return { edges, hotFiles };
|
|
29
49
|
}
|
|
30
|
-
function extractTSImports(content, rel, absPath, project,
|
|
50
|
+
function extractTSImports(content, rel, absPath, project, relPathSet, edges, importCount) {
|
|
31
51
|
// Match: import ... from "./path" or import("./path") or require("./path")
|
|
32
52
|
const patterns = [
|
|
33
53
|
/(?:import|export)\s+.*?from\s+['"]([^'"]+)['"]/g,
|
|
@@ -50,8 +70,10 @@ function extractTSImports(content, rel, absPath, project, allFiles, edges, impor
|
|
|
50
70
|
const dir = dirname(absPath);
|
|
51
71
|
resolvedPath = relative(project.root, resolve(dir, importPath));
|
|
52
72
|
}
|
|
53
|
-
// Strip extension
|
|
54
|
-
|
|
73
|
+
// Strip .js/.mjs extension that TypeScript adds for ESM compatibility
|
|
74
|
+
// e.g., import { foo } from "./bar.js" actually refers to ./bar.ts
|
|
75
|
+
const stripped = resolvedPath.replace(/\.(js|mjs|cjs)$/, "");
|
|
76
|
+
const normalized = normalizeImportPath(stripped, relPathSet);
|
|
55
77
|
if (normalized && normalized !== rel) {
|
|
56
78
|
edges.push({ from: rel, to: normalized });
|
|
57
79
|
importCount.set(normalized, (importCount.get(normalized) || 0) + 1);
|
|
@@ -70,11 +92,9 @@ function extractPythonImports(content, rel, edges, importCount) {
|
|
|
70
92
|
}
|
|
71
93
|
}
|
|
72
94
|
function extractGoImports(content, rel, edges, importCount) {
|
|
73
|
-
// Go doesn't have relative imports in the same way, but we can track internal package imports
|
|
74
95
|
const importBlock = content.match(/import\s*\(([\s\S]*?)\)/);
|
|
75
96
|
if (!importBlock)
|
|
76
97
|
return;
|
|
77
|
-
// Look for internal package paths (not standard library)
|
|
78
98
|
const lines = importBlock[1].split("\n");
|
|
79
99
|
for (const line of lines) {
|
|
80
100
|
const pathMatch = line.match(/["']([^"']+)["']/);
|
|
@@ -85,29 +105,73 @@ function extractGoImports(content, rel, edges, importCount) {
|
|
|
85
105
|
}
|
|
86
106
|
}
|
|
87
107
|
}
|
|
88
|
-
function
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
108
|
+
function extractRubyImports(content, rel, edges, importCount) {
|
|
109
|
+
// require_relative "./path"
|
|
110
|
+
const pattern = /require_relative\s+['"]([^'"]+)['"]/g;
|
|
111
|
+
let match;
|
|
112
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
113
|
+
const target = match[1].replace(/^\.\//, "") + ".rb";
|
|
114
|
+
edges.push({ from: rel, to: target });
|
|
115
|
+
importCount.set(target, (importCount.get(target) || 0) + 1);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function extractElixirImports(content, rel, edges, importCount) {
|
|
119
|
+
// alias MyApp.Accounts.User
|
|
120
|
+
const pattern = /(?:alias|import|use)\s+([\w.]+)/g;
|
|
121
|
+
let match;
|
|
122
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
123
|
+
const mod = match[1];
|
|
124
|
+
// Convert module path to potential file: MyApp.Accounts.User -> lib/my_app/accounts/user.ex
|
|
125
|
+
if (mod.includes(".") && !mod.startsWith("Ecto") && !mod.startsWith("Phoenix") && !mod.startsWith("Plug")) {
|
|
126
|
+
const target = "lib/" + mod.split(".").map(s => s.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "")).join("/") + ".ex";
|
|
127
|
+
edges.push({ from: rel, to: target });
|
|
128
|
+
importCount.set(target, (importCount.get(target) || 0) + 1);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function extractJavaImports(content, rel, edges, importCount, relPaths) {
|
|
133
|
+
// import com.myapp.service.UserService;
|
|
134
|
+
const pattern = /^import\s+([\w.]+);/gm;
|
|
135
|
+
let match;
|
|
136
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
137
|
+
const imp = match[1];
|
|
138
|
+
// Skip standard library and common third-party
|
|
139
|
+
if (imp.startsWith("java.") || imp.startsWith("javax.") || imp.startsWith("org.springframework") || imp.startsWith("org.apache"))
|
|
140
|
+
continue;
|
|
141
|
+
// Convert to path pattern: com.myapp.service.UserService -> UserService
|
|
142
|
+
const className = imp.split(".").pop();
|
|
143
|
+
const found = relPaths.find(p => p.endsWith(`/${className}.java`) || p.endsWith(`/${className}.kt`));
|
|
144
|
+
if (found && found !== rel) {
|
|
145
|
+
edges.push({ from: rel, to: found });
|
|
146
|
+
importCount.set(found, (importCount.get(found) || 0) + 1);
|
|
147
|
+
}
|
|
94
148
|
}
|
|
149
|
+
}
|
|
150
|
+
function extractRustImports(content, rel, edges, importCount) {
|
|
151
|
+
// mod my_module; or use crate::my_module::something;
|
|
152
|
+
const modPattern = /^mod\s+(\w+)\s*;/gm;
|
|
153
|
+
let match;
|
|
154
|
+
while ((match = modPattern.exec(content)) !== null) {
|
|
155
|
+
const dir = dirname(rel);
|
|
156
|
+
const target = dir === "." ? `${match[1]}.rs` : `${dir}/${match[1]}.rs`;
|
|
157
|
+
edges.push({ from: rel, to: target });
|
|
158
|
+
importCount.set(target, (importCount.get(target) || 0) + 1);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function normalizeImportPath(importPath, relPathSet) {
|
|
162
|
+
// Try exact match first
|
|
163
|
+
if (relPathSet.has(importPath))
|
|
164
|
+
return importPath;
|
|
95
165
|
// Try with extensions
|
|
96
166
|
const extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs"];
|
|
97
167
|
for (const ext of extensions) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (rel === importPath + ext)
|
|
101
|
-
return rel;
|
|
102
|
-
}
|
|
168
|
+
if (relPathSet.has(importPath + ext))
|
|
169
|
+
return importPath + ext;
|
|
103
170
|
}
|
|
104
171
|
// Try index files
|
|
105
172
|
for (const ext of extensions) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (rel === importPath + "/index" + ext)
|
|
109
|
-
return rel;
|
|
110
|
-
}
|
|
173
|
+
if (relPathSet.has(importPath + "/index" + ext))
|
|
174
|
+
return importPath + "/index" + ext;
|
|
111
175
|
}
|
|
112
176
|
return null;
|
|
113
177
|
}
|