larats 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs DELETED
@@ -1,1744 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") {
11
- for (let key of __getOwnPropNames(from))
12
- if (!__hasOwnProp.call(to, key) && key !== except)
13
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
- }
15
- return to;
16
- };
17
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
- // If the importer is in node compatibility mode or this is not an ESM
19
- // file that has been converted to a CommonJS file using a Babel-
20
- // compatible transform (i.e. "__esModule" has not been set), then set
21
- // "default" to the CommonJS "module.exports" for node compatibility.
22
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
- mod
24
- ));
25
-
26
- // src/index.ts
27
- var import_node_fs3 = __toESM(require("fs"), 1);
28
- var import_node_path3 = __toESM(require("path"), 1);
29
- var p = __toESM(require("@clack/prompts"), 1);
30
- var import_picocolors = __toESM(require("picocolors"), 1);
31
-
32
- // src/detector.ts
33
- var import_node_fs = __toESM(require("fs"), 1);
34
- var import_node_path = __toESM(require("path"), 1);
35
- function readJsonSafe(filePath) {
36
- try {
37
- if (!import_node_fs.default.existsSync(filePath)) return null;
38
- const content = import_node_fs.default.readFileSync(filePath, "utf-8");
39
- return JSON.parse(content);
40
- } catch {
41
- return null;
42
- }
43
- }
44
- async function detectProjectType(targetDir = process.cwd()) {
45
- const resolvedDir = import_node_path.default.resolve(targetDir);
46
- const dirName = import_node_path.default.basename(resolvedDir);
47
- const hasArtisan = import_node_fs.default.existsSync(import_node_path.default.join(resolvedDir, "artisan"));
48
- const composerPath = import_node_path.default.join(resolvedDir, "composer.json");
49
- const hasComposer = import_node_fs.default.existsSync(composerPath);
50
- const tsconfigPath = import_node_path.default.join(resolvedDir, "tsconfig.json");
51
- const hasTsConfig = import_node_fs.default.existsSync(tsconfigPath);
52
- const packageJsonPath = import_node_path.default.join(resolvedDir, "package.json");
53
- const hasPackageJson = import_node_fs.default.existsSync(packageJsonPath);
54
- if (hasArtisan && hasComposer) {
55
- const composerData = readJsonSafe(composerPath);
56
- const allDeps = {
57
- ...composerData?.require || {},
58
- ...composerData?.["require-dev"] || {}
59
- };
60
- const hasFilament = Object.keys(allDeps).some((dep) => dep.startsWith("filament/"));
61
- const hasLivewire = "livewire/livewire" in allDeps;
62
- const hasInertia = "inertiajs/inertia-laravel" in allDeps;
63
- const hasSanctum = "laravel/sanctum" in allDeps;
64
- const rawVersion = allDeps["laravel/framework"] || "";
65
- const cleanVersion = rawVersion.replace(/[\^~><=]/g, "").trim();
66
- const appModulesDir = import_node_path.default.join(resolvedDir, "app", "Modules");
67
- const rootModulesDir = import_node_path.default.join(resolvedDir, "modules");
68
- const isModular = import_node_fs.default.existsSync(appModulesDir) || import_node_fs.default.existsSync(rootModulesDir);
69
- const modulesDir = import_node_fs.default.existsSync(appModulesDir) ? "app/Modules" : import_node_fs.default.existsSync(rootModulesDir) ? "modules" : void 0;
70
- const extras = [];
71
- if (hasFilament) extras.push("Filament");
72
- if (hasLivewire) extras.push("Livewire");
73
- if (hasInertia) extras.push("Inertia");
74
- if (isModular) extras.push("Modular Architecture");
75
- const summary = `Laravel ${cleanVersion || ""}${extras.length > 0 ? ` (${extras.join(", ")})` : ""}`.trim();
76
- return {
77
- type: "laravel",
78
- name: composerData?.name?.split("/")[1] || dirName,
79
- rootPath: resolvedDir,
80
- laravel: {
81
- version: cleanVersion || void 0,
82
- hasFilament,
83
- hasLivewire,
84
- hasInertia,
85
- hasSanctum,
86
- isModular,
87
- modulesDir
88
- },
89
- summary
90
- };
91
- }
92
- if (hasTsConfig && hasPackageJson) {
93
- const pkg = readJsonSafe(packageJsonPath);
94
- const allDeps = {
95
- ...pkg?.dependencies || {},
96
- ...pkg?.devDependencies || {}
97
- };
98
- let framework = "unknown";
99
- if ("@nestjs/core" in allDeps) framework = "nest";
100
- else if ("next" in allDeps) framework = "next";
101
- else if ("express" in allDeps) framework = "express";
102
- else if ("fastify" in allDeps) framework = "fastify";
103
- else if ("hono" in allDeps) framework = "hono";
104
- else if ("react" in allDeps) framework = "react";
105
- else if ("vue" in allDeps) framework = "vue";
106
- else framework = "node-vanilla";
107
- const hasPrisma = "prisma" in allDeps || "@prisma/client" in allDeps || import_node_fs.default.existsSync(import_node_path.default.join(resolvedDir, "prisma"));
108
- const hasTypeORM = "typeorm" in allDeps;
109
- const hasDrizzle = "drizzle-orm" in allDeps;
110
- const isMonorepo = Boolean(pkg?.workspaces) || import_node_fs.default.existsSync(import_node_path.default.join(resolvedDir, "pnpm-workspace.yaml")) || import_node_fs.default.existsSync(import_node_path.default.join(resolvedDir, "turbo.json")) || import_node_fs.default.existsSync(import_node_path.default.join(resolvedDir, "lerna.json"));
111
- const hasSrc = import_node_fs.default.existsSync(import_node_path.default.join(resolvedDir, "src"));
112
- const srcDir = hasSrc ? "src" : ".";
113
- const isModular = import_node_fs.default.existsSync(import_node_path.default.join(resolvedDir, srcDir, "modules")) || import_node_fs.default.existsSync(import_node_path.default.join(resolvedDir, "modules"));
114
- const extras = [framework !== "node-vanilla" ? framework : "TypeScript"];
115
- if (hasPrisma) extras.push("Prisma");
116
- if (hasTypeORM) extras.push("TypeORM");
117
- if (hasDrizzle) extras.push("Drizzle");
118
- if (isMonorepo) extras.push("Monorepo");
119
- if (isModular) extras.push("Modular");
120
- const summary = `TypeScript (${extras.join(", ")})`;
121
- return {
122
- type: "typescript",
123
- name: pkg?.name || dirName,
124
- rootPath: resolvedDir,
125
- typescript: {
126
- framework,
127
- hasPrisma,
128
- hasTypeORM,
129
- hasDrizzle,
130
- isMonorepo,
131
- isModular,
132
- srcDir
133
- },
134
- summary
135
- };
136
- }
137
- let fallbackName = dirName;
138
- if (hasPackageJson) {
139
- const pkg = readJsonSafe(packageJsonPath);
140
- if (pkg?.name) fallbackName = pkg.name;
141
- }
142
- return {
143
- type: "unknown",
144
- name: fallbackName,
145
- rootPath: resolvedDir,
146
- summary: "Generic / Unknown Project"
147
- };
148
- }
149
-
150
- // src/scanner.ts
151
- var import_fast_glob = __toESM(require("fast-glob"), 1);
152
- var SAFE_IGNORE_LIST = [
153
- // Dependencies & package management
154
- "**/node_modules/**",
155
- "**/vendor/**",
156
- "**/.pnpm/**",
157
- "**/.yarn/**",
158
- // Version control
159
- "**/.git/**",
160
- "**/.svn/**",
161
- "**/.hg/**",
162
- // Storage, cache & build outputs
163
- "**/storage/**",
164
- "**/dist/**",
165
- "**/build/**",
166
- "**/out/**",
167
- "**/.next/**",
168
- "**/.nuxt/**",
169
- "**/.turbo/**",
170
- "**/.cache/**",
171
- "**/coverage/**",
172
- "**/.temp/**",
173
- "**/tmp/**",
174
- // IDEs & OS files
175
- "**/.idea/**",
176
- "**/.vscode/**",
177
- "**/.DS_Store",
178
- "**/Thumbs.db",
179
- // Secrets & environment configurations
180
- "**/.env*",
181
- "**/*.pem",
182
- "**/*.key",
183
- "**/*.pfx",
184
- "**/*.cert",
185
- "**/*.crt",
186
- "**/*.secret",
187
- "**/*.id_rsa",
188
- "**/*.pub",
189
- // Logs & Lock files
190
- "**/*.log",
191
- "**/*.lock",
192
- "**/package-lock.json",
193
- "**/yarn.lock",
194
- "**/pnpm-lock.yaml",
195
- "**/composer.lock"
196
- ];
197
- function buildCleanDirectoryTree(files, maxDepth = 3) {
198
- const root = { name: "", isDir: true, children: /* @__PURE__ */ new Map() };
199
- for (const file of files) {
200
- const parts = file.split(/[/\\]/);
201
- let current = root;
202
- for (let i = 0; i < parts.length; i++) {
203
- const part = parts[i];
204
- const isLast = i === parts.length - 1;
205
- if (!current.children.has(part)) {
206
- current.children.set(part, {
207
- name: part,
208
- isDir: !isLast,
209
- children: /* @__PURE__ */ new Map()
210
- });
211
- }
212
- current = current.children.get(part);
213
- }
214
- }
215
- const lines = [];
216
- function renderNode(node, prefix, currentDepth) {
217
- if (currentDepth > maxDepth) return;
218
- const entries = Array.from(node.children.values()).sort((a, b) => {
219
- if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
220
- return a.name.localeCompare(b.name);
221
- });
222
- if (currentDepth === maxDepth && entries.length > 0) {
223
- lines.push(`${prefix}\u2514\u2500\u2500 ... (${entries.length} items hidden)`);
224
- return;
225
- }
226
- const maxEntriesPerDir = 8;
227
- const displayedEntries = entries.slice(0, maxEntriesPerDir);
228
- const hiddenCount = entries.length - displayedEntries.length;
229
- displayedEntries.forEach((child, index) => {
230
- const isLast = index === displayedEntries.length - 1 && hiddenCount === 0;
231
- const pointer = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
232
- const displayName = child.isDir ? `${child.name}/` : child.name;
233
- lines.push(`${prefix}${pointer}${displayName}`);
234
- if (child.isDir) {
235
- const nextPrefix = prefix + (isLast ? " " : "\u2502 ");
236
- renderNode(child, nextPrefix, currentDepth + 1);
237
- }
238
- });
239
- if (hiddenCount > 0) {
240
- lines.push(`${prefix}\u2514\u2500\u2500 ... (${hiddenCount} more files/folders)`);
241
- }
242
- }
243
- const topLevel = Array.from(root.children.values()).sort((a, b) => {
244
- if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
245
- return a.name.localeCompare(b.name);
246
- });
247
- topLevel.forEach((child, index) => {
248
- const isLast = index === topLevel.length - 1;
249
- const pointer = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
250
- const displayName = child.isDir ? `${child.name}/` : child.name;
251
- lines.push(`${pointer}${displayName}`);
252
- if (child.isDir) {
253
- const nextPrefix = isLast ? " " : "\u2502 ";
254
- renderNode(child, nextPrefix, 1);
255
- }
256
- });
257
- return lines.join("\n");
258
- }
259
- async function scanProject(projectInfo) {
260
- const startTime = Date.now();
261
- const root = projectInfo.rootPath;
262
- const allFiles = await (0, import_fast_glob.default)("**/*", {
263
- cwd: root,
264
- ignore: SAFE_IGNORE_LIST,
265
- dot: false,
266
- onlyFiles: true
267
- });
268
- const normalizedFiles = allFiles.map((f) => f.replace(/\\/g, "/"));
269
- const components = {};
270
- const modulesSet = /* @__PURE__ */ new Set();
271
- if (projectInfo.type === "laravel") {
272
- const models = normalizedFiles.filter((f) => /^app\/(Models\/.*|.*Model)\.php$/i.test(f));
273
- components["Models"] = {
274
- name: "Eloquent Models",
275
- count: models.length,
276
- files: models,
277
- description: "Database entity mappings and business logic models"
278
- };
279
- const controllers = normalizedFiles.filter((f) => /^app\/Http\/Controllers\/.*\.php$/i.test(f));
280
- components["Controllers"] = {
281
- name: "HTTP Controllers",
282
- count: controllers.length,
283
- files: controllers,
284
- description: "Request handling and response orchestration"
285
- };
286
- const services = normalizedFiles.filter((f) => /^app\/(Services|Domain\/.*\/Services)\/.*\.php$/i.test(f));
287
- components["Services"] = {
288
- name: "Service Classes",
289
- count: services.length,
290
- files: services,
291
- description: "Encapsulated business logic and external integrations"
292
- };
293
- const actions = normalizedFiles.filter((f) => /^app\/(Actions|Domain\/.*\/Actions)\/.*\.php$/i.test(f));
294
- components["Actions"] = {
295
- name: "Action Classes",
296
- count: actions.length,
297
- files: actions,
298
- description: "Single-responsibility invokable operations"
299
- };
300
- const requests = normalizedFiles.filter((f) => /^app\/Http\/Requests\/.*\.php$/i.test(f));
301
- components["Form Requests"] = {
302
- name: "Form Requests",
303
- count: requests.length,
304
- files: requests,
305
- description: "Input validation and authorization rules"
306
- };
307
- const filament = normalizedFiles.filter((f) => /^app\/Filament\/.*\.php$/i.test(f));
308
- if (filament.length > 0 || projectInfo.laravel?.hasFilament) {
309
- components["Filament Resources"] = {
310
- name: "Filament Admin Resources",
311
- count: filament.length,
312
- files: filament,
313
- description: "Admin panel CRUD pages, widgets, and schemas"
314
- };
315
- }
316
- const routes = normalizedFiles.filter((f) => /^routes\/.*\.php$/i.test(f));
317
- components["Routes"] = {
318
- name: "Routing Definitions",
319
- count: routes.length,
320
- files: routes,
321
- description: "Web, API, and console endpoint routes"
322
- };
323
- const migrations = normalizedFiles.filter((f) => /^database\/migrations\/.*\.php$/i.test(f));
324
- components["Migrations"] = {
325
- name: "Database Migrations",
326
- count: migrations.length,
327
- files: migrations,
328
- description: "Schema version control and table definitions"
329
- };
330
- const moduleFiles = normalizedFiles.filter((f) => /^app\/Modules\/([^/]+)/i.test(f) || /^modules\/([^/]+)/i.test(f));
331
- for (const mf of moduleFiles) {
332
- const match = mf.match(/^(?:app\/Modules|modules)\/([^/]+)/i);
333
- if (match && match[1]) modulesSet.add(match[1]);
334
- }
335
- } else if (projectInfo.type === "typescript") {
336
- const controllers = normalizedFiles.filter((f) => /(?:^src\/.*(?:\.|\/)controller\.(?:ts|js)$)/i.test(f));
337
- components["Controllers"] = {
338
- name: "Controllers",
339
- count: controllers.length,
340
- files: controllers,
341
- description: "HTTP request handlers and response dispatchers"
342
- };
343
- const services = normalizedFiles.filter((f) => /(?:^src\/.*(?:\.|\/)service\.(?:ts|js)$)/i.test(f));
344
- components["Services"] = {
345
- name: "Services",
346
- count: services.length,
347
- files: services,
348
- description: "Core domain logic and application workflows"
349
- };
350
- const repositories = normalizedFiles.filter((f) => /(?:^src\/.*(?:\.|\/)repository\.(?:ts|js)$)/i.test(f));
351
- components["Repositories"] = {
352
- name: "Repositories",
353
- count: repositories.length,
354
- files: repositories,
355
- description: "Data access abstraction and database queries"
356
- };
357
- const routes = normalizedFiles.filter((f) => /(?:^src\/.*(?:\.|\/)routes?\.(?:ts|js)$)/i.test(f));
358
- components["Routes"] = {
359
- name: "Routes",
360
- count: routes.length,
361
- files: routes,
362
- description: "API endpoint mappings and router handlers"
363
- };
364
- const dtos = normalizedFiles.filter((f) => /(?:^src\/.*(?:\.|\/)(?:dto|schema)\.(?:ts|js)$)/i.test(f));
365
- components["DTOs & Schemas"] = {
366
- name: "DTOs & Schemas",
367
- count: dtos.length,
368
- files: dtos,
369
- description: "Data transfer objects and request validation schemas"
370
- };
371
- const models = normalizedFiles.filter(
372
- (f) => /(?:^src\/.*(?:\.|\/)(?:model|entity)\.(?:ts|js)$|^prisma\/schema\.prisma$)/i.test(f)
373
- );
374
- components["Models & Entities"] = {
375
- name: "Models & Entities",
376
- count: models.length,
377
- files: models,
378
- description: "Database schema definitions and entity declarations"
379
- };
380
- const componentsFiles = normalizedFiles.filter((f) => /^src\/(?:components|ui)\/.*\.(?:tsx|jsx|vue|ts|js)$/i.test(f));
381
- if (componentsFiles.length > 0) {
382
- components["UI Components"] = {
383
- name: "UI Components",
384
- count: componentsFiles.length,
385
- files: componentsFiles,
386
- description: "Reusable user interface views and components"
387
- };
388
- }
389
- const moduleFiles = normalizedFiles.filter((f) => /^src\/modules\/([^/]+)/i.test(f));
390
- for (const mf of moduleFiles) {
391
- const match = mf.match(/^src\/modules\/([^/]+)/i);
392
- if (match && match[1]) modulesSet.add(match[1]);
393
- }
394
- } else {
395
- const srcFiles = normalizedFiles.filter((f) => /^src\//i.test(f));
396
- const testFiles = normalizedFiles.filter((f) => /(?:test|spec)\.[a-z]+$/i.test(f));
397
- components["Source Files"] = {
398
- name: "Source Files",
399
- count: srcFiles.length,
400
- files: srcFiles,
401
- description: "Main application source code"
402
- };
403
- components["Test Files"] = {
404
- name: "Test Files",
405
- count: testFiles.length,
406
- files: testFiles,
407
- description: "Automated test suites and specifications"
408
- };
409
- }
410
- const directoryTree = buildCleanDirectoryTree(normalizedFiles, 3);
411
- const scanDurationMs = Date.now() - startTime;
412
- return {
413
- projectInfo,
414
- totalFilesScanned: normalizedFiles.length,
415
- components,
416
- modules: Array.from(modulesSet).sort(),
417
- directoryTree,
418
- scanDurationMs
419
- };
420
- }
421
-
422
- // src/generator/markdown.ts
423
- function generateMermaidDiagram(scanResult) {
424
- const { projectInfo, components, modules } = scanResult;
425
- if (projectInfo.type === "laravel") {
426
- const hasFilament = projectInfo.laravel?.hasFilament || (components["Filament Resources"]?.count ?? 0) > 0;
427
- const hasActions = (components["Actions"]?.count ?? 0) > 0;
428
- const hasServices = (components["Services"]?.count ?? 0) > 0;
429
- let diagram = `flowchart TD
430
- %% Styling Definitions
431
- classDef client fill:#E0F2FE,stroke:#0284C7,stroke-width:2px,color:#0369A1;
432
- classDef routing fill:#FEF3C7,stroke:#D97706,stroke-width:2px,color:#B45309;
433
- classDef controller fill:#DCFCE7,stroke:#16A34A,stroke-width:2px,color:#15803D;
434
- classDef service fill:#F3E8FF,stroke:#9333EA,stroke-width:2px,color:#7E22CE;
435
- classDef data fill:#FEE2E2,stroke:#DC2626,stroke-width:2px,color:#B91C1C;
436
- classDef db fill:#F1F5F9,stroke:#475569,stroke-width:2px,color:#334155;
437
-
438
- subgraph Layer1 ["1. Client & Entrypoints"]
439
- Client["Client / Browser / API Consumer"]:::client
440
- Routes["HTTP Routes<br/>(routes/web.php, routes/api.php)"]:::routing
441
- end
442
-
443
- subgraph Layer2 ["2. Request Pipeline & Security"]
444
- Middleware["Middleware Pipeline<br/>(Auth, CSRF, Rate Limiting)"]:::routing
445
- FormRequests["Form Requests<br/>(Validation & Authorization)"]:::routing
446
- end
447
-
448
- subgraph Layer3 ["3. Controllers & Presentation"]
449
- Controllers["HTTP Controllers<br/>(app/Http/Controllers)"]:::controller
450
- `;
451
- if (hasFilament) {
452
- diagram += ` Filament["Filament Admin Panel<br/>(app/Filament/Resources)"]:::controller
453
- `;
454
- }
455
- diagram += ` end
456
-
457
- subgraph Layer4 ["4. Domain & Business Logic"]
458
- `;
459
- if (hasServices || !hasActions) {
460
- diagram += ` Services["Service Layer<br/>(app/Services)"]:::service
461
- `;
462
- }
463
- if (hasActions) {
464
- diagram += ` Actions["Action Classes<br/>(app/Actions)"]:::service
465
- `;
466
- }
467
- diagram += ` end
468
-
469
- subgraph Layer5 ["5. Data Persistence & Storage"]
470
- Models["Eloquent Models<br/>(app/Models)"]:::data
471
- Database[("Database<br/>(MySQL / Postgres / SQLite)")]:::db
472
- Migrations["Database Migrations<br/>(database/migrations)"]:::data
473
- end
474
-
475
- Client --> Routes
476
- Routes --> Middleware
477
- Middleware --> FormRequests
478
- FormRequests --> Controllers
479
- `;
480
- if (hasServices && hasActions) {
481
- diagram += ` Controllers --> Services
482
- Controllers --> Actions
483
- Services --> Models
484
- Actions --> Models
485
- `;
486
- } else if (hasActions) {
487
- diagram += ` Controllers --> Actions
488
- Actions --> Models
489
- `;
490
- } else {
491
- diagram += ` Controllers --> Services
492
- Services --> Models
493
- `;
494
- }
495
- if (hasFilament) {
496
- diagram += ` Client --> Filament
497
- Filament --> Models
498
- `;
499
- }
500
- diagram += ` Models --> Database
501
- Migrations -.-> Database
502
- `;
503
- if (modules.length > 0) {
504
- diagram += `
505
- subgraph ModulesLayer ["\u{1F4E6} Detected Modules (${modules.length})"]
506
- `;
507
- modules.slice(0, 6).forEach((mod, idx) => {
508
- diagram += ` Mod_${idx}["${mod} Module"]:::service
509
- `;
510
- });
511
- if (modules.length > 6) {
512
- diagram += ` Mod_More["...and ${modules.length - 6} more modules"]:::service
513
- `;
514
- }
515
- diagram += ` end
516
- `;
517
- }
518
- return diagram;
519
- }
520
- if (projectInfo.type === "typescript") {
521
- const fw = projectInfo.typescript?.framework || "Express";
522
- const orm = projectInfo.typescript?.hasPrisma ? "Prisma" : projectInfo.typescript?.hasTypeORM ? "TypeORM" : "ORM";
523
- let diagram = `flowchart TD
524
- %% Styling Definitions
525
- classDef client fill:#E0F2FE,stroke:#0284C7,stroke-width:2px,color:#0369A1;
526
- classDef routing fill:#FEF3C7,stroke:#D97706,stroke-width:2px,color:#B45309;
527
- classDef controller fill:#DCFCE7,stroke:#16A34A,stroke-width:2px,color:#15803D;
528
- classDef service fill:#F3E8FF,stroke:#9333EA,stroke-width:2px,color:#7E22CE;
529
- classDef data fill:#FEE2E2,stroke:#DC2626,stroke-width:2px,color:#B91C1C;
530
- classDef db fill:#F1F5F9,stroke:#475569,stroke-width:2px,color:#334155;
531
-
532
- subgraph Layer1 ["1. Client & API Gateway"]
533
- Client["Frontend / API Client"]:::client
534
- Router["Router & Endpoints<br/>(${fw} Routes)"]:::routing
535
- end
536
-
537
- subgraph Layer2 ["2. Validation & Guards"]
538
- Middleware["Auth & Error Middleware"]:::routing
539
- DTOs["DTOs & Schemas<br/>(Input Validation)"]:::routing
540
- end
541
-
542
- subgraph Layer3 ["3. Controllers"]
543
- Controllers["Controllers<br/>(Request Handling)"]:::controller
544
- end
545
-
546
- subgraph Layer4 ["4. Application Services"]
547
- Services["Domain Services<br/>(Business Logic)"]:::service
548
- end
549
-
550
- subgraph Layer5 ["5. Data Layer & Storage"]
551
- Repositories["Repositories / ${orm}<br/>(Data Access Layer)"]:::data
552
- Database[("Database<br/>(SQL / NoSQL)")]:::db
553
- end
554
-
555
- Client --> Router
556
- Router --> Middleware
557
- Middleware --> DTOs
558
- DTOs --> Controllers
559
- Controllers --> Services
560
- Services --> Repositories
561
- Repositories --> Database
562
- `;
563
- if (modules.length > 0) {
564
- diagram += `
565
- subgraph ModulesLayer ["\u{1F4E6} Detected Feature Modules (${modules.length})"]
566
- `;
567
- modules.slice(0, 6).forEach((mod, idx) => {
568
- diagram += ` Mod_${idx}["${mod} Module"]:::service
569
- `;
570
- });
571
- if (modules.length > 6) {
572
- diagram += ` Mod_More["...and ${modules.length - 6} more modules"]:::service
573
- `;
574
- }
575
- diagram += ` end
576
- `;
577
- }
578
- return diagram;
579
- }
580
- return `flowchart TD
581
- classDef client fill:#E0F2FE,stroke:#0284C7,stroke-width:2px,color:#0369A1;
582
- classDef core fill:#DCFCE7,stroke:#16A34A,stroke-width:2px,color:#15803D;
583
- classDef test fill:#FEF3C7,stroke:#D97706,stroke-width:2px,color:#B45309;
584
-
585
- subgraph App ["Application Architecture"]
586
- Entry["Entrypoint / CLI"]:::client
587
- Source["Source Core Logic (src/)"]:::core
588
- Tests["Test Suites"]:::test
589
- end
590
-
591
- Entry --> Source
592
- Tests -.-> Source
593
- `;
594
- }
595
- function generateArchitectureMarkdown(scanResult) {
596
- const { projectInfo, totalFilesScanned, components, modules, directoryTree, scanDurationMs } = scanResult;
597
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19) + " UTC";
598
- const mermaid = generateMermaidDiagram(scanResult);
599
- const componentRows = Object.entries(components).map(([key, comp]) => {
600
- const sampleFiles = comp.files.slice(0, 3).map((f) => `\`${f}\``).join(", ");
601
- const moreSuffix = comp.files.length > 3 ? ` *(+${comp.files.length - 3} more)*` : "";
602
- return `| **${comp.name}** | \`${comp.count}\` | ${comp.description} | ${sampleFiles || "-"} ${moreSuffix} |`;
603
- }).join("\n");
604
- let modulesSection = "";
605
- if (modules.length > 0) {
606
- modulesSection = `
607
- ## \u{1F4E6} Detected Modules & Domains (${modules.length})
608
-
609
- This project organizes features into modular domains:
610
-
611
- ${modules.map((m) => `- **\`${m}\`**`).join("\n")}
612
- `;
613
- }
614
- return `# \u{1F3DB}\uFE0F Architecture Overview: ${projectInfo.name}
615
-
616
- > Auto-generated by **[LaraTS CLI](https://github.com/SidiqFatuh187/Larats)** on \`${timestamp}\`.
617
- > **100% Local & Safe:** Generated entirely in offline mode via local filesystem metadata analysis. Zero data transmitted.
618
-
619
- ---
620
-
621
- ## \u{1F4CC} Project Summary
622
-
623
- | Attribute | Details |
624
- | :--- | :--- |
625
- | **Project Name** | \`${projectInfo.name}\` |
626
- | **Project Type** | **${projectInfo.type.toUpperCase()}** |
627
- | **Detected Stack** | \`${projectInfo.summary}\` |
628
- | **Scanned Files** | \`${totalFilesScanned}\` files indexed (safe ignore filter applied) |
629
- | **Scan Duration** | \`${scanDurationMs} ms\` |
630
-
631
- ---
632
-
633
- ## \u{1F4CA} Architectural Components Statistics
634
-
635
- | Component Layer | Count | Description | Sample Files |
636
- | :--- | :---: | :--- | :--- |
637
- ${componentRows || "| *No architectural components identified* | `0` | - | - |"}
638
- ${modulesSection}
639
- ---
640
-
641
- ## \u{1F5FA}\uFE0F Visual Architecture Flowchart
642
-
643
- Visual representation of the project layers and request lifecycle rendered with [Mermaid.js](https://mermaid.js.org/):
644
-
645
- \`\`\`mermaid
646
- ${mermaid}
647
- \`\`\`
648
-
649
- ---
650
-
651
- ## \u{1F332} Directory Structure Tree
652
-
653
- Clean, high-level directory tree showing key folders (ignoring build artifacts, vendor files, and sensitive keys):
654
-
655
- \`\`\`text
656
- ${directoryTree || "(Empty directory structure)"}
657
- \`\`\`
658
-
659
- ---
660
-
661
- ## \u{1F512} Security & Privacy Notice
662
-
663
- This architectural map was produced with strict privacy rules:
664
- - **Zero Network Transmission:** Executed locally without telemetry or outbound communication.
665
- - **Safe Exclusion Filter:** Ignored \`node_modules\`, \`vendor\`, \`.env*\`, key files, credentials, and cache folders.
666
- - **Metadata Only:** Scanned file names and relative paths only; no secrets or private business contents were extracted.
667
-
668
- *Generated with \u2764\uFE0F by LaraTS CLI.*
669
- `;
670
- }
671
-
672
- // src/generator/scaffolder.ts
673
- var import_node_fs2 = __toESM(require("fs"), 1);
674
- var import_node_path2 = __toESM(require("path"), 1);
675
-
676
- // src/templates/laravel.ts
677
- function getNaming(input) {
678
- const words = input.trim().replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[^a-zA-Z0-9]/g, " ").split(/\s+/).filter(Boolean);
679
- if (words.length === 0) {
680
- words.push("SampleModule");
681
- }
682
- const pascal = words.map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
683
- const camel = pascal.charAt(0).toLowerCase() + pascal.slice(1);
684
- const snake = words.map((w) => w.toLowerCase()).join("_");
685
- const kebab = words.map((w) => w.toLowerCase()).join("-");
686
- const pluralSuffix = snake.endsWith("s") || snake.endsWith("ch") || snake.endsWith("sh") ? "es" : "s";
687
- const pluralSnake = snake + pluralSuffix;
688
- const pluralPascal = pascal + pluralSuffix;
689
- return { pascal, camel, kebab, snake, pluralSnake, pluralPascal };
690
- }
691
- function laravelModelTemplate(names, namespace = "App\\Models") {
692
- return `<?php
693
-
694
- namespace ${namespace};
695
-
696
- use IlluminateDatabaseEloquentFactoriesHasFactory;
697
- use IlluminateDatabaseEloquentModel;
698
-
699
- class ${names.pascal} extends Model
700
- {
701
- use HasFactory;
702
-
703
- /**
704
- * The table associated with the model.
705
- *
706
- * @var string
707
- */
708
- protected $table = '${names.pluralSnake}';
709
-
710
- /**
711
- * The attributes that are mass assignable.
712
- *
713
- * @var list<string>
714
- */
715
- protected $fillable = [
716
- 'name',
717
- 'status',
718
- 'metadata',
719
- ];
720
-
721
- /**
722
- * Get the attributes that should be cast.
723
- *
724
- * @return array<string, string>
725
- */
726
- protected function casts(): array
727
- {
728
- return [
729
- 'metadata' => 'array',
730
- 'created_at' => 'datetime',
731
- 'updated_at' => 'datetime',
732
- ];
733
- }
734
- }
735
- `;
736
- }
737
- function laravelControllerTemplate(names, options = {}) {
738
- const namespace = options.namespace || "App\\Http\\Controllers";
739
- const modelNamespace = options.modelNamespace || "App\\Models";
740
- const serviceNamespace = options.serviceNamespace || "App\\Services";
741
- const requestNamespace = options.requestNamespace || "App\\Http\\Requests";
742
- return `<?php
743
-
744
- namespace ${namespace};
745
-
746
- use ${modelNamespace}\\${names.pascal};
747
- use ${serviceNamespace}\\${names.pascal}Service;
748
- use ${requestNamespace}\\${names.pascal}Request;
749
- use IlluminateHttpJsonResponse;
750
- use IlluminateHttpRequest;
751
-
752
- class ${names.pascal}Controller extends Controller
753
- {
754
- public function __construct(
755
- protected ${names.pascal}Service $${names.camel}Service
756
- ) {}
757
-
758
- /**
759
- * Display a listing of ${names.pascal} resources.
760
- */
761
- public function index(Request $request): JsonResponse
762
- {
763
- $items = $this->${names.camel}Service->getAll($request->all());
764
-
765
- return response()->json([
766
- 'status' => 'success',
767
- 'data' => $items,
768
- ]);
769
- }
770
-
771
- /**
772
- * Display the specified ${names.pascal} resource.
773
- */
774
- public function show(int $id): JsonResponse
775
- {
776
- $item = $this->${names.camel}Service->getById($id);
777
-
778
- return response()->json([
779
- 'status' => 'success',
780
- 'data' => $item,
781
- ]);
782
- }
783
-
784
- /**
785
- * Store a newly created ${names.pascal} in storage.
786
- */
787
- public function store(${names.pascal}Request $request): JsonResponse
788
- {
789
- $item = $this->${names.camel}Service->create($request->validated());
790
-
791
- return response()->json([
792
- 'status' => 'success',
793
- 'message' => '${names.pascal} created successfully.',
794
- 'data' => $item,
795
- ], 201);
796
- }
797
-
798
- /**
799
- * Update the specified ${names.pascal} in storage.
800
- */
801
- public function update(${names.pascal}Request $request, int $id): JsonResponse
802
- {
803
- $item = $this->${names.camel}Service->update($id, $request->validated());
804
-
805
- return response()->json([
806
- 'status' => 'success',
807
- 'message' => '${names.pascal} updated successfully.',
808
- 'data' => $item,
809
- ]);
810
- }
811
-
812
- /**
813
- * Remove the specified ${names.pascal} from storage.
814
- */
815
- public function destroy(int $id): JsonResponse
816
- {
817
- $this->${names.camel}Service->delete($id);
818
-
819
- return response()->json([
820
- 'status' => 'success',
821
- 'message' => '${names.pascal} deleted successfully.',
822
- ]);
823
- }
824
- }
825
- `;
826
- }
827
- function laravelServiceTemplate(names, options = {}) {
828
- const namespace = options.namespace || "App\\Services";
829
- const modelNamespace = options.modelNamespace || "App\\Models";
830
- return `<?php
831
-
832
- namespace ${namespace};
833
-
834
- use ${modelNamespace}\\${names.pascal};
835
- use IlluminateDatabaseEloquentCollection;
836
-
837
- class ${names.pascal}Service
838
- {
839
- /**
840
- * Retrieve all ${names.pluralPascal}.
841
- */
842
- public function getAll(array $filters = []): Collection
843
- {
844
- return ${names.pascal}::query()
845
- ->latest()
846
- ->get();
847
- }
848
-
849
- /**
850
- * Find ${names.pascal} by primary key.
851
- */
852
- public function getById(int $id): ${names.pascal}
853
- {
854
- return ${names.pascal}::findOrFail($id);
855
- }
856
-
857
- /**
858
- * Create a new ${names.pascal}.
859
- */
860
- public function create(array $data): ${names.pascal}
861
- {
862
- return ${names.pascal}::create($data);
863
- }
864
-
865
- /**
866
- * Update an existing ${names.pascal}.
867
- */
868
- public function update(int $id, array $data): ${names.pascal}
869
- {
870
- $item = $this->getById($id);
871
- $item->update($data);
872
-
873
- return $item;
874
- }
875
-
876
- /**
877
- * Delete a ${names.pascal}.
878
- */
879
- public function delete(int $id): bool
880
- {
881
- $item = $this->getById($id);
882
-
883
- return (bool) $item->delete();
884
- }
885
- }
886
- `;
887
- }
888
- function laravelActionTemplate(names, options = {}) {
889
- const namespace = options.namespace || "App\\Actions";
890
- const modelNamespace = options.modelNamespace || "App\\Models";
891
- return `<?php
892
-
893
- namespace ${namespace};
894
-
895
- use ${modelNamespace}\\${names.pascal};
896
-
897
- class Create${names.pascal}Action
898
- {
899
- /**
900
- * Execute the ${names.pascal} creation workflow.
901
- */
902
- public function execute(array $data): ${names.pascal}
903
- {
904
- return ${names.pascal}::create($data);
905
- }
906
- }
907
- `;
908
- }
909
- function laravelRequestTemplate(names, namespace = "App\\Http\\Requests") {
910
- return `<?php
911
-
912
- namespace ${namespace};
913
-
914
- use IlluminateFoundationHttpFormRequest;
915
-
916
- class ${names.pascal}Request extends FormRequest
917
- {
918
- /**
919
- * Determine if the user is authorized to make this request.
920
- */
921
- public function authorize(): bool
922
- {
923
- return true;
924
- }
925
-
926
- /**
927
- * Get the validation rules that apply to the request.
928
- *
929
- * @return array<string, list<string>>
930
- */
931
- public function rules(): array
932
- {
933
- return [
934
- 'name' => ['required', 'string', 'max:255'],
935
- 'status' => ['nullable', 'string', 'in:draft,active,archived'],
936
- 'metadata' => ['nullable', 'array'],
937
- ];
938
- }
939
-
940
- /**
941
- * Get custom error messages for validation rules.
942
- *
943
- * @return array<string, string>
944
- */
945
- public function messages(): array
946
- {
947
- return [
948
- 'name.required' => 'The ${names.camel} name field is required.',
949
- ];
950
- }
951
- }
952
- `;
953
- }
954
- function laravelMigrationTemplate(names) {
955
- return `<?php
956
-
957
- use IlluminateDatabaseMigrationsMigration;
958
- use IlluminateDatabaseSchemaBlueprint;
959
- use IlluminateSupportFacadesSchema;
960
-
961
- return new class extends Migration
962
- {
963
- /**
964
- * Run the migrations.
965
- */
966
- public function up(): void
967
- {
968
- Schema::create('${names.pluralSnake}', function (Blueprint $table) {
969
- $table->id();
970
- $table->string('name');
971
- $table->string('status')->default('draft');
972
- $table->json('metadata')->nullable();
973
- $table->timestamps();
974
- });
975
- }
976
-
977
- /**
978
- * Reverse the migrations.
979
- */
980
- public function down(): void
981
- {
982
- Schema::dropIfExists('${names.pluralSnake}');
983
- }
984
- };
985
- `;
986
- }
987
-
988
- // src/templates/typescript.ts
989
- function tsControllerTemplate(names, isModular = true) {
990
- const serviceImport = isModular ? `./${names.kebab}.service.js` : `../services/${names.kebab}.service.js`;
991
- const dtoImport = isModular ? `./${names.kebab}.dto.js` : `../dtos/${names.kebab}.dto.js`;
992
- return `import type { Request, Response, NextFunction } from 'express';
993
- import { ${names.pascal}Service } from '${serviceImport}';
994
- import type { Create${names.pascal}Dto, Update${names.pascal}Dto } from '${dtoImport}';
995
-
996
- export class ${names.pascal}Controller {
997
- constructor(private readonly ${names.camel}Service: ${names.pascal}Service = new ${names.pascal}Service()) {}
998
-
999
- /**
1000
- * GET /api/${names.kebab}
1001
- */
1002
- getAll = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
1003
- try {
1004
- const items = await this.${names.camel}Service.findAll(req.query);
1005
- res.status(200).json({ status: 'success', data: items });
1006
- } catch (error) {
1007
- next(error);
1008
- }
1009
- };
1010
-
1011
- /**
1012
- * GET /api/${names.kebab}/:id
1013
- */
1014
- getById = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
1015
- try {
1016
- const { id } = req.params;
1017
- const item = await this.${names.camel}Service.findById(id);
1018
- if (!item) {
1019
- res.status(404).json({ status: 'fail', message: '${names.pascal} not found' });
1020
- return;
1021
- }
1022
- res.status(200).json({ status: 'success', data: item });
1023
- } catch (error) {
1024
- next(error);
1025
- }
1026
- };
1027
-
1028
- /**
1029
- * POST /api/${names.kebab}
1030
- */
1031
- create = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
1032
- try {
1033
- const payload: Create${names.pascal}Dto = req.body;
1034
- const created = await this.${names.camel}Service.create(payload);
1035
- res.status(201).json({ status: 'success', data: created });
1036
- } catch (error) {
1037
- next(error);
1038
- }
1039
- };
1040
-
1041
- /**
1042
- * PUT /api/${names.kebab}/:id
1043
- */
1044
- update = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
1045
- try {
1046
- const { id } = req.params;
1047
- const payload: Update${names.pascal}Dto = req.body;
1048
- const updated = await this.${names.camel}Service.update(id, payload);
1049
- res.status(200).json({ status: 'success', data: updated });
1050
- } catch (error) {
1051
- next(error);
1052
- }
1053
- };
1054
-
1055
- /**
1056
- * DELETE /api/${names.kebab}/:id
1057
- */
1058
- delete = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
1059
- try {
1060
- const { id } = req.params;
1061
- await this.${names.camel}Service.delete(id);
1062
- res.status(200).json({ status: 'success', message: '${names.pascal} deleted successfully' });
1063
- } catch (error) {
1064
- next(error);
1065
- }
1066
- };
1067
- }
1068
- `;
1069
- }
1070
- function tsServiceTemplate(names, isModular = true) {
1071
- const repoImport = isModular ? `./${names.kebab}.repository.js` : `../repositories/${names.kebab}.repository.js`;
1072
- const dtoImport = isModular ? `./${names.kebab}.dto.js` : `../dtos/${names.kebab}.dto.js`;
1073
- return `import { ${names.pascal}Repository } from '${repoImport}';
1074
- import type { ${names.pascal}Entity, Create${names.pascal}Dto, Update${names.pascal}Dto } from '${dtoImport}';
1075
-
1076
- export class ${names.pascal}Service {
1077
- constructor(private readonly ${names.camel}Repository: ${names.pascal}Repository = new ${names.pascal}Repository()) {}
1078
-
1079
- async findAll(query: Record<string, any> = {}): Promise<${names.pascal}Entity[]> {
1080
- return this.${names.camel}Repository.findAll(query);
1081
- }
1082
-
1083
- async findById(id: string): Promise<${names.pascal}Entity | null> {
1084
- return this.${names.camel}Repository.findById(id);
1085
- }
1086
-
1087
- async create(data: Create${names.pascal}Dto): Promise<${names.pascal}Entity> {
1088
- return this.${names.camel}Repository.create(data);
1089
- }
1090
-
1091
- async update(id: string, data: Update${names.pascal}Dto): Promise<${names.pascal}Entity> {
1092
- return this.${names.camel}Repository.update(id, data);
1093
- }
1094
-
1095
- async delete(id: string): Promise<boolean> {
1096
- return this.${names.camel}Repository.delete(id);
1097
- }
1098
- }
1099
- `;
1100
- }
1101
- function tsRepositoryTemplate(names, isModular = true) {
1102
- const dtoImport = isModular ? `./${names.kebab}.dto.js` : `../dtos/${names.kebab}.dto.js`;
1103
- return `import type { ${names.pascal}Entity, Create${names.pascal}Dto, Update${names.pascal}Dto } from '${dtoImport}';
1104
-
1105
- export interface I${names.pascal}Repository {
1106
- findAll(query?: Record<string, any>): Promise<${names.pascal}Entity[]>;
1107
- findById(id: string): Promise<${names.pascal}Entity | null>;
1108
- create(data: Create${names.pascal}Dto): Promise<${names.pascal}Entity>;
1109
- update(id: string, data: Update${names.pascal}Dto): Promise<${names.pascal}Entity>;
1110
- delete(id: string): Promise<boolean>;
1111
- }
1112
-
1113
- export class ${names.pascal}Repository implements I${names.pascal}Repository {
1114
- // In-memory store fallback. Replace with Prisma / TypeORM / Drizzle client calls.
1115
- private storage: Map<string, ${names.pascal}Entity> = new Map();
1116
-
1117
- async findAll(_query?: Record<string, any>): Promise<${names.pascal}Entity[]> {
1118
- return Array.from(this.storage.values());
1119
- }
1120
-
1121
- async findById(id: string): Promise<${names.pascal}Entity | null> {
1122
- return this.storage.get(id) || null;
1123
- }
1124
-
1125
- async create(data: Create${names.pascal}Dto): Promise<${names.pascal}Entity> {
1126
- const id = (Math.random() * 100000).toFixed(0);
1127
- const newRecord: ${names.pascal}Entity = {
1128
- id,
1129
- ...data,
1130
- status: data.status || 'draft',
1131
- createdAt: new Date(),
1132
- updatedAt: new Date(),
1133
- };
1134
- this.storage.set(id, newRecord);
1135
- return newRecord;
1136
- }
1137
-
1138
- async update(id: string, data: Update${names.pascal}Dto): Promise<${names.pascal}Entity> {
1139
- const existing = await this.findById(id);
1140
- if (!existing) {
1141
- throw new Error(\`${names.pascal} with id \${id} not found\`);
1142
- }
1143
- const updated: ${names.pascal}Entity = {
1144
- ...existing,
1145
- ...data,
1146
- updatedAt: new Date(),
1147
- };
1148
- this.storage.set(id, updated);
1149
- return updated;
1150
- }
1151
-
1152
- async delete(id: string): Promise<boolean> {
1153
- return this.storage.delete(id);
1154
- }
1155
- }
1156
- `;
1157
- }
1158
- function tsRoutesTemplate(names, isModular = true) {
1159
- const controllerImport = isModular ? `./${names.kebab}.controller.js` : `../controllers/${names.kebab}.controller.js`;
1160
- return `import { Router } from 'express';
1161
- import { ${names.pascal}Controller } from '${controllerImport}';
1162
-
1163
- const router = Router();
1164
- const controller = new ${names.pascal}Controller();
1165
-
1166
- /**
1167
- * Route declarations for ${names.pascal}
1168
- */
1169
- router.get('/', controller.getAll);
1170
- router.get('/:id', controller.getById);
1171
- router.post('/', controller.create);
1172
- router.put('/:id', controller.update);
1173
- router.delete('/:id', controller.delete);
1174
-
1175
- export const ${names.camel}Routes = router;
1176
- export default router;
1177
- `;
1178
- }
1179
- function tsDtoTemplate(names) {
1180
- return `export interface ${names.pascal}Entity {
1181
- id: string;
1182
- name: string;
1183
- status: 'draft' | 'active' | 'archived';
1184
- description?: string;
1185
- metadata?: Record<string, unknown>;
1186
- createdAt: Date;
1187
- updatedAt: Date;
1188
- }
1189
-
1190
- export interface Create${names.pascal}Dto {
1191
- name: string;
1192
- status?: 'draft' | 'active' | 'archived';
1193
- description?: string;
1194
- metadata?: Record<string, unknown>;
1195
- }
1196
-
1197
- export interface Update${names.pascal}Dto {
1198
- name?: string;
1199
- status?: 'draft' | 'active' | 'archived';
1200
- description?: string;
1201
- metadata?: Record<string, unknown>;
1202
- }
1203
- `;
1204
- }
1205
-
1206
- // src/generator/scaffolder.ts
1207
- function getMigrationTimestamp() {
1208
- const now = /* @__PURE__ */ new Date();
1209
- const YYYY = now.getFullYear();
1210
- const MM = String(now.getMonth() + 1).padStart(2, "0");
1211
- const DD = String(now.getDate()).padStart(2, "0");
1212
- const hh = String(now.getHours()).padStart(2, "0");
1213
- const mm = String(now.getMinutes()).padStart(2, "0");
1214
- const ss = String(now.getSeconds()).padStart(2, "0");
1215
- return `${YYYY}_${MM}_${DD}_${hh}${mm}${ss}`;
1216
- }
1217
- async function scaffoldModule(projectInfo, options) {
1218
- const root = projectInfo.rootPath;
1219
- const names = getNaming(options.moduleName);
1220
- const filesToGenerate = [];
1221
- const isModular = options.isModular ?? (projectInfo.type === "laravel" ? Boolean(projectInfo.laravel?.isModular) : projectInfo.type === "typescript" ? Boolean(projectInfo.typescript?.isModular) : true);
1222
- if (projectInfo.type === "laravel") {
1223
- const selected = new Set(options.components);
1224
- if (isModular) {
1225
- const baseDir = `app/Modules/${names.pascal}`;
1226
- const baseNamespace = `App\\Modules\\${names.pascal}`;
1227
- if (selected.has("model")) {
1228
- filesToGenerate.push({
1229
- relativePath: `${baseDir}/Models/${names.pascal}.php`,
1230
- content: laravelModelTemplate(names, `${baseNamespace}\\Models`)
1231
- });
1232
- }
1233
- if (selected.has("controller")) {
1234
- filesToGenerate.push({
1235
- relativePath: `${baseDir}/Controllers/${names.pascal}Controller.php`,
1236
- content: laravelControllerTemplate(names, {
1237
- namespace: `${baseNamespace}\\Controllers`,
1238
- modelNamespace: `${baseNamespace}\\Models`,
1239
- serviceNamespace: `${baseNamespace}\\Services`,
1240
- requestNamespace: `${baseNamespace}\\Requests`
1241
- })
1242
- });
1243
- }
1244
- if (selected.has("service")) {
1245
- filesToGenerate.push({
1246
- relativePath: `${baseDir}/Services/${names.pascal}Service.php`,
1247
- content: laravelServiceTemplate(names, {
1248
- namespace: `${baseNamespace}\\Services`,
1249
- modelNamespace: `${baseNamespace}\\Models`
1250
- })
1251
- });
1252
- }
1253
- if (selected.has("action")) {
1254
- filesToGenerate.push({
1255
- relativePath: `${baseDir}/Actions/Create${names.pascal}Action.php`,
1256
- content: laravelActionTemplate(names, {
1257
- namespace: `${baseNamespace}\\Actions`,
1258
- modelNamespace: `${baseNamespace}\\Models`
1259
- })
1260
- });
1261
- }
1262
- if (selected.has("request")) {
1263
- filesToGenerate.push({
1264
- relativePath: `${baseDir}/Requests/${names.pascal}Request.php`,
1265
- content: laravelRequestTemplate(names, `${baseNamespace}\\Requests`)
1266
- });
1267
- }
1268
- if (selected.has("migration")) {
1269
- const timestamp = getMigrationTimestamp();
1270
- filesToGenerate.push({
1271
- relativePath: `database/migrations/${timestamp}_create_${names.pluralSnake}_table.php`,
1272
- content: laravelMigrationTemplate(names)
1273
- });
1274
- }
1275
- } else {
1276
- if (selected.has("model")) {
1277
- filesToGenerate.push({
1278
- relativePath: `app/Models/${names.pascal}.php`,
1279
- content: laravelModelTemplate(names, "App\\Models")
1280
- });
1281
- }
1282
- if (selected.has("controller")) {
1283
- filesToGenerate.push({
1284
- relativePath: `app/Http/Controllers/${names.pascal}Controller.php`,
1285
- content: laravelControllerTemplate(names)
1286
- });
1287
- }
1288
- if (selected.has("service")) {
1289
- filesToGenerate.push({
1290
- relativePath: `app/Services/${names.pascal}Service.php`,
1291
- content: laravelServiceTemplate(names)
1292
- });
1293
- }
1294
- if (selected.has("action")) {
1295
- filesToGenerate.push({
1296
- relativePath: `app/Actions/Create${names.pascal}Action.php`,
1297
- content: laravelActionTemplate(names)
1298
- });
1299
- }
1300
- if (selected.has("request")) {
1301
- filesToGenerate.push({
1302
- relativePath: `app/Http/Requests/${names.pascal}Request.php`,
1303
- content: laravelRequestTemplate(names)
1304
- });
1305
- }
1306
- if (selected.has("migration")) {
1307
- const timestamp = getMigrationTimestamp();
1308
- filesToGenerate.push({
1309
- relativePath: `database/migrations/${timestamp}_create_${names.pluralSnake}_table.php`,
1310
- content: laravelMigrationTemplate(names)
1311
- });
1312
- }
1313
- }
1314
- } else {
1315
- const selected = new Set(options.components);
1316
- const srcDir = projectInfo.typescript?.srcDir || "src";
1317
- if (isModular) {
1318
- const moduleFolder = `${srcDir}/modules/${names.kebab}`;
1319
- if (selected.has("controller")) {
1320
- filesToGenerate.push({
1321
- relativePath: `${moduleFolder}/${names.kebab}.controller.ts`,
1322
- content: tsControllerTemplate(names, true)
1323
- });
1324
- }
1325
- if (selected.has("service")) {
1326
- filesToGenerate.push({
1327
- relativePath: `${moduleFolder}/${names.kebab}.service.ts`,
1328
- content: tsServiceTemplate(names, true)
1329
- });
1330
- }
1331
- if (selected.has("repository")) {
1332
- filesToGenerate.push({
1333
- relativePath: `${moduleFolder}/${names.kebab}.repository.ts`,
1334
- content: tsRepositoryTemplate(names, true)
1335
- });
1336
- }
1337
- if (selected.has("route")) {
1338
- filesToGenerate.push({
1339
- relativePath: `${moduleFolder}/${names.kebab}.routes.ts`,
1340
- content: tsRoutesTemplate(names, true)
1341
- });
1342
- }
1343
- if (selected.has("dto")) {
1344
- filesToGenerate.push({
1345
- relativePath: `${moduleFolder}/${names.kebab}.dto.ts`,
1346
- content: tsDtoTemplate(names)
1347
- });
1348
- }
1349
- if (filesToGenerate.length > 1) {
1350
- const exports2 = [];
1351
- if (selected.has("controller")) exports2.push(`export * from './${names.kebab}.controller.js';`);
1352
- if (selected.has("service")) exports2.push(`export * from './${names.kebab}.service.js';`);
1353
- if (selected.has("repository")) exports2.push(`export * from './${names.kebab}.repository.js';`);
1354
- if (selected.has("route")) exports2.push(`export * from './${names.kebab}.routes.js';`);
1355
- if (selected.has("dto")) exports2.push(`export * from './${names.kebab}.dto.js';`);
1356
- filesToGenerate.push({
1357
- relativePath: `${moduleFolder}/index.ts`,
1358
- content: `${exports2.join("\n")}
1359
- `
1360
- });
1361
- }
1362
- } else {
1363
- if (selected.has("controller")) {
1364
- filesToGenerate.push({
1365
- relativePath: `${srcDir}/controllers/${names.kebab}.controller.ts`,
1366
- content: tsControllerTemplate(names, false)
1367
- });
1368
- }
1369
- if (selected.has("service")) {
1370
- filesToGenerate.push({
1371
- relativePath: `${srcDir}/services/${names.kebab}.service.ts`,
1372
- content: tsServiceTemplate(names, false)
1373
- });
1374
- }
1375
- if (selected.has("repository")) {
1376
- filesToGenerate.push({
1377
- relativePath: `${srcDir}/repositories/${names.kebab}.repository.ts`,
1378
- content: tsRepositoryTemplate(names, false)
1379
- });
1380
- }
1381
- if (selected.has("route")) {
1382
- filesToGenerate.push({
1383
- relativePath: `${srcDir}/routes/${names.kebab}.routes.ts`,
1384
- content: tsRoutesTemplate(names, false)
1385
- });
1386
- }
1387
- if (selected.has("dto")) {
1388
- filesToGenerate.push({
1389
- relativePath: `${srcDir}/dtos/${names.kebab}.dto.ts`,
1390
- content: tsDtoTemplate(names)
1391
- });
1392
- }
1393
- }
1394
- }
1395
- const results = [];
1396
- let createdCount = 0;
1397
- let skippedCount = 0;
1398
- let errorCount = 0;
1399
- for (const item of filesToGenerate) {
1400
- const fullPath = import_node_path2.default.resolve(root, item.relativePath);
1401
- if (import_node_fs2.default.existsSync(fullPath) && !options.forceOverwrite) {
1402
- results.push({
1403
- filePath: fullPath,
1404
- relativeFilePath: item.relativePath,
1405
- status: "skipped",
1406
- reason: "File already exists (overwrite protected)"
1407
- });
1408
- skippedCount++;
1409
- continue;
1410
- }
1411
- if (options.dryRun) {
1412
- results.push({
1413
- filePath: fullPath,
1414
- relativeFilePath: item.relativePath,
1415
- status: "created",
1416
- reason: "Dry run simulated"
1417
- });
1418
- createdCount++;
1419
- continue;
1420
- }
1421
- try {
1422
- const parentDir = import_node_path2.default.dirname(fullPath);
1423
- if (!import_node_fs2.default.existsSync(parentDir)) {
1424
- import_node_fs2.default.mkdirSync(parentDir, { recursive: true });
1425
- }
1426
- import_node_fs2.default.writeFileSync(fullPath, item.content, "utf-8");
1427
- results.push({
1428
- filePath: fullPath,
1429
- relativeFilePath: item.relativePath,
1430
- status: "created"
1431
- });
1432
- createdCount++;
1433
- } catch (err) {
1434
- results.push({
1435
- filePath: fullPath,
1436
- relativeFilePath: item.relativePath,
1437
- status: "error",
1438
- reason: err.message || String(err)
1439
- });
1440
- errorCount++;
1441
- }
1442
- }
1443
- return {
1444
- moduleName: names.pascal,
1445
- targetDir: root,
1446
- files: results,
1447
- createdCount,
1448
- skippedCount,
1449
- errorCount
1450
- };
1451
- }
1452
-
1453
- // src/index.ts
1454
- var VERSION = "1.0.1";
1455
- function showHelp() {
1456
- console.log(`
1457
- ${import_picocolors.default.bold(import_picocolors.default.cyan("LaraTS CLI"))} ${import_picocolors.default.gray(`v${VERSION}`)}
1458
- ${import_picocolors.default.gray("Smart Architecture Visualizer & Contextual Module Scaffolder")}
1459
-
1460
- ${import_picocolors.default.bold("USAGE:")}
1461
- ${import_picocolors.default.cyan("$")} npx larats [command] [options]
1462
- ${import_picocolors.default.cyan("$")} larats [command] [options]
1463
-
1464
- ${import_picocolors.default.bold("COMMANDS:")}
1465
- ${import_picocolors.default.green("scan")} Scan repository & generate ARCHITECTURE.md
1466
- ${import_picocolors.default.green("scaffold")}, ${import_picocolors.default.green("make")} [name] Scaffold a new module or feature with clean boilerplate
1467
- ${import_picocolors.default.green("info")} Show detected project type & architectural flavor
1468
- ${import_picocolors.default.green("help")} Display this help message
1469
-
1470
- ${import_picocolors.default.bold("OPTIONS:")}
1471
- ${import_picocolors.default.yellow("-o, --output")} <file> Output markdown path for scan (default: ARCHITECTURE.md)
1472
- ${import_picocolors.default.yellow("-m, --modular")} Enforce modular directory structure
1473
- ${import_picocolors.default.yellow("-s, --standard")} Enforce standard/layered directory structure
1474
- ${import_picocolors.default.yellow("--dry-run")} Simulate generation without writing files
1475
- ${import_picocolors.default.yellow("-f, --force")} Overwrite files if they already exist
1476
- ${import_picocolors.default.yellow("-v, --version")} Display CLI version
1477
- ${import_picocolors.default.yellow("-h, --help")} Display this help message
1478
-
1479
- ${import_picocolors.default.bold("EXAMPLES:")}
1480
- ${import_picocolors.default.cyan("$")} larats
1481
- ${import_picocolors.default.cyan("$")} larats scan
1482
- ${import_picocolors.default.cyan("$")} larats scan --output docs/ARCHITECTURE.md
1483
- ${import_picocolors.default.cyan("$")} larats scaffold Payment
1484
- ${import_picocolors.default.cyan("$")} larats make Order --modular
1485
- `);
1486
- }
1487
- function handleCancel() {
1488
- p.cancel(import_picocolors.default.yellow("Operation cancelled. See you next time! \u{1F44B}"));
1489
- process.exit(0);
1490
- }
1491
- async function runScan(projectInfo, outputPath = "ARCHITECTURE.md") {
1492
- const spinner2 = p.spinner();
1493
- spinner2.start("Scanning codebase structure & components (strict ignore list active)...");
1494
- try {
1495
- const scanResult = await scanProject(projectInfo);
1496
- spinner2.stop(import_picocolors.default.green(`Scan completed in ${scanResult.scanDurationMs}ms`));
1497
- const markdown = generateArchitectureMarkdown(scanResult);
1498
- const destination = import_node_path3.default.resolve(projectInfo.rootPath, outputPath);
1499
- const parentDir = import_node_path3.default.dirname(destination);
1500
- if (!import_node_fs3.default.existsSync(parentDir)) {
1501
- import_node_fs3.default.mkdirSync(parentDir, { recursive: true });
1502
- }
1503
- import_node_fs3.default.writeFileSync(destination, markdown, "utf-8");
1504
- p.note(
1505
- `${import_picocolors.default.bold("Scanned Files:")} ${scanResult.totalFilesScanned}
1506
- ${import_picocolors.default.bold("Components Identified:")} ${Object.keys(scanResult.components).length}
1507
- ${import_picocolors.default.bold("Modules Detected:")} ${scanResult.modules.length > 0 ? scanResult.modules.join(", ") : "None (monolithic structure)"}
1508
- ${import_picocolors.default.bold("Documentation:")} Saved to ${import_picocolors.default.cyan(import_node_path3.default.relative(projectInfo.rootPath, destination) || outputPath)}`,
1509
- import_picocolors.default.green("Architecture Map Generated! \u{1F5FA}\uFE0F")
1510
- );
1511
- p.outro(import_picocolors.default.cyan(`Open ${outputPath} to view your project structure & Mermaid diagram!`));
1512
- } catch (err) {
1513
- spinner2.stop(import_picocolors.default.red("Scan failed"));
1514
- p.log.error(err.message || String(err));
1515
- }
1516
- }
1517
- async function runScaffold(projectInfo, moduleNameArg, cliOptions = {}) {
1518
- let moduleName = moduleNameArg;
1519
- if (!moduleName) {
1520
- const input = await p.text({
1521
- message: "What is the name of the new module or feature?",
1522
- placeholder: "e.g. Payment, Order, UserProfile",
1523
- validate(val) {
1524
- if (!val || !val.trim()) return "Module name cannot be empty.";
1525
- if (!/^[a-zA-Z0-9_-]+$/.test(val.trim())) return "Use only alphanumeric characters, dashes, or underscores.";
1526
- }
1527
- });
1528
- if (p.isCancel(input)) handleCancel();
1529
- moduleName = input.trim();
1530
- }
1531
- let selectedComponents = [];
1532
- if (projectInfo.type === "laravel") {
1533
- const componentsPrompt = await p.multiselect({
1534
- message: `Select components to scaffold for ${import_picocolors.default.cyan(moduleName)}:`,
1535
- options: [
1536
- { value: "model", label: "Eloquent Model", hint: "Includes table name, fillables, and casts" },
1537
- { value: "controller", label: "Resourceful Controller", hint: "Includes standard CRUD methods & DI service" },
1538
- { value: "service", label: "Service Class", hint: "Encapsulated business logic operations" },
1539
- { value: "action", label: "Single Action Class", hint: "Invokable single-responsibility class" },
1540
- { value: "request", label: "Form Request", hint: "Request validation rules & authorization" },
1541
- { value: "migration", label: "Database Migration", hint: "Schema definition with timestamps" }
1542
- ],
1543
- initialValues: ["model", "controller", "service", "request", "migration"],
1544
- required: true
1545
- });
1546
- if (p.isCancel(componentsPrompt)) handleCancel();
1547
- selectedComponents = componentsPrompt;
1548
- } else {
1549
- const componentsPrompt = await p.multiselect({
1550
- message: `Select components to scaffold for ${import_picocolors.default.cyan(moduleName)}:`,
1551
- options: [
1552
- { value: "controller", label: "Controller", hint: "Express request handlers & response orchestration" },
1553
- { value: "service", label: "Service", hint: "Application logic & repository coordination" },
1554
- { value: "repository", label: "Repository", hint: "Data persistence abstraction & queries" },
1555
- { value: "route", label: "Router & Routes", hint: "Express router mapping endpoints to controller" },
1556
- { value: "dto", label: "DTOs & Interfaces", hint: "Typed interfaces for entities and payloads" }
1557
- ],
1558
- initialValues: ["controller", "service", "repository", "route", "dto"],
1559
- required: true
1560
- });
1561
- if (p.isCancel(componentsPrompt)) handleCancel();
1562
- selectedComponents = componentsPrompt;
1563
- }
1564
- let isModular = true;
1565
- if (cliOptions.modular) {
1566
- isModular = true;
1567
- } else if (cliOptions.standard) {
1568
- isModular = false;
1569
- } else {
1570
- const defaultModular = projectInfo.type === "laravel" ? Boolean(projectInfo.laravel?.isModular) : projectInfo.type === "typescript" ? Boolean(projectInfo.typescript?.isModular) : true;
1571
- const structurePrompt = await p.select({
1572
- message: "Choose folder architecture structure:",
1573
- options: projectInfo.type === "laravel" ? [
1574
- {
1575
- value: "modular",
1576
- label: "Modular Domain Structure",
1577
- hint: `app/Modules/${moduleName}/[Models, Controllers, Services]`
1578
- },
1579
- {
1580
- value: "standard",
1581
- label: "Standard Laravel Structure",
1582
- hint: `app/Models, app/Http/Controllers, app/Services`
1583
- }
1584
- ] : [
1585
- {
1586
- value: "modular",
1587
- label: "Modular Feature Structure",
1588
- hint: `src/modules/${moduleName.toLowerCase()}/...`
1589
- },
1590
- {
1591
- value: "standard",
1592
- label: "Layered Folder Structure",
1593
- hint: `src/controllers, src/services, src/routes...`
1594
- }
1595
- ],
1596
- initialValue: defaultModular ? "modular" : "standard"
1597
- });
1598
- if (p.isCancel(structurePrompt)) handleCancel();
1599
- isModular = structurePrompt === "modular";
1600
- }
1601
- const spinner2 = p.spinner();
1602
- spinner2.start(`Generating boilerplate files for ${import_picocolors.default.cyan(moduleName)}...`);
1603
- const result = await scaffoldModule(projectInfo, {
1604
- moduleName,
1605
- components: selectedComponents,
1606
- isModular,
1607
- dryRun: cliOptions.dryRun,
1608
- forceOverwrite: cliOptions.force
1609
- });
1610
- spinner2.stop("Generation finished!");
1611
- const fileLines = result.files.map((f) => {
1612
- if (f.status === "created") {
1613
- return ` ${import_picocolors.default.green("\u2714")} ${f.relativeFilePath} ${import_picocolors.default.gray("(created)")}`;
1614
- } else if (f.status === "skipped") {
1615
- return ` ${import_picocolors.default.yellow("\u26A0")} ${f.relativeFilePath} ${import_picocolors.default.yellow(`(skipped - ${f.reason})`)}`;
1616
- } else {
1617
- return ` ${import_picocolors.default.red("\u2716")} ${f.relativeFilePath} ${import_picocolors.default.red(`(error - ${f.reason})`)}`;
1618
- }
1619
- });
1620
- p.note(
1621
- fileLines.join("\n") + `
1622
-
1623
- ${import_picocolors.default.bold("Summary:")} ${import_picocolors.default.green(`${result.createdCount} created`)}, ${import_picocolors.default.yellow(`${result.skippedCount} skipped`)}, ${import_picocolors.default.red(`${result.errorCount} errors`)}`,
1624
- import_picocolors.default.cyan(`Module Scaffolding: ${moduleName}`)
1625
- );
1626
- if (result.skippedCount > 0 && !cliOptions.force) {
1627
- p.log.warn(import_picocolors.default.yellow("Anti-Overwrite Protection prevented modifying existing files. Use --force to overwrite if intended."));
1628
- }
1629
- p.outro(import_picocolors.default.green(`\u2728 Successfully scaffolded ${moduleName} module!`));
1630
- }
1631
- function showProjectInfo(projectInfo) {
1632
- let details = `${import_picocolors.default.bold("Project Name:")} ${projectInfo.name}
1633
- ${import_picocolors.default.bold("Project Type:")} ${projectInfo.type.toUpperCase()}
1634
- ${import_picocolors.default.bold("Flavor / Stack:")} ${projectInfo.summary}
1635
- ${import_picocolors.default.bold("Working Path:")} ${projectInfo.rootPath}`;
1636
- if (projectInfo.type === "laravel" && projectInfo.laravel) {
1637
- details += `
1638
- ${import_picocolors.default.bold("Filament Admin:")} ${projectInfo.laravel.hasFilament ? import_picocolors.default.green("Yes") : "No"}
1639
- ${import_picocolors.default.bold("Livewire:")} ${projectInfo.laravel.hasLivewire ? import_picocolors.default.green("Yes") : "No"}
1640
- ${import_picocolors.default.bold("Inertia.js:")} ${projectInfo.laravel.hasInertia ? import_picocolors.default.green("Yes") : "No"}
1641
- ${import_picocolors.default.bold("Modular App:")} ${projectInfo.laravel.isModular ? import_picocolors.default.green("Yes") : "No"}`;
1642
- } else if (projectInfo.type === "typescript" && projectInfo.typescript) {
1643
- details += `
1644
- ${import_picocolors.default.bold("Framework:")} ${projectInfo.typescript.framework}
1645
- ${import_picocolors.default.bold("Prisma ORM:")} ${projectInfo.typescript.hasPrisma ? import_picocolors.default.green("Yes") : "No"}
1646
- ${import_picocolors.default.bold("Monorepo:")} ${projectInfo.typescript.isMonorepo ? import_picocolors.default.green("Yes") : "No"}
1647
- ${import_picocolors.default.bold("Modular Src:")} ${projectInfo.typescript.isModular ? import_picocolors.default.green("Yes") : "No"}`;
1648
- }
1649
- p.note(details, import_picocolors.default.cyan("Detected Project Architecture"));
1650
- }
1651
- async function main() {
1652
- const args = process.argv.slice(2);
1653
- if (args.includes("--help") || args.includes("-h") || args[0] === "help") {
1654
- showHelp();
1655
- return;
1656
- }
1657
- if (args.includes("--version") || args.includes("-v")) {
1658
- console.log(`larats v${VERSION}`);
1659
- return;
1660
- }
1661
- let outputPath = "ARCHITECTURE.md";
1662
- const outputIdx = args.findIndex((a) => a === "--output" || a === "-o");
1663
- if (outputIdx !== -1 && args[outputIdx + 1]) {
1664
- outputPath = args[outputIdx + 1];
1665
- }
1666
- const isModularFlag = args.includes("--modular") || args.includes("-m");
1667
- const isStandardFlag = args.includes("--standard") || args.includes("-s");
1668
- const isForceFlag = args.includes("--force") || args.includes("-f");
1669
- const isDryRunFlag = args.includes("--dry-run");
1670
- const command = args[0] && !args[0].startsWith("-") ? args[0] : null;
1671
- const projectInfo = await detectProjectType(process.cwd());
1672
- if (command === "scan") {
1673
- p.intro(`${import_picocolors.default.bgCyan(import_picocolors.default.black(" LaraTS "))} ${import_picocolors.default.gray(`v${VERSION}`)} - Scanning Architecture`);
1674
- p.log.info(`Detected: ${import_picocolors.default.green(projectInfo.summary)}`);
1675
- await runScan(projectInfo, outputPath);
1676
- return;
1677
- }
1678
- if (command === "scaffold" || command === "make") {
1679
- const moduleName = args[1] && !args[1].startsWith("-") ? args[1] : void 0;
1680
- p.intro(`${import_picocolors.default.bgCyan(import_picocolors.default.black(" LaraTS "))} ${import_picocolors.default.gray(`v${VERSION}`)} - Module Scaffolder`);
1681
- p.log.info(`Target Project: ${import_picocolors.default.green(projectInfo.summary)}`);
1682
- await runScaffold(projectInfo, moduleName, {
1683
- modular: isModularFlag,
1684
- standard: isStandardFlag,
1685
- force: isForceFlag,
1686
- dryRun: isDryRunFlag
1687
- });
1688
- return;
1689
- }
1690
- if (command === "info") {
1691
- p.intro(`${import_picocolors.default.bgCyan(import_picocolors.default.black(" LaraTS "))} ${import_picocolors.default.gray(`v${VERSION}`)}`);
1692
- showProjectInfo(projectInfo);
1693
- p.outro(import_picocolors.default.cyan("Done."));
1694
- return;
1695
- }
1696
- p.intro(`${import_picocolors.default.bgCyan(import_picocolors.default.black(" LaraTS "))} ${import_picocolors.default.bold("Smart Architecture Visualizer & Scaffolder")} ${import_picocolors.default.gray(`v${VERSION}`)}`);
1697
- p.log.info(`Working Directory: ${import_picocolors.default.gray(process.cwd())}`);
1698
- p.log.info(`Detected Architecture: ${import_picocolors.default.bold(import_picocolors.default.green(projectInfo.summary))}`);
1699
- const action = await p.select({
1700
- message: "What would you like to do?",
1701
- options: [
1702
- {
1703
- value: "scan",
1704
- label: "\u{1F50D} Scan Architecture & Generate ARCHITECTURE.md",
1705
- hint: "Creates component stats, directory tree & Mermaid flowchart"
1706
- },
1707
- {
1708
- value: "scaffold",
1709
- label: "\u{1F3D7}\uFE0F Scaffold New Feature / Module",
1710
- hint: "Interactively generate boilerplate components with safety checks"
1711
- },
1712
- {
1713
- value: "info",
1714
- label: "\u{1F4CA} Inspect Detected Architecture Details",
1715
- hint: "View detected frameworks, ORMs, and configurations"
1716
- },
1717
- {
1718
- value: "exit",
1719
- label: "\u{1F6AA} Exit"
1720
- }
1721
- ]
1722
- });
1723
- if (p.isCancel(action) || action === "exit") {
1724
- handleCancel();
1725
- }
1726
- if (action === "scan") {
1727
- await runScan(projectInfo, outputPath);
1728
- } else if (action === "scaffold") {
1729
- await runScaffold(projectInfo, void 0, {
1730
- modular: isModularFlag,
1731
- standard: isStandardFlag,
1732
- force: isForceFlag,
1733
- dryRun: isDryRunFlag
1734
- });
1735
- } else if (action === "info") {
1736
- showProjectInfo(projectInfo);
1737
- p.outro(import_picocolors.default.cyan("Done!"));
1738
- }
1739
- }
1740
- main().catch((err) => {
1741
- p.log.error(import_picocolors.default.red(`Fatal error: ${err.message || String(err)}`));
1742
- process.exit(1);
1743
- });
1744
- //# sourceMappingURL=index.cjs.map