genbox 1.0.2 → 1.0.4

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.
@@ -0,0 +1,621 @@
1
+ "use strict";
2
+ /**
3
+ * Framework Detector
4
+ *
5
+ * Detects frameworks and their configurations:
6
+ * - Node.js: Next.js, NestJS, Express, etc.
7
+ * - Python: Django, FastAPI, Flask, etc.
8
+ * - Go: Gin, Echo, Fiber, etc.
9
+ * - Ruby: Rails, Sinatra
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.FrameworkDetector = void 0;
46
+ const fs = __importStar(require("fs"));
47
+ const path = __importStar(require("path"));
48
+ const FRAMEWORK_SIGNATURES = {
49
+ // Node.js - Fullstack/Frontend
50
+ nextjs: {
51
+ language: 'node',
52
+ detection: [
53
+ { type: 'file', value: 'next.config.js' },
54
+ { type: 'file', value: 'next.config.mjs' },
55
+ { type: 'file', value: 'next.config.ts' },
56
+ { type: 'dependency', value: 'next' },
57
+ ],
58
+ defaults: {
59
+ type: 'fullstack',
60
+ defaultPort: 3000,
61
+ devCommand: 'next dev',
62
+ buildCommand: 'next build',
63
+ startCommand: 'next start',
64
+ outputDir: '.next',
65
+ },
66
+ },
67
+ nuxt: {
68
+ language: 'node',
69
+ detection: [
70
+ { type: 'file', value: 'nuxt.config.ts' },
71
+ { type: 'file', value: 'nuxt.config.js' },
72
+ { type: 'dependency', value: 'nuxt' },
73
+ ],
74
+ defaults: {
75
+ type: 'fullstack',
76
+ defaultPort: 3000,
77
+ devCommand: 'nuxt dev',
78
+ buildCommand: 'nuxt build',
79
+ startCommand: 'nuxt start',
80
+ outputDir: '.output',
81
+ },
82
+ },
83
+ remix: {
84
+ language: 'node',
85
+ detection: [
86
+ { type: 'file', value: 'remix.config.js' },
87
+ { type: 'dependency', value: '@remix-run/react' },
88
+ ],
89
+ defaults: {
90
+ type: 'fullstack',
91
+ defaultPort: 3000,
92
+ devCommand: 'remix dev',
93
+ buildCommand: 'remix build',
94
+ startCommand: 'remix-serve build',
95
+ outputDir: 'build',
96
+ },
97
+ },
98
+ astro: {
99
+ language: 'node',
100
+ detection: [
101
+ { type: 'file', value: 'astro.config.mjs' },
102
+ { type: 'file', value: 'astro.config.ts' },
103
+ { type: 'dependency', value: 'astro' },
104
+ ],
105
+ defaults: {
106
+ type: 'frontend',
107
+ defaultPort: 4321,
108
+ devCommand: 'astro dev',
109
+ buildCommand: 'astro build',
110
+ outputDir: 'dist',
111
+ },
112
+ },
113
+ gatsby: {
114
+ language: 'node',
115
+ detection: [
116
+ { type: 'file', value: 'gatsby-config.js' },
117
+ { type: 'file', value: 'gatsby-config.ts' },
118
+ { type: 'dependency', value: 'gatsby' },
119
+ ],
120
+ defaults: {
121
+ type: 'frontend',
122
+ defaultPort: 8000,
123
+ devCommand: 'gatsby develop',
124
+ buildCommand: 'gatsby build',
125
+ outputDir: 'public',
126
+ },
127
+ },
128
+ sveltekit: {
129
+ language: 'node',
130
+ detection: [
131
+ { type: 'file', value: 'svelte.config.js' },
132
+ { type: 'dependency', value: '@sveltejs/kit' },
133
+ ],
134
+ defaults: {
135
+ type: 'fullstack',
136
+ defaultPort: 5173,
137
+ devCommand: 'vite dev',
138
+ buildCommand: 'vite build',
139
+ outputDir: 'build',
140
+ },
141
+ },
142
+ react: {
143
+ language: 'node',
144
+ detection: [
145
+ { type: 'dependency', value: 'react' },
146
+ { type: 'dependency', value: 'react-dom' },
147
+ ],
148
+ defaults: {
149
+ type: 'frontend',
150
+ defaultPort: 3000,
151
+ devCommand: 'vite dev',
152
+ buildCommand: 'vite build',
153
+ outputDir: 'dist',
154
+ },
155
+ },
156
+ vue: {
157
+ language: 'node',
158
+ detection: [
159
+ { type: 'dependency', value: 'vue' },
160
+ ],
161
+ defaults: {
162
+ type: 'frontend',
163
+ defaultPort: 5173,
164
+ devCommand: 'vite dev',
165
+ buildCommand: 'vite build',
166
+ outputDir: 'dist',
167
+ },
168
+ },
169
+ angular: {
170
+ language: 'node',
171
+ detection: [
172
+ { type: 'file', value: 'angular.json' },
173
+ { type: 'dependency', value: '@angular/core' },
174
+ ],
175
+ defaults: {
176
+ type: 'frontend',
177
+ defaultPort: 4200,
178
+ devCommand: 'ng serve',
179
+ buildCommand: 'ng build',
180
+ outputDir: 'dist',
181
+ },
182
+ },
183
+ svelte: {
184
+ language: 'node',
185
+ detection: [
186
+ { type: 'dependency', value: 'svelte' },
187
+ ],
188
+ defaults: {
189
+ type: 'frontend',
190
+ defaultPort: 5173,
191
+ devCommand: 'vite dev',
192
+ buildCommand: 'vite build',
193
+ outputDir: 'dist',
194
+ },
195
+ },
196
+ solid: {
197
+ language: 'node',
198
+ detection: [
199
+ { type: 'dependency', value: 'solid-js' },
200
+ ],
201
+ defaults: {
202
+ type: 'frontend',
203
+ defaultPort: 3000,
204
+ devCommand: 'vite dev',
205
+ buildCommand: 'vite build',
206
+ outputDir: 'dist',
207
+ },
208
+ },
209
+ vite: {
210
+ language: 'node',
211
+ detection: [
212
+ { type: 'file', value: 'vite.config.ts' },
213
+ { type: 'file', value: 'vite.config.js' },
214
+ { type: 'dependency', value: 'vite' },
215
+ ],
216
+ defaults: {
217
+ type: 'frontend',
218
+ defaultPort: 5173,
219
+ devCommand: 'vite',
220
+ buildCommand: 'vite build',
221
+ outputDir: 'dist',
222
+ },
223
+ },
224
+ // Node.js - Backend
225
+ nestjs: {
226
+ language: 'node',
227
+ detection: [
228
+ { type: 'file', value: 'nest-cli.json' },
229
+ { type: 'dependency', value: '@nestjs/core' },
230
+ ],
231
+ defaults: {
232
+ type: 'backend',
233
+ defaultPort: 3000,
234
+ devCommand: 'nest start --watch',
235
+ buildCommand: 'nest build',
236
+ startCommand: 'node dist/main',
237
+ outputDir: 'dist',
238
+ },
239
+ },
240
+ express: {
241
+ language: 'node',
242
+ detection: [
243
+ { type: 'dependency', value: 'express' },
244
+ ],
245
+ defaults: {
246
+ type: 'backend',
247
+ defaultPort: 3000,
248
+ devCommand: 'node --watch src/index.js',
249
+ startCommand: 'node src/index.js',
250
+ },
251
+ },
252
+ fastify: {
253
+ language: 'node',
254
+ detection: [
255
+ { type: 'dependency', value: 'fastify' },
256
+ ],
257
+ defaults: {
258
+ type: 'backend',
259
+ defaultPort: 3000,
260
+ devCommand: 'node --watch src/index.js',
261
+ startCommand: 'node src/index.js',
262
+ },
263
+ },
264
+ koa: {
265
+ language: 'node',
266
+ detection: [
267
+ { type: 'dependency', value: 'koa' },
268
+ ],
269
+ defaults: {
270
+ type: 'backend',
271
+ defaultPort: 3000,
272
+ devCommand: 'node --watch src/index.js',
273
+ startCommand: 'node src/index.js',
274
+ },
275
+ },
276
+ hono: {
277
+ language: 'node',
278
+ detection: [
279
+ { type: 'dependency', value: 'hono' },
280
+ ],
281
+ defaults: {
282
+ type: 'backend',
283
+ defaultPort: 3000,
284
+ devCommand: 'node --watch src/index.ts',
285
+ startCommand: 'node dist/index.js',
286
+ },
287
+ },
288
+ hapi: {
289
+ language: 'node',
290
+ detection: [
291
+ { type: 'dependency', value: '@hapi/hapi' },
292
+ ],
293
+ defaults: {
294
+ type: 'backend',
295
+ defaultPort: 3000,
296
+ devCommand: 'node --watch src/index.js',
297
+ startCommand: 'node src/index.js',
298
+ },
299
+ },
300
+ // Python
301
+ django: {
302
+ language: 'python',
303
+ detection: [
304
+ { type: 'file', value: 'manage.py' },
305
+ { type: 'dependency', value: 'django' },
306
+ { type: 'dependency', value: 'Django' },
307
+ ],
308
+ defaults: {
309
+ type: 'fullstack',
310
+ defaultPort: 8000,
311
+ devCommand: 'python manage.py runserver',
312
+ startCommand: 'gunicorn app.wsgi:application',
313
+ },
314
+ },
315
+ fastapi: {
316
+ language: 'python',
317
+ detection: [
318
+ { type: 'dependency', value: 'fastapi' },
319
+ { type: 'pattern', value: /from\s+fastapi\s+import/ },
320
+ ],
321
+ defaults: {
322
+ type: 'backend',
323
+ defaultPort: 8000,
324
+ devCommand: 'uvicorn main:app --reload',
325
+ startCommand: 'uvicorn main:app --host 0.0.0.0',
326
+ },
327
+ },
328
+ flask: {
329
+ language: 'python',
330
+ detection: [
331
+ { type: 'dependency', value: 'flask' },
332
+ { type: 'dependency', value: 'Flask' },
333
+ ],
334
+ defaults: {
335
+ type: 'backend',
336
+ defaultPort: 5000,
337
+ devCommand: 'flask run --debug',
338
+ startCommand: 'gunicorn app:app',
339
+ },
340
+ },
341
+ starlette: {
342
+ language: 'python',
343
+ detection: [
344
+ { type: 'dependency', value: 'starlette' },
345
+ ],
346
+ defaults: {
347
+ type: 'backend',
348
+ defaultPort: 8000,
349
+ devCommand: 'uvicorn main:app --reload',
350
+ startCommand: 'uvicorn main:app',
351
+ },
352
+ },
353
+ // Go
354
+ gin: {
355
+ language: 'go',
356
+ detection: [
357
+ { type: 'dependency', value: 'github.com/gin-gonic/gin' },
358
+ ],
359
+ defaults: {
360
+ type: 'backend',
361
+ defaultPort: 8080,
362
+ devCommand: 'go run .',
363
+ buildCommand: 'go build -o app',
364
+ startCommand: './app',
365
+ },
366
+ },
367
+ echo: {
368
+ language: 'go',
369
+ detection: [
370
+ { type: 'dependency', value: 'github.com/labstack/echo' },
371
+ ],
372
+ defaults: {
373
+ type: 'backend',
374
+ defaultPort: 8080,
375
+ devCommand: 'go run .',
376
+ buildCommand: 'go build -o app',
377
+ startCommand: './app',
378
+ },
379
+ },
380
+ fiber: {
381
+ language: 'go',
382
+ detection: [
383
+ { type: 'dependency', value: 'github.com/gofiber/fiber' },
384
+ ],
385
+ defaults: {
386
+ type: 'backend',
387
+ defaultPort: 3000,
388
+ devCommand: 'go run .',
389
+ buildCommand: 'go build -o app',
390
+ startCommand: './app',
391
+ },
392
+ },
393
+ chi: {
394
+ language: 'go',
395
+ detection: [
396
+ { type: 'dependency', value: 'github.com/go-chi/chi' },
397
+ ],
398
+ defaults: {
399
+ type: 'backend',
400
+ defaultPort: 8080,
401
+ devCommand: 'go run .',
402
+ buildCommand: 'go build -o app',
403
+ startCommand: './app',
404
+ },
405
+ },
406
+ // Ruby
407
+ rails: {
408
+ language: 'ruby',
409
+ detection: [
410
+ { type: 'file', value: 'config/routes.rb' },
411
+ { type: 'dependency', value: 'rails' },
412
+ ],
413
+ defaults: {
414
+ type: 'fullstack',
415
+ defaultPort: 3000,
416
+ devCommand: 'rails server',
417
+ startCommand: 'rails server -e production',
418
+ },
419
+ },
420
+ sinatra: {
421
+ language: 'ruby',
422
+ detection: [
423
+ { type: 'dependency', value: 'sinatra' },
424
+ ],
425
+ defaults: {
426
+ type: 'backend',
427
+ defaultPort: 4567,
428
+ devCommand: 'ruby app.rb',
429
+ startCommand: 'ruby app.rb -e production',
430
+ },
431
+ },
432
+ };
433
+ // Priority order for detection (more specific frameworks first)
434
+ const DETECTION_PRIORITY = [
435
+ // Node.js fullstack (most specific)
436
+ 'nextjs', 'nuxt', 'remix', 'sveltekit', 'gatsby', 'astro',
437
+ // Node.js backend
438
+ 'nestjs', 'fastify', 'hono', 'hapi', 'koa', 'express',
439
+ // Node.js frontend
440
+ 'angular', 'vue', 'svelte', 'solid', 'react', 'vite',
441
+ // Python
442
+ 'django', 'fastapi', 'flask', 'starlette',
443
+ // Go
444
+ 'gin', 'echo', 'fiber', 'chi',
445
+ // Ruby
446
+ 'rails', 'sinatra',
447
+ ];
448
+ class FrameworkDetector {
449
+ /**
450
+ * Detect frameworks in the project
451
+ */
452
+ async detect(root, runtimes) {
453
+ const frameworks = [];
454
+ const detectedLanguages = new Set(runtimes.map(r => r.language));
455
+ const alreadyDetected = new Set();
456
+ // Check frameworks in priority order
457
+ for (const framework of DETECTION_PRIORITY) {
458
+ if (alreadyDetected.has(framework))
459
+ continue;
460
+ const signature = FRAMEWORK_SIGNATURES[framework];
461
+ // Skip if language not detected
462
+ if (!detectedLanguages.has(signature.language))
463
+ continue;
464
+ // Check detection rules
465
+ const matches = await this.matchesSignature(root, signature.detection);
466
+ if (matches) {
467
+ // For generic frameworks (react, vue, express), check they're not part of a fullstack framework
468
+ if (this.isGenericFramework(framework)) {
469
+ const hasFullstackFramework = frameworks.some(f => FRAMEWORK_SIGNATURES[f.name].defaults.type === 'fullstack');
470
+ if (hasFullstackFramework)
471
+ continue;
472
+ }
473
+ const version = await this.getFrameworkVersion(root, framework);
474
+ frameworks.push({
475
+ name: framework,
476
+ version,
477
+ ...signature.defaults,
478
+ });
479
+ alreadyDetected.add(framework);
480
+ }
481
+ }
482
+ return frameworks;
483
+ }
484
+ isGenericFramework(framework) {
485
+ return ['react', 'vue', 'svelte', 'solid', 'express', 'vite'].includes(framework);
486
+ }
487
+ async matchesSignature(root, rules) {
488
+ for (const rule of rules) {
489
+ const matches = await this.matchRule(root, rule);
490
+ if (matches)
491
+ return true;
492
+ }
493
+ return false;
494
+ }
495
+ async matchRule(root, rule) {
496
+ switch (rule.type) {
497
+ case 'file':
498
+ return fs.existsSync(path.join(root, rule.value));
499
+ case 'dependency':
500
+ return this.hasDependency(root, rule.value);
501
+ case 'pattern':
502
+ return this.matchesPattern(root, rule.value);
503
+ default:
504
+ return false;
505
+ }
506
+ }
507
+ hasDependency(root, dep) {
508
+ // Check Node.js package.json
509
+ const pkgPath = path.join(root, 'package.json');
510
+ if (fs.existsSync(pkgPath)) {
511
+ try {
512
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
513
+ const allDeps = {
514
+ ...(pkg.dependencies || {}),
515
+ ...(pkg.devDependencies || {}),
516
+ ...(pkg.peerDependencies || {}),
517
+ };
518
+ if (allDeps[dep])
519
+ return true;
520
+ }
521
+ catch { }
522
+ }
523
+ // Check Python requirements
524
+ const reqPath = path.join(root, 'requirements.txt');
525
+ if (fs.existsSync(reqPath)) {
526
+ try {
527
+ const content = fs.readFileSync(reqPath, 'utf8').toLowerCase();
528
+ if (content.includes(dep.toLowerCase()))
529
+ return true;
530
+ }
531
+ catch { }
532
+ }
533
+ // Check Python pyproject.toml
534
+ const pyprojectPath = path.join(root, 'pyproject.toml');
535
+ if (fs.existsSync(pyprojectPath)) {
536
+ try {
537
+ const content = fs.readFileSync(pyprojectPath, 'utf8').toLowerCase();
538
+ if (content.includes(dep.toLowerCase()))
539
+ return true;
540
+ }
541
+ catch { }
542
+ }
543
+ // Check Go go.mod
544
+ const goModPath = path.join(root, 'go.mod');
545
+ if (fs.existsSync(goModPath)) {
546
+ try {
547
+ const content = fs.readFileSync(goModPath, 'utf8');
548
+ if (content.includes(dep))
549
+ return true;
550
+ }
551
+ catch { }
552
+ }
553
+ // Check Ruby Gemfile
554
+ const gemfilePath = path.join(root, 'Gemfile');
555
+ if (fs.existsSync(gemfilePath)) {
556
+ try {
557
+ const content = fs.readFileSync(gemfilePath, 'utf8').toLowerCase();
558
+ if (content.includes(dep.toLowerCase()))
559
+ return true;
560
+ }
561
+ catch { }
562
+ }
563
+ return false;
564
+ }
565
+ matchesPattern(root, pattern) {
566
+ // Search common source files for the pattern
567
+ const sourceFiles = [
568
+ 'main.py', 'app.py', 'src/main.py', 'src/app.py',
569
+ 'main.go', 'cmd/main.go',
570
+ 'app.rb', 'config.ru',
571
+ ];
572
+ for (const file of sourceFiles) {
573
+ const filePath = path.join(root, file);
574
+ if (fs.existsSync(filePath)) {
575
+ try {
576
+ const content = fs.readFileSync(filePath, 'utf8');
577
+ if (pattern.test(content))
578
+ return true;
579
+ }
580
+ catch { }
581
+ }
582
+ }
583
+ return false;
584
+ }
585
+ async getFrameworkVersion(root, framework) {
586
+ const signature = FRAMEWORK_SIGNATURES[framework];
587
+ if (signature.language === 'node') {
588
+ // Get from package.json
589
+ const pkgPath = path.join(root, 'package.json');
590
+ if (fs.existsSync(pkgPath)) {
591
+ try {
592
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
593
+ const allDeps = {
594
+ ...(pkg.dependencies || {}),
595
+ ...(pkg.devDependencies || {}),
596
+ };
597
+ // Find the main dependency
598
+ for (const rule of signature.detection) {
599
+ if (rule.type === 'dependency') {
600
+ const version = allDeps[rule.value];
601
+ if (version) {
602
+ // Strip semver operators
603
+ return version.replace(/^[\^~>=<]+/, '');
604
+ }
605
+ }
606
+ }
607
+ }
608
+ catch { }
609
+ }
610
+ }
611
+ return undefined;
612
+ }
613
+ /**
614
+ * Detect framework for a specific app
615
+ */
616
+ async detectForApp(appPath, runtime) {
617
+ const frameworks = await this.detect(appPath, [runtime]);
618
+ return frameworks[0];
619
+ }
620
+ }
621
+ exports.FrameworkDetector = FrameworkDetector;