@codexa/cli 8.5.0 → 8.6.9

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/detectors/node.ts CHANGED
@@ -1,494 +1,494 @@
1
- /**
2
- * Node.js/JavaScript/TypeScript Ecosystem Detector
3
- *
4
- * Detects JavaScript and TypeScript projects including:
5
- * - Runtime: Node.js, Bun, Deno
6
- * - Frontend: React, Next.js, Vue, Svelte, Angular
7
- * - Backend: Express, Fastify, Hono, Elysia, NestJS
8
- * - ORMs: Prisma, Drizzle, TypeORM, Sequelize
9
- * - Testing: Jest, Vitest, Playwright, Cypress
10
- */
11
-
12
- import { join } from "path";
13
- import {
14
- registerDetector,
15
- Detector,
16
- DetectorResult,
17
- DetectedTechnology,
18
- fileExists,
19
- dirExists,
20
- findFiles,
21
- readJson,
22
- readText,
23
- } from "./index";
24
-
25
- interface PackageJson {
26
- name?: string;
27
- version?: string;
28
- type?: "module" | "commonjs";
29
- dependencies?: Record<string, string>;
30
- devDependencies?: Record<string, string>;
31
- peerDependencies?: Record<string, string>;
32
- scripts?: Record<string, string>;
33
- engines?: Record<string, string>;
34
- workspaces?: string[] | { packages: string[] };
35
- }
36
-
37
- const nodeDetector: Detector = {
38
- name: "node",
39
- ecosystem: "node",
40
- priority: 90,
41
- markers: [
42
- { type: "file", pattern: "package.json", weight: 1.0 },
43
- { type: "file", pattern: "package-lock.json", weight: 0.9 },
44
- { type: "file", pattern: "yarn.lock", weight: 0.9 },
45
- { type: "file", pattern: "pnpm-lock.yaml", weight: 0.9 },
46
- { type: "file", pattern: "bun.lockb", weight: 0.9 },
47
- { type: "file", pattern: "deno.json", weight: 1.0 },
48
- { type: "file", pattern: "deno.jsonc", weight: 1.0 },
49
- { type: "file", pattern: "tsconfig.json", weight: 0.7 },
50
- { type: "file", pattern: "jsconfig.json", weight: 0.7 },
51
- { type: "directory", pattern: "node_modules", weight: 0.5 },
52
- ],
53
-
54
- async detect(cwd: string): Promise<DetectorResult | null> {
55
- const technologies: DetectedTechnology[] = [];
56
- const structure: Record<string, string> = {};
57
- const configFiles: string[] = [];
58
-
59
- // Check for Deno first (different ecosystem)
60
- if (fileExists(join(cwd, "deno.json")) || fileExists(join(cwd, "deno.jsonc"))) {
61
- technologies.push({
62
- name: "Deno",
63
- confidence: 1.0,
64
- source: "deno.json",
65
- category: "runtime",
66
- });
67
- configFiles.push("deno.json");
68
- }
69
-
70
- // Check for package.json
71
- const pkgPath = join(cwd, "package.json");
72
- if (!fileExists(pkgPath)) {
73
- // No package.json - might still be JS project
74
- const jsFiles = findFiles(cwd, "**/*.{js,ts,jsx,tsx}");
75
- if (jsFiles.length === 0) {
76
- return technologies.length > 0
77
- ? { ecosystem: "node", technologies, structure, configFiles }
78
- : null;
79
- }
80
-
81
- // Has JS files but no package.json
82
- technologies.push({
83
- name: "JavaScript",
84
- confidence: 0.6,
85
- source: "*.js files",
86
- category: "runtime",
87
- });
88
-
89
- return { ecosystem: "node", technologies, structure, configFiles };
90
- }
91
-
92
- configFiles.push("package.json");
93
- const pkg: PackageJson | null = readJson(pkgPath);
94
- if (!pkg) return null;
95
-
96
- const allDeps = {
97
- ...pkg.dependencies,
98
- ...pkg.devDependencies,
99
- ...pkg.peerDependencies,
100
- };
101
-
102
- // Detect package manager from lock files
103
- if (fileExists(join(cwd, "bun.lockb"))) {
104
- technologies.push({
105
- name: "Bun",
106
- confidence: 1.0,
107
- source: "bun.lockb",
108
- category: "runtime",
109
- });
110
- configFiles.push("bun.lockb");
111
- } else if (fileExists(join(cwd, "pnpm-lock.yaml"))) {
112
- technologies.push({
113
- name: "pnpm",
114
- confidence: 0.9,
115
- source: "pnpm-lock.yaml",
116
- category: "build",
117
- });
118
- configFiles.push("pnpm-lock.yaml");
119
- } else if (fileExists(join(cwd, "yarn.lock"))) {
120
- technologies.push({
121
- name: "Yarn",
122
- confidence: 0.9,
123
- source: "yarn.lock",
124
- category: "build",
125
- });
126
- configFiles.push("yarn.lock");
127
- } else if (fileExists(join(cwd, "package-lock.json"))) {
128
- technologies.push({
129
- name: "npm",
130
- confidence: 0.9,
131
- source: "package-lock.json",
132
- category: "build",
133
- });
134
- configFiles.push("package-lock.json");
135
- }
136
-
137
- // TypeScript detection
138
- if (fileExists(join(cwd, "tsconfig.json")) || allDeps["typescript"]) {
139
- technologies.push({
140
- name: "TypeScript",
141
- version: allDeps["typescript"],
142
- confidence: 1.0,
143
- source: "tsconfig.json or dependency",
144
- category: "runtime",
145
- });
146
- configFiles.push("tsconfig.json");
147
- }
148
-
149
- // Runtime from engines
150
- if (pkg.engines?.node) {
151
- technologies.push({
152
- name: "Node.js",
153
- version: pkg.engines.node,
154
- confidence: 0.95,
155
- source: "package.json engines",
156
- category: "runtime",
157
- });
158
- } else if (pkg.engines?.bun) {
159
- technologies.push({
160
- name: "Bun",
161
- version: pkg.engines.bun,
162
- confidence: 0.95,
163
- source: "package.json engines",
164
- category: "runtime",
165
- });
166
- }
167
-
168
- // Frontend Framework Detection (priority order matters)
169
- const frontendFrameworks = [
170
- { dep: "next", name: "Next.js", confidence: 1.0 },
171
- { dep: "nuxt", name: "Nuxt", confidence: 1.0 },
172
- { dep: "@remix-run/react", name: "Remix", confidence: 1.0 },
173
- { dep: "gatsby", name: "Gatsby", confidence: 1.0 },
174
- { dep: "@angular/core", name: "Angular", confidence: 1.0 },
175
- { dep: "vue", name: "Vue", confidence: 0.95 },
176
- { dep: "svelte", name: "Svelte", confidence: 0.95 },
177
- { dep: "@solidjs/core", name: "SolidJS", confidence: 0.95 },
178
- { dep: "solid-js", name: "SolidJS", confidence: 0.95 },
179
- { dep: "preact", name: "Preact", confidence: 0.95 },
180
- { dep: "react", name: "React", confidence: 0.9 },
181
- { dep: "astro", name: "Astro", confidence: 1.0 },
182
- { dep: "qwik", name: "Qwik", confidence: 1.0 },
183
- ];
184
-
185
- for (const fw of frontendFrameworks) {
186
- if (allDeps[fw.dep]) {
187
- technologies.push({
188
- name: fw.name,
189
- version: allDeps[fw.dep],
190
- confidence: fw.confidence,
191
- source: `Dependency: ${fw.dep}`,
192
- category: "frontend",
193
- });
194
- break; // Only detect primary frontend framework
195
- }
196
- }
197
-
198
- // Backend Framework Detection
199
- const backendFrameworks = [
200
- { dep: "@nestjs/core", name: "NestJS", confidence: 1.0 },
201
- { dep: "hono", name: "Hono", confidence: 1.0 },
202
- { dep: "elysia", name: "Elysia", confidence: 1.0 },
203
- { dep: "fastify", name: "Fastify", confidence: 0.95 },
204
- { dep: "koa", name: "Koa", confidence: 0.95 },
205
- { dep: "express", name: "Express", confidence: 0.9 },
206
- { dep: "@hapi/hapi", name: "Hapi", confidence: 0.95 },
207
- { dep: "trpc", name: "tRPC", confidence: 0.9 },
208
- { dep: "@trpc/server", name: "tRPC", confidence: 0.9 },
209
- ];
210
-
211
- for (const fw of backendFrameworks) {
212
- if (allDeps[fw.dep]) {
213
- technologies.push({
214
- name: fw.name,
215
- version: allDeps[fw.dep],
216
- confidence: fw.confidence,
217
- source: `Dependency: ${fw.dep}`,
218
- category: "backend",
219
- });
220
- }
221
- }
222
-
223
- // ORM Detection
224
- const orms = [
225
- { dep: "drizzle-orm", name: "Drizzle", confidence: 1.0 },
226
- { dep: "prisma", name: "Prisma", confidence: 1.0 },
227
- { dep: "@prisma/client", name: "Prisma", confidence: 1.0 },
228
- { dep: "typeorm", name: "TypeORM", confidence: 1.0 },
229
- { dep: "sequelize", name: "Sequelize", confidence: 1.0 },
230
- { dep: "kysely", name: "Kysely", confidence: 1.0 },
231
- { dep: "mongoose", name: "Mongoose", confidence: 1.0 },
232
- { dep: "knex", name: "Knex", confidence: 0.9 },
233
- { dep: "mikro-orm", name: "MikroORM", confidence: 1.0 },
234
- { dep: "@mikro-orm/core", name: "MikroORM", confidence: 1.0 },
235
- ];
236
-
237
- const detectedOrms = new Set<string>();
238
- for (const orm of orms) {
239
- if (allDeps[orm.dep] && !detectedOrms.has(orm.name)) {
240
- technologies.push({
241
- name: orm.name,
242
- version: allDeps[orm.dep],
243
- confidence: orm.confidence,
244
- source: `Dependency: ${orm.dep}`,
245
- category: "orm",
246
- });
247
- detectedOrms.add(orm.name);
248
- }
249
- }
250
-
251
- // Database Detection
252
- const databases = [
253
- { dep: "@supabase/supabase-js", name: "Supabase", confidence: 1.0 },
254
- { dep: "firebase", name: "Firebase", confidence: 1.0 },
255
- { dep: "@planetscale/database", name: "PlanetScale", confidence: 1.0 },
256
- { dep: "pg", name: "PostgreSQL", confidence: 0.9 },
257
- { dep: "postgres", name: "PostgreSQL", confidence: 0.9 },
258
- { dep: "@neondatabase/serverless", name: "Neon", confidence: 1.0 },
259
- { dep: "mysql2", name: "MySQL", confidence: 0.9 },
260
- { dep: "mysql", name: "MySQL", confidence: 0.9 },
261
- { dep: "better-sqlite3", name: "SQLite", confidence: 0.9 },
262
- { dep: "sql.js", name: "SQLite", confidence: 0.9 },
263
- { dep: "mongodb", name: "MongoDB", confidence: 0.9 },
264
- { dep: "redis", name: "Redis", confidence: 0.9 },
265
- { dep: "ioredis", name: "Redis", confidence: 0.9 },
266
- { dep: "@upstash/redis", name: "Upstash Redis", confidence: 1.0 },
267
- { dep: "@libsql/client", name: "Turso", confidence: 1.0 },
268
- ];
269
-
270
- const detectedDbs = new Set<string>();
271
- for (const db of databases) {
272
- if (allDeps[db.dep] && !detectedDbs.has(db.name)) {
273
- technologies.push({
274
- name: db.name,
275
- version: allDeps[db.dep],
276
- confidence: db.confidence,
277
- source: `Dependency: ${db.dep}`,
278
- category: "database",
279
- });
280
- detectedDbs.add(db.name);
281
- }
282
- }
283
-
284
- // Styling Detection
285
- const stylingTools = [
286
- { dep: "tailwindcss", name: "Tailwind CSS", confidence: 1.0 },
287
- { dep: "styled-components", name: "Styled Components", confidence: 1.0 },
288
- { dep: "@emotion/react", name: "Emotion", confidence: 1.0 },
289
- { dep: "@emotion/styled", name: "Emotion", confidence: 1.0 },
290
- { dep: "sass", name: "Sass", confidence: 0.9 },
291
- { dep: "less", name: "Less", confidence: 0.9 },
292
- { dep: "@vanilla-extract/css", name: "Vanilla Extract", confidence: 1.0 },
293
- { dep: "linaria", name: "Linaria", confidence: 1.0 },
294
- { dep: "@stitches/react", name: "Stitches", confidence: 1.0 },
295
- { dep: "unocss", name: "UnoCSS", confidence: 1.0 },
296
- { dep: "@master/css", name: "Master CSS", confidence: 1.0 },
297
- ];
298
-
299
- for (const style of stylingTools) {
300
- if (allDeps[style.dep]) {
301
- technologies.push({
302
- name: style.name,
303
- version: allDeps[style.dep],
304
- confidence: style.confidence,
305
- source: `Dependency: ${style.dep}`,
306
- category: "styling",
307
- });
308
- break;
309
- }
310
- }
311
-
312
- // UI Component Libraries
313
- const uiLibs = [
314
- { dep: "@radix-ui/react-dialog", name: "Radix UI", confidence: 0.9 },
315
- { dep: "@headlessui/react", name: "Headless UI", confidence: 0.9 },
316
- { dep: "@chakra-ui/react", name: "Chakra UI", confidence: 1.0 },
317
- { dep: "@mantine/core", name: "Mantine", confidence: 1.0 },
318
- { dep: "@mui/material", name: "Material UI", confidence: 1.0 },
319
- { dep: "antd", name: "Ant Design", confidence: 1.0 },
320
- { dep: "@nextui-org/react", name: "NextUI", confidence: 1.0 },
321
- { dep: "shadcn-ui", name: "shadcn/ui", confidence: 1.0 },
322
- ];
323
-
324
- for (const ui of uiLibs) {
325
- if (allDeps[ui.dep]) {
326
- technologies.push({
327
- name: ui.name,
328
- version: allDeps[ui.dep],
329
- confidence: ui.confidence,
330
- source: `Dependency: ${ui.dep}`,
331
- category: "styling",
332
- });
333
- }
334
- }
335
-
336
- // Auth Detection
337
- const authProviders = [
338
- { dep: "next-auth", name: "NextAuth.js", confidence: 1.0 },
339
- { dep: "@auth/core", name: "Auth.js", confidence: 1.0 },
340
- { dep: "@clerk/nextjs", name: "Clerk", confidence: 1.0 },
341
- { dep: "@clerk/clerk-react", name: "Clerk", confidence: 1.0 },
342
- { dep: "@supabase/auth-helpers-nextjs", name: "Supabase Auth", confidence: 1.0 },
343
- { dep: "@supabase/auth-helpers-react", name: "Supabase Auth", confidence: 1.0 },
344
- { dep: "@auth0/auth0-react", name: "Auth0", confidence: 1.0 },
345
- { dep: "@okta/okta-react", name: "Okta", confidence: 1.0 },
346
- { dep: "passport", name: "Passport.js", confidence: 0.9 },
347
- { dep: "@lucia-auth/core", name: "Lucia", confidence: 1.0 },
348
- { dep: "lucia", name: "Lucia", confidence: 1.0 },
349
- { dep: "better-auth", name: "Better Auth", confidence: 1.0 },
350
- ];
351
-
352
- for (const auth of authProviders) {
353
- if (allDeps[auth.dep]) {
354
- technologies.push({
355
- name: auth.name,
356
- version: allDeps[auth.dep],
357
- confidence: auth.confidence,
358
- source: `Dependency: ${auth.dep}`,
359
- category: "auth",
360
- });
361
- break;
362
- }
363
- }
364
-
365
- // Testing Detection
366
- const testingTools = [
367
- { dep: "vitest", name: "Vitest", confidence: 1.0 },
368
- { dep: "jest", name: "Jest", confidence: 1.0 },
369
- { dep: "@testing-library/react", name: "Testing Library", confidence: 0.9 },
370
- { dep: "playwright", name: "Playwright", confidence: 1.0 },
371
- { dep: "@playwright/test", name: "Playwright", confidence: 1.0 },
372
- { dep: "cypress", name: "Cypress", confidence: 1.0 },
373
- { dep: "mocha", name: "Mocha", confidence: 0.9 },
374
- { dep: "ava", name: "AVA", confidence: 0.9 },
375
- { dep: "tap", name: "Node TAP", confidence: 0.9 },
376
- { dep: "bun:test", name: "Bun Test", confidence: 0.9 },
377
- ];
378
-
379
- for (const test of testingTools) {
380
- if (allDeps[test.dep]) {
381
- technologies.push({
382
- name: test.name,
383
- version: allDeps[test.dep],
384
- confidence: test.confidence,
385
- source: `Dependency: ${test.dep}`,
386
- category: "testing",
387
- });
388
- }
389
- }
390
-
391
- // Build Tools
392
- const buildTools = [
393
- { dep: "vite", name: "Vite", confidence: 1.0 },
394
- { dep: "webpack", name: "Webpack", confidence: 0.9 },
395
- { dep: "esbuild", name: "esbuild", confidence: 0.9 },
396
- { dep: "rollup", name: "Rollup", confidence: 0.9 },
397
- { dep: "parcel", name: "Parcel", confidence: 0.9 },
398
- { dep: "turbo", name: "Turborepo", confidence: 1.0 },
399
- { dep: "nx", name: "Nx", confidence: 1.0 },
400
- { dep: "lerna", name: "Lerna", confidence: 0.9 },
401
- ];
402
-
403
- for (const tool of buildTools) {
404
- if (allDeps[tool.dep]) {
405
- technologies.push({
406
- name: tool.name,
407
- version: allDeps[tool.dep],
408
- confidence: tool.confidence,
409
- source: `Dependency: ${tool.dep}`,
410
- category: "build",
411
- });
412
- }
413
- }
414
-
415
- // Structure detection
416
- const structurePaths = [
417
- { key: "components", paths: ["src/components", "components", "app/components", "lib/components"] },
418
- { key: "services", paths: ["src/services", "services", "src/lib/services", "lib/services"] },
419
- { key: "hooks", paths: ["src/hooks", "hooks", "src/lib/hooks", "lib/hooks"] },
420
- { key: "utils", paths: ["src/utils", "utils", "src/lib/utils", "lib/utils", "src/helpers", "helpers"] },
421
- { key: "types", paths: ["src/types", "types", "src/@types", "@types"] },
422
- { key: "api", paths: ["src/app/api", "pages/api", "src/api", "api", "app/api"] },
423
- { key: "schema", paths: ["src/db/schema", "src/schema", "prisma/schema", "drizzle", "src/database"] },
424
- { key: "stores", paths: ["src/stores", "stores", "src/store", "store"] },
425
- { key: "context", paths: ["src/context", "context", "src/contexts", "contexts"] },
426
- { key: "middleware", paths: ["src/middleware", "middleware"] },
427
- { key: "lib", paths: ["src/lib", "lib"] },
428
- ];
429
-
430
- for (const { key, paths } of structurePaths) {
431
- for (const p of paths) {
432
- if (dirExists(join(cwd, p))) {
433
- structure[key] = p;
434
- break;
435
- }
436
- }
437
- }
438
-
439
- // Detect config files
440
- const configPatterns = [
441
- "tsconfig.json",
442
- "jsconfig.json",
443
- "next.config.js",
444
- "next.config.mjs",
445
- "next.config.ts",
446
- "vite.config.ts",
447
- "vite.config.js",
448
- "vitest.config.ts",
449
- "jest.config.js",
450
- "jest.config.ts",
451
- "tailwind.config.js",
452
- "tailwind.config.ts",
453
- "postcss.config.js",
454
- "postcss.config.mjs",
455
- ".eslintrc.js",
456
- ".eslintrc.json",
457
- "eslint.config.js",
458
- ".prettierrc",
459
- ".prettierrc.json",
460
- "drizzle.config.ts",
461
- "prisma/schema.prisma",
462
- "turbo.json",
463
- "nx.json",
464
- ];
465
-
466
- for (const pattern of configPatterns) {
467
- if (fileExists(join(cwd, pattern))) {
468
- configFiles.push(pattern);
469
- }
470
- }
471
-
472
- // Monorepo detection
473
- if (pkg.workspaces || fileExists(join(cwd, "pnpm-workspace.yaml")) || fileExists(join(cwd, "turbo.json"))) {
474
- technologies.push({
475
- name: "Monorepo",
476
- confidence: 0.95,
477
- source: "workspaces or turbo.json",
478
- category: "build",
479
- });
480
- }
481
-
482
- return {
483
- ecosystem: "node",
484
- technologies,
485
- structure,
486
- configFiles: [...new Set(configFiles)],
487
- };
488
- },
489
- };
490
-
491
- // Register the detector
492
- registerDetector(nodeDetector);
493
-
1
+ /**
2
+ * Node.js/JavaScript/TypeScript Ecosystem Detector
3
+ *
4
+ * Detects JavaScript and TypeScript projects including:
5
+ * - Runtime: Node.js, Bun, Deno
6
+ * - Frontend: React, Next.js, Vue, Svelte, Angular
7
+ * - Backend: Express, Fastify, Hono, Elysia, NestJS
8
+ * - ORMs: Prisma, Drizzle, TypeORM, Sequelize
9
+ * - Testing: Jest, Vitest, Playwright, Cypress
10
+ */
11
+
12
+ import { join } from "path";
13
+ import {
14
+ registerDetector,
15
+ Detector,
16
+ DetectorResult,
17
+ DetectedTechnology,
18
+ fileExists,
19
+ dirExists,
20
+ findFiles,
21
+ readJson,
22
+ readText,
23
+ } from "./index";
24
+
25
+ interface PackageJson {
26
+ name?: string;
27
+ version?: string;
28
+ type?: "module" | "commonjs";
29
+ dependencies?: Record<string, string>;
30
+ devDependencies?: Record<string, string>;
31
+ peerDependencies?: Record<string, string>;
32
+ scripts?: Record<string, string>;
33
+ engines?: Record<string, string>;
34
+ workspaces?: string[] | { packages: string[] };
35
+ }
36
+
37
+ const nodeDetector: Detector = {
38
+ name: "node",
39
+ ecosystem: "node",
40
+ priority: 90,
41
+ markers: [
42
+ { type: "file", pattern: "package.json", weight: 1.0 },
43
+ { type: "file", pattern: "package-lock.json", weight: 0.9 },
44
+ { type: "file", pattern: "yarn.lock", weight: 0.9 },
45
+ { type: "file", pattern: "pnpm-lock.yaml", weight: 0.9 },
46
+ { type: "file", pattern: "bun.lockb", weight: 0.9 },
47
+ { type: "file", pattern: "deno.json", weight: 1.0 },
48
+ { type: "file", pattern: "deno.jsonc", weight: 1.0 },
49
+ { type: "file", pattern: "tsconfig.json", weight: 0.7 },
50
+ { type: "file", pattern: "jsconfig.json", weight: 0.7 },
51
+ { type: "directory", pattern: "node_modules", weight: 0.5 },
52
+ ],
53
+
54
+ async detect(cwd: string): Promise<DetectorResult | null> {
55
+ const technologies: DetectedTechnology[] = [];
56
+ const structure: Record<string, string> = {};
57
+ const configFiles: string[] = [];
58
+
59
+ // Check for Deno first (different ecosystem)
60
+ if (fileExists(join(cwd, "deno.json")) || fileExists(join(cwd, "deno.jsonc"))) {
61
+ technologies.push({
62
+ name: "Deno",
63
+ confidence: 1.0,
64
+ source: "deno.json",
65
+ category: "runtime",
66
+ });
67
+ configFiles.push("deno.json");
68
+ }
69
+
70
+ // Check for package.json
71
+ const pkgPath = join(cwd, "package.json");
72
+ if (!fileExists(pkgPath)) {
73
+ // No package.json - might still be JS project
74
+ const jsFiles = findFiles(cwd, "**/*.{js,ts,jsx,tsx}");
75
+ if (jsFiles.length === 0) {
76
+ return technologies.length > 0
77
+ ? { ecosystem: "node", technologies, structure, configFiles }
78
+ : null;
79
+ }
80
+
81
+ // Has JS files but no package.json
82
+ technologies.push({
83
+ name: "JavaScript",
84
+ confidence: 0.6,
85
+ source: "*.js files",
86
+ category: "runtime",
87
+ });
88
+
89
+ return { ecosystem: "node", technologies, structure, configFiles };
90
+ }
91
+
92
+ configFiles.push("package.json");
93
+ const pkg: PackageJson | null = readJson(pkgPath);
94
+ if (!pkg) return null;
95
+
96
+ const allDeps = {
97
+ ...pkg.dependencies,
98
+ ...pkg.devDependencies,
99
+ ...pkg.peerDependencies,
100
+ };
101
+
102
+ // Detect package manager from lock files
103
+ if (fileExists(join(cwd, "bun.lockb"))) {
104
+ technologies.push({
105
+ name: "Bun",
106
+ confidence: 1.0,
107
+ source: "bun.lockb",
108
+ category: "runtime",
109
+ });
110
+ configFiles.push("bun.lockb");
111
+ } else if (fileExists(join(cwd, "pnpm-lock.yaml"))) {
112
+ technologies.push({
113
+ name: "pnpm",
114
+ confidence: 0.9,
115
+ source: "pnpm-lock.yaml",
116
+ category: "build",
117
+ });
118
+ configFiles.push("pnpm-lock.yaml");
119
+ } else if (fileExists(join(cwd, "yarn.lock"))) {
120
+ technologies.push({
121
+ name: "Yarn",
122
+ confidence: 0.9,
123
+ source: "yarn.lock",
124
+ category: "build",
125
+ });
126
+ configFiles.push("yarn.lock");
127
+ } else if (fileExists(join(cwd, "package-lock.json"))) {
128
+ technologies.push({
129
+ name: "npm",
130
+ confidence: 0.9,
131
+ source: "package-lock.json",
132
+ category: "build",
133
+ });
134
+ configFiles.push("package-lock.json");
135
+ }
136
+
137
+ // TypeScript detection
138
+ if (fileExists(join(cwd, "tsconfig.json")) || allDeps["typescript"]) {
139
+ technologies.push({
140
+ name: "TypeScript",
141
+ version: allDeps["typescript"],
142
+ confidence: 1.0,
143
+ source: "tsconfig.json or dependency",
144
+ category: "runtime",
145
+ });
146
+ configFiles.push("tsconfig.json");
147
+ }
148
+
149
+ // Runtime from engines
150
+ if (pkg.engines?.node) {
151
+ technologies.push({
152
+ name: "Node.js",
153
+ version: pkg.engines.node,
154
+ confidence: 0.95,
155
+ source: "package.json engines",
156
+ category: "runtime",
157
+ });
158
+ } else if (pkg.engines?.bun) {
159
+ technologies.push({
160
+ name: "Bun",
161
+ version: pkg.engines.bun,
162
+ confidence: 0.95,
163
+ source: "package.json engines",
164
+ category: "runtime",
165
+ });
166
+ }
167
+
168
+ // Frontend Framework Detection (priority order matters)
169
+ const frontendFrameworks = [
170
+ { dep: "next", name: "Next.js", confidence: 1.0 },
171
+ { dep: "nuxt", name: "Nuxt", confidence: 1.0 },
172
+ { dep: "@remix-run/react", name: "Remix", confidence: 1.0 },
173
+ { dep: "gatsby", name: "Gatsby", confidence: 1.0 },
174
+ { dep: "@angular/core", name: "Angular", confidence: 1.0 },
175
+ { dep: "vue", name: "Vue", confidence: 0.95 },
176
+ { dep: "svelte", name: "Svelte", confidence: 0.95 },
177
+ { dep: "@solidjs/core", name: "SolidJS", confidence: 0.95 },
178
+ { dep: "solid-js", name: "SolidJS", confidence: 0.95 },
179
+ { dep: "preact", name: "Preact", confidence: 0.95 },
180
+ { dep: "react", name: "React", confidence: 0.9 },
181
+ { dep: "astro", name: "Astro", confidence: 1.0 },
182
+ { dep: "qwik", name: "Qwik", confidence: 1.0 },
183
+ ];
184
+
185
+ for (const fw of frontendFrameworks) {
186
+ if (allDeps[fw.dep]) {
187
+ technologies.push({
188
+ name: fw.name,
189
+ version: allDeps[fw.dep],
190
+ confidence: fw.confidence,
191
+ source: `Dependency: ${fw.dep}`,
192
+ category: "frontend",
193
+ });
194
+ break; // Only detect primary frontend framework
195
+ }
196
+ }
197
+
198
+ // Backend Framework Detection
199
+ const backendFrameworks = [
200
+ { dep: "@nestjs/core", name: "NestJS", confidence: 1.0 },
201
+ { dep: "hono", name: "Hono", confidence: 1.0 },
202
+ { dep: "elysia", name: "Elysia", confidence: 1.0 },
203
+ { dep: "fastify", name: "Fastify", confidence: 0.95 },
204
+ { dep: "koa", name: "Koa", confidence: 0.95 },
205
+ { dep: "express", name: "Express", confidence: 0.9 },
206
+ { dep: "@hapi/hapi", name: "Hapi", confidence: 0.95 },
207
+ { dep: "trpc", name: "tRPC", confidence: 0.9 },
208
+ { dep: "@trpc/server", name: "tRPC", confidence: 0.9 },
209
+ ];
210
+
211
+ for (const fw of backendFrameworks) {
212
+ if (allDeps[fw.dep]) {
213
+ technologies.push({
214
+ name: fw.name,
215
+ version: allDeps[fw.dep],
216
+ confidence: fw.confidence,
217
+ source: `Dependency: ${fw.dep}`,
218
+ category: "backend",
219
+ });
220
+ }
221
+ }
222
+
223
+ // ORM Detection
224
+ const orms = [
225
+ { dep: "drizzle-orm", name: "Drizzle", confidence: 1.0 },
226
+ { dep: "prisma", name: "Prisma", confidence: 1.0 },
227
+ { dep: "@prisma/client", name: "Prisma", confidence: 1.0 },
228
+ { dep: "typeorm", name: "TypeORM", confidence: 1.0 },
229
+ { dep: "sequelize", name: "Sequelize", confidence: 1.0 },
230
+ { dep: "kysely", name: "Kysely", confidence: 1.0 },
231
+ { dep: "mongoose", name: "Mongoose", confidence: 1.0 },
232
+ { dep: "knex", name: "Knex", confidence: 0.9 },
233
+ { dep: "mikro-orm", name: "MikroORM", confidence: 1.0 },
234
+ { dep: "@mikro-orm/core", name: "MikroORM", confidence: 1.0 },
235
+ ];
236
+
237
+ const detectedOrms = new Set<string>();
238
+ for (const orm of orms) {
239
+ if (allDeps[orm.dep] && !detectedOrms.has(orm.name)) {
240
+ technologies.push({
241
+ name: orm.name,
242
+ version: allDeps[orm.dep],
243
+ confidence: orm.confidence,
244
+ source: `Dependency: ${orm.dep}`,
245
+ category: "orm",
246
+ });
247
+ detectedOrms.add(orm.name);
248
+ }
249
+ }
250
+
251
+ // Database Detection
252
+ const databases = [
253
+ { dep: "@supabase/supabase-js", name: "Supabase", confidence: 1.0 },
254
+ { dep: "firebase", name: "Firebase", confidence: 1.0 },
255
+ { dep: "@planetscale/database", name: "PlanetScale", confidence: 1.0 },
256
+ { dep: "pg", name: "PostgreSQL", confidence: 0.9 },
257
+ { dep: "postgres", name: "PostgreSQL", confidence: 0.9 },
258
+ { dep: "@neondatabase/serverless", name: "Neon", confidence: 1.0 },
259
+ { dep: "mysql2", name: "MySQL", confidence: 0.9 },
260
+ { dep: "mysql", name: "MySQL", confidence: 0.9 },
261
+ { dep: "better-sqlite3", name: "SQLite", confidence: 0.9 },
262
+ { dep: "sql.js", name: "SQLite", confidence: 0.9 },
263
+ { dep: "mongodb", name: "MongoDB", confidence: 0.9 },
264
+ { dep: "redis", name: "Redis", confidence: 0.9 },
265
+ { dep: "ioredis", name: "Redis", confidence: 0.9 },
266
+ { dep: "@upstash/redis", name: "Upstash Redis", confidence: 1.0 },
267
+ { dep: "@libsql/client", name: "Turso", confidence: 1.0 },
268
+ ];
269
+
270
+ const detectedDbs = new Set<string>();
271
+ for (const db of databases) {
272
+ if (allDeps[db.dep] && !detectedDbs.has(db.name)) {
273
+ technologies.push({
274
+ name: db.name,
275
+ version: allDeps[db.dep],
276
+ confidence: db.confidence,
277
+ source: `Dependency: ${db.dep}`,
278
+ category: "database",
279
+ });
280
+ detectedDbs.add(db.name);
281
+ }
282
+ }
283
+
284
+ // Styling Detection
285
+ const stylingTools = [
286
+ { dep: "tailwindcss", name: "Tailwind CSS", confidence: 1.0 },
287
+ { dep: "styled-components", name: "Styled Components", confidence: 1.0 },
288
+ { dep: "@emotion/react", name: "Emotion", confidence: 1.0 },
289
+ { dep: "@emotion/styled", name: "Emotion", confidence: 1.0 },
290
+ { dep: "sass", name: "Sass", confidence: 0.9 },
291
+ { dep: "less", name: "Less", confidence: 0.9 },
292
+ { dep: "@vanilla-extract/css", name: "Vanilla Extract", confidence: 1.0 },
293
+ { dep: "linaria", name: "Linaria", confidence: 1.0 },
294
+ { dep: "@stitches/react", name: "Stitches", confidence: 1.0 },
295
+ { dep: "unocss", name: "UnoCSS", confidence: 1.0 },
296
+ { dep: "@master/css", name: "Master CSS", confidence: 1.0 },
297
+ ];
298
+
299
+ for (const style of stylingTools) {
300
+ if (allDeps[style.dep]) {
301
+ technologies.push({
302
+ name: style.name,
303
+ version: allDeps[style.dep],
304
+ confidence: style.confidence,
305
+ source: `Dependency: ${style.dep}`,
306
+ category: "styling",
307
+ });
308
+ break;
309
+ }
310
+ }
311
+
312
+ // UI Component Libraries
313
+ const uiLibs = [
314
+ { dep: "@radix-ui/react-dialog", name: "Radix UI", confidence: 0.9 },
315
+ { dep: "@headlessui/react", name: "Headless UI", confidence: 0.9 },
316
+ { dep: "@chakra-ui/react", name: "Chakra UI", confidence: 1.0 },
317
+ { dep: "@mantine/core", name: "Mantine", confidence: 1.0 },
318
+ { dep: "@mui/material", name: "Material UI", confidence: 1.0 },
319
+ { dep: "antd", name: "Ant Design", confidence: 1.0 },
320
+ { dep: "@nextui-org/react", name: "NextUI", confidence: 1.0 },
321
+ { dep: "shadcn-ui", name: "shadcn/ui", confidence: 1.0 },
322
+ ];
323
+
324
+ for (const ui of uiLibs) {
325
+ if (allDeps[ui.dep]) {
326
+ technologies.push({
327
+ name: ui.name,
328
+ version: allDeps[ui.dep],
329
+ confidence: ui.confidence,
330
+ source: `Dependency: ${ui.dep}`,
331
+ category: "styling",
332
+ });
333
+ }
334
+ }
335
+
336
+ // Auth Detection
337
+ const authProviders = [
338
+ { dep: "next-auth", name: "NextAuth.js", confidence: 1.0 },
339
+ { dep: "@auth/core", name: "Auth.js", confidence: 1.0 },
340
+ { dep: "@clerk/nextjs", name: "Clerk", confidence: 1.0 },
341
+ { dep: "@clerk/clerk-react", name: "Clerk", confidence: 1.0 },
342
+ { dep: "@supabase/auth-helpers-nextjs", name: "Supabase Auth", confidence: 1.0 },
343
+ { dep: "@supabase/auth-helpers-react", name: "Supabase Auth", confidence: 1.0 },
344
+ { dep: "@auth0/auth0-react", name: "Auth0", confidence: 1.0 },
345
+ { dep: "@okta/okta-react", name: "Okta", confidence: 1.0 },
346
+ { dep: "passport", name: "Passport.js", confidence: 0.9 },
347
+ { dep: "@lucia-auth/core", name: "Lucia", confidence: 1.0 },
348
+ { dep: "lucia", name: "Lucia", confidence: 1.0 },
349
+ { dep: "better-auth", name: "Better Auth", confidence: 1.0 },
350
+ ];
351
+
352
+ for (const auth of authProviders) {
353
+ if (allDeps[auth.dep]) {
354
+ technologies.push({
355
+ name: auth.name,
356
+ version: allDeps[auth.dep],
357
+ confidence: auth.confidence,
358
+ source: `Dependency: ${auth.dep}`,
359
+ category: "auth",
360
+ });
361
+ break;
362
+ }
363
+ }
364
+
365
+ // Testing Detection
366
+ const testingTools = [
367
+ { dep: "vitest", name: "Vitest", confidence: 1.0 },
368
+ { dep: "jest", name: "Jest", confidence: 1.0 },
369
+ { dep: "@testing-library/react", name: "Testing Library", confidence: 0.9 },
370
+ { dep: "playwright", name: "Playwright", confidence: 1.0 },
371
+ { dep: "@playwright/test", name: "Playwright", confidence: 1.0 },
372
+ { dep: "cypress", name: "Cypress", confidence: 1.0 },
373
+ { dep: "mocha", name: "Mocha", confidence: 0.9 },
374
+ { dep: "ava", name: "AVA", confidence: 0.9 },
375
+ { dep: "tap", name: "Node TAP", confidence: 0.9 },
376
+ { dep: "bun:test", name: "Bun Test", confidence: 0.9 },
377
+ ];
378
+
379
+ for (const test of testingTools) {
380
+ if (allDeps[test.dep]) {
381
+ technologies.push({
382
+ name: test.name,
383
+ version: allDeps[test.dep],
384
+ confidence: test.confidence,
385
+ source: `Dependency: ${test.dep}`,
386
+ category: "testing",
387
+ });
388
+ }
389
+ }
390
+
391
+ // Build Tools
392
+ const buildTools = [
393
+ { dep: "vite", name: "Vite", confidence: 1.0 },
394
+ { dep: "webpack", name: "Webpack", confidence: 0.9 },
395
+ { dep: "esbuild", name: "esbuild", confidence: 0.9 },
396
+ { dep: "rollup", name: "Rollup", confidence: 0.9 },
397
+ { dep: "parcel", name: "Parcel", confidence: 0.9 },
398
+ { dep: "turbo", name: "Turborepo", confidence: 1.0 },
399
+ { dep: "nx", name: "Nx", confidence: 1.0 },
400
+ { dep: "lerna", name: "Lerna", confidence: 0.9 },
401
+ ];
402
+
403
+ for (const tool of buildTools) {
404
+ if (allDeps[tool.dep]) {
405
+ technologies.push({
406
+ name: tool.name,
407
+ version: allDeps[tool.dep],
408
+ confidence: tool.confidence,
409
+ source: `Dependency: ${tool.dep}`,
410
+ category: "build",
411
+ });
412
+ }
413
+ }
414
+
415
+ // Structure detection
416
+ const structurePaths = [
417
+ { key: "components", paths: ["src/components", "components", "app/components", "lib/components"] },
418
+ { key: "services", paths: ["src/services", "services", "src/lib/services", "lib/services"] },
419
+ { key: "hooks", paths: ["src/hooks", "hooks", "src/lib/hooks", "lib/hooks"] },
420
+ { key: "utils", paths: ["src/utils", "utils", "src/lib/utils", "lib/utils", "src/helpers", "helpers"] },
421
+ { key: "types", paths: ["src/types", "types", "src/@types", "@types"] },
422
+ { key: "api", paths: ["src/app/api", "pages/api", "src/api", "api", "app/api"] },
423
+ { key: "schema", paths: ["src/db/schema", "src/schema", "prisma/schema", "drizzle", "src/database"] },
424
+ { key: "stores", paths: ["src/stores", "stores", "src/store", "store"] },
425
+ { key: "context", paths: ["src/context", "context", "src/contexts", "contexts"] },
426
+ { key: "middleware", paths: ["src/middleware", "middleware"] },
427
+ { key: "lib", paths: ["src/lib", "lib"] },
428
+ ];
429
+
430
+ for (const { key, paths } of structurePaths) {
431
+ for (const p of paths) {
432
+ if (dirExists(join(cwd, p))) {
433
+ structure[key] = p;
434
+ break;
435
+ }
436
+ }
437
+ }
438
+
439
+ // Detect config files
440
+ const configPatterns = [
441
+ "tsconfig.json",
442
+ "jsconfig.json",
443
+ "next.config.js",
444
+ "next.config.mjs",
445
+ "next.config.ts",
446
+ "vite.config.ts",
447
+ "vite.config.js",
448
+ "vitest.config.ts",
449
+ "jest.config.js",
450
+ "jest.config.ts",
451
+ "tailwind.config.js",
452
+ "tailwind.config.ts",
453
+ "postcss.config.js",
454
+ "postcss.config.mjs",
455
+ ".eslintrc.js",
456
+ ".eslintrc.json",
457
+ "eslint.config.js",
458
+ ".prettierrc",
459
+ ".prettierrc.json",
460
+ "drizzle.config.ts",
461
+ "prisma/schema.prisma",
462
+ "turbo.json",
463
+ "nx.json",
464
+ ];
465
+
466
+ for (const pattern of configPatterns) {
467
+ if (fileExists(join(cwd, pattern))) {
468
+ configFiles.push(pattern);
469
+ }
470
+ }
471
+
472
+ // Monorepo detection
473
+ if (pkg.workspaces || fileExists(join(cwd, "pnpm-workspace.yaml")) || fileExists(join(cwd, "turbo.json"))) {
474
+ technologies.push({
475
+ name: "Monorepo",
476
+ confidence: 0.95,
477
+ source: "workspaces or turbo.json",
478
+ category: "build",
479
+ });
480
+ }
481
+
482
+ return {
483
+ ecosystem: "node",
484
+ technologies,
485
+ structure,
486
+ configFiles: [...new Set(configFiles)],
487
+ };
488
+ },
489
+ };
490
+
491
+ // Register the detector
492
+ registerDetector(nodeDetector);
493
+
494
494
  export default nodeDetector;