@abuhannaa/create-apptemplate 1.0.8 → 1.0.10

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.js CHANGED
@@ -4,11 +4,669 @@
4
4
  import { intro, outro, isCancel as isCancel2 } from "@clack/prompts";
5
5
  import pc3 from "picocolors";
6
6
 
7
+ // src/types.ts
8
+ var ALL_FEATURES = [
9
+ "auth",
10
+ "userManagement",
11
+ "departments",
12
+ "fileUpload",
13
+ "auditLogs",
14
+ "notifications",
15
+ "dataExport",
16
+ "dashboard"
17
+ ];
18
+
19
+ // src/features.ts
20
+ var FEATURE_OPTIONS = [
21
+ {
22
+ value: "auth",
23
+ label: "Authentication (Login, JWT, Guards)",
24
+ hint: "Login/logout pages, JWT middleware, auth guards, profile"
25
+ },
26
+ {
27
+ value: "userManagement",
28
+ label: "User Management",
29
+ hint: "User CRUD, roles, permissions"
30
+ },
31
+ {
32
+ value: "departments",
33
+ label: "Departments",
34
+ hint: "Department CRUD module"
35
+ },
36
+ {
37
+ value: "fileUpload",
38
+ label: "File Management",
39
+ hint: "File upload, download, preview"
40
+ },
41
+ {
42
+ value: "auditLogs",
43
+ label: "Audit Logging",
44
+ hint: "Track all data changes with audit trail"
45
+ },
46
+ {
47
+ value: "notifications",
48
+ label: "Notifications",
49
+ hint: "Real-time notification system (WebSocket)"
50
+ },
51
+ {
52
+ value: "dataExport",
53
+ label: "Data Export",
54
+ hint: "CSV, Excel, PDF export"
55
+ },
56
+ {
57
+ value: "dashboard",
58
+ label: "Dashboard",
59
+ hint: "Dashboard page with statistics (Requires Users, Depts, Notifications)"
60
+ }
61
+ ];
62
+ function applyFeatureDependencies(selected) {
63
+ let result = [...selected];
64
+ if (!result.includes("auth")) {
65
+ result = result.filter((f) => f !== "userManagement" && f !== "departments");
66
+ }
67
+ if (!result.includes("userManagement")) {
68
+ result = result.filter((f) => f !== "departments");
69
+ }
70
+ if (!result.includes("userManagement") || !result.includes("departments") || !result.includes("notifications")) {
71
+ result = result.filter((f) => f !== "dashboard");
72
+ }
73
+ return result;
74
+ }
75
+ var DOTNET_CLEAN_MAP = {
76
+ auth: {
77
+ backend: [
78
+ // Application layer
79
+ "src/Core/App.Template.Application/Features/Authentication",
80
+ "src/Core/App.Template.Application/DTOs/Auth",
81
+ "src/Core/App.Template.Application/Interfaces/IJwtTokenService.cs",
82
+ "src/Core/App.Template.Application/Interfaces/IPasswordHashService.cs",
83
+ "src/Core/App.Template.Application/Interfaces/ISsoAuthService.cs",
84
+ "src/Core/App.Template.Application/Interfaces/IEmailService.cs",
85
+ // Infrastructure layer
86
+ "src/Infrastructure/App.Template.Infrastructure/Services/JwtTokenService.cs",
87
+ "src/Infrastructure/App.Template.Infrastructure/Services/PasswordHashService.cs",
88
+ "src/Infrastructure/App.Template.Infrastructure/Services/SsoAuthService.cs",
89
+ "src/Infrastructure/App.Template.Infrastructure/Services/SsoApiModels.cs",
90
+ "src/Infrastructure/App.Template.Infrastructure/Services/EmailService.cs",
91
+ // Presentation layer
92
+ "src/Presentation/App.Template.WebAPI/Controllers/AuthController.cs"
93
+ ],
94
+ frontend: []
95
+ // Frontend auth files handled by pattern below
96
+ },
97
+ userManagement: {
98
+ backend: [
99
+ "src/Core/App.Template.Application/Features/UserManagement",
100
+ "src/Core/App.Template.Application/DTOs/UserDto.cs",
101
+ "src/Presentation/App.Template.WebAPI/Controllers/UsersController.cs"
102
+ ],
103
+ frontend: []
104
+ },
105
+ departments: {
106
+ backend: [
107
+ "src/Core/App.Template.Application/Features/DepartmentManagement",
108
+ "src/Core/App.Template.Application/DTOs/DepartmentDto.cs",
109
+ "src/Core/App.Template.Application/Interfaces/IOrganizationService.cs",
110
+ "src/Infrastructure/App.Template.Infrastructure/Services/OrganizationService.cs",
111
+ "src/Presentation/App.Template.WebAPI/Controllers/DepartmentsController.cs"
112
+ ],
113
+ frontend: []
114
+ },
115
+ fileUpload: {
116
+ backend: [
117
+ "src/Core/App.Template.Application/Features/FileManagement",
118
+ "src/Core/App.Template.Application/DTOs/UploadedFileDto.cs",
119
+ "src/Core/App.Template.Application/Interfaces/IFileStorageService.cs",
120
+ "src/Infrastructure/App.Template.Infrastructure/Services/FileStorageService.cs",
121
+ "src/Presentation/App.Template.WebAPI/Controllers/FilesController.cs"
122
+ ],
123
+ frontend: []
124
+ },
125
+ auditLogs: {
126
+ backend: [
127
+ "src/Core/App.Template.Application/Features/AuditLogManagement",
128
+ "src/Core/App.Template.Application/DTOs/AuditLogDto.cs",
129
+ "src/Infrastructure/App.Template.Infrastructure/Persistence/AuditEntry.cs",
130
+ "src/Presentation/App.Template.WebAPI/Controllers/AuditLogsController.cs"
131
+ ],
132
+ frontend: []
133
+ },
134
+ notifications: {
135
+ backend: [
136
+ "src/Core/App.Template.Application/Features/NotificationManagement",
137
+ "src/Core/App.Template.Application/DTOs/NotificationDto.cs",
138
+ "src/Core/App.Template.Application/Interfaces/INotificationService.cs",
139
+ "src/Infrastructure/App.Template.Infrastructure/Services/NotificationService.cs",
140
+ "src/Infrastructure/App.Template.Infrastructure/Hubs",
141
+ "src/Infrastructure/App.Template.Infrastructure/SignalR",
142
+ "src/Presentation/App.Template.WebAPI/Controllers/NotificationsController.cs"
143
+ ],
144
+ frontend: []
145
+ },
146
+ dataExport: {
147
+ backend: [
148
+ "src/Core/App.Template.Application/Interfaces/IExportService.cs",
149
+ "src/Infrastructure/App.Template.Infrastructure/Services/ExportService.cs",
150
+ "src/Presentation/App.Template.WebAPI/Controllers/ExportController.cs"
151
+ ],
152
+ frontend: []
153
+ },
154
+ dashboard: {
155
+ backend: [],
156
+ frontend: []
157
+ }
158
+ };
159
+ var DOTNET_NLAYER_MAP = {
160
+ auth: {
161
+ backend: [
162
+ "src/App.Template.Api/Controllers/AuthController.cs",
163
+ "src/App.Template.Api/Services/AuthService.cs",
164
+ "src/App.Template.Api/Services/JwtTokenGenerator.cs",
165
+ "src/App.Template.Api/Models/Dtos/AuthDtos.cs"
166
+ ],
167
+ frontend: []
168
+ },
169
+ userManagement: {
170
+ backend: [
171
+ "src/App.Template.Api/Controllers/UsersController.cs",
172
+ "src/App.Template.Api/Services/UserService.cs",
173
+ "src/App.Template.Api/Services/IUserService.cs",
174
+ "src/App.Template.Api/Repositories/UserRepository.cs",
175
+ "src/App.Template.Api/Repositories/IUserRepository.cs",
176
+ "src/App.Template.Api/Models/Dtos/UserDtos.cs"
177
+ ],
178
+ frontend: []
179
+ },
180
+ departments: {
181
+ backend: [
182
+ "src/App.Template.Api/Controllers/DepartmentsController.cs",
183
+ "src/App.Template.Api/Models/Dtos/DepartmentDtos.cs"
184
+ ],
185
+ frontend: []
186
+ },
187
+ fileUpload: {
188
+ backend: [
189
+ "src/App.Template.Api/Controllers/FilesController.cs"
190
+ ],
191
+ frontend: []
192
+ },
193
+ auditLogs: {
194
+ backend: [],
195
+ frontend: []
196
+ },
197
+ notifications: {
198
+ backend: [
199
+ "src/App.Template.Api/Controllers/NotificationsController.cs",
200
+ "src/App.Template.Api/Infrastructure/Hubs"
201
+ ],
202
+ frontend: []
203
+ },
204
+ dataExport: {
205
+ backend: [
206
+ "src/App.Template.Api/Controllers/ExportController.cs"
207
+ ],
208
+ frontend: []
209
+ },
210
+ dashboard: {
211
+ backend: [],
212
+ frontend: []
213
+ }
214
+ };
215
+ var DOTNET_FEATURE_MAP = {
216
+ auth: {
217
+ backend: [
218
+ "src/App.Template.Api/Features/Auth"
219
+ ],
220
+ frontend: []
221
+ },
222
+ userManagement: {
223
+ backend: [
224
+ "src/App.Template.Api/Features/Users"
225
+ ],
226
+ frontend: []
227
+ },
228
+ departments: {
229
+ backend: [
230
+ "src/App.Template.Api/Features/Departments"
231
+ ],
232
+ frontend: []
233
+ },
234
+ fileUpload: {
235
+ backend: [
236
+ "src/App.Template.Api/Features/Files"
237
+ ],
238
+ frontend: []
239
+ },
240
+ auditLogs: {
241
+ backend: [],
242
+ frontend: []
243
+ },
244
+ notifications: {
245
+ backend: [
246
+ "src/App.Template.Api/Features/Notifications"
247
+ ],
248
+ frontend: []
249
+ },
250
+ dataExport: {
251
+ backend: [
252
+ "src/App.Template.Api/Features/Export"
253
+ ],
254
+ frontend: []
255
+ },
256
+ dashboard: {
257
+ backend: [],
258
+ frontend: []
259
+ }
260
+ };
261
+ var SPRING_CLEAN_MAP = {
262
+ auth: {
263
+ backend: [
264
+ "api/src/main/java/apptemplate/api/controllers/AuthController.java"
265
+ ],
266
+ frontend: []
267
+ },
268
+ userManagement: {
269
+ backend: [
270
+ "api/src/main/java/apptemplate/api/controllers/UsersController.java"
271
+ ],
272
+ frontend: []
273
+ },
274
+ departments: {
275
+ backend: [
276
+ "api/src/main/java/apptemplate/api/controllers/DepartmentsController.java"
277
+ ],
278
+ frontend: []
279
+ },
280
+ fileUpload: {
281
+ backend: [
282
+ "api/src/main/java/apptemplate/api/controllers/FilesController.java"
283
+ ],
284
+ frontend: []
285
+ },
286
+ auditLogs: {
287
+ backend: [
288
+ "api/src/main/java/apptemplate/api/controllers/AuditLogsController.java"
289
+ ],
290
+ frontend: []
291
+ },
292
+ notifications: {
293
+ backend: [
294
+ "api/src/main/java/apptemplate/api/controllers/NotificationsController.java"
295
+ ],
296
+ frontend: []
297
+ },
298
+ dataExport: {
299
+ backend: [
300
+ "api/src/main/java/apptemplate/api/controllers/ExportController.java"
301
+ ],
302
+ frontend: []
303
+ },
304
+ dashboard: {
305
+ backend: [],
306
+ frontend: []
307
+ }
308
+ };
309
+ var SPRING_NLAYER_MAP = {
310
+ auth: {
311
+ backend: [
312
+ "src/main/java/com/apptemplate/api/controller/AuthController.java"
313
+ ],
314
+ frontend: []
315
+ },
316
+ userManagement: {
317
+ backend: [
318
+ "src/main/java/com/apptemplate/api/controller/UserController.java"
319
+ ],
320
+ frontend: []
321
+ },
322
+ departments: {
323
+ backend: [
324
+ "src/main/java/com/apptemplate/api/controller/DepartmentController.java"
325
+ ],
326
+ frontend: []
327
+ },
328
+ fileUpload: {
329
+ backend: [
330
+ "src/main/java/com/apptemplate/api/controller/FileController.java"
331
+ ],
332
+ frontend: []
333
+ },
334
+ auditLogs: {
335
+ backend: [
336
+ "src/main/java/com/apptemplate/api/audit"
337
+ ],
338
+ frontend: []
339
+ },
340
+ notifications: {
341
+ backend: [
342
+ "src/main/java/com/apptemplate/api/controller/NotificationController.java"
343
+ ],
344
+ frontend: []
345
+ },
346
+ dataExport: {
347
+ backend: [
348
+ "src/main/java/com/apptemplate/api/controller/ExportController.java"
349
+ ],
350
+ frontend: []
351
+ },
352
+ dashboard: {
353
+ backend: [],
354
+ frontend: []
355
+ }
356
+ };
357
+ var SPRING_FEATURE_MAP = {
358
+ auth: {
359
+ backend: [
360
+ "src/main/java/com/apptemplate/api/features/auth"
361
+ ],
362
+ frontend: []
363
+ },
364
+ userManagement: {
365
+ backend: [
366
+ "src/main/java/com/apptemplate/api/features/users"
367
+ ],
368
+ frontend: []
369
+ },
370
+ departments: {
371
+ backend: [
372
+ "src/main/java/com/apptemplate/api/features/departments"
373
+ ],
374
+ frontend: []
375
+ },
376
+ fileUpload: {
377
+ backend: [
378
+ "src/main/java/com/apptemplate/api/features/files"
379
+ ],
380
+ frontend: []
381
+ },
382
+ auditLogs: {
383
+ backend: [
384
+ "src/main/java/com/apptemplate/api/common/audit"
385
+ ],
386
+ frontend: []
387
+ },
388
+ notifications: {
389
+ backend: [
390
+ "src/main/java/com/apptemplate/api/features/notifications"
391
+ ],
392
+ frontend: []
393
+ },
394
+ dataExport: {
395
+ backend: [
396
+ "src/main/java/com/apptemplate/api/features/export"
397
+ ],
398
+ frontend: []
399
+ },
400
+ dashboard: {
401
+ backend: [],
402
+ frontend: []
403
+ }
404
+ };
405
+ var NESTJS_CLEAN_MAP = {
406
+ auth: {
407
+ backend: [
408
+ "src/modules/auth"
409
+ ],
410
+ frontend: []
411
+ },
412
+ userManagement: {
413
+ backend: [
414
+ "src/modules/user-management"
415
+ ],
416
+ frontend: []
417
+ },
418
+ departments: {
419
+ backend: [
420
+ "src/modules/department-management"
421
+ ],
422
+ frontend: []
423
+ },
424
+ fileUpload: {
425
+ backend: [
426
+ "src/modules/file-management"
427
+ ],
428
+ frontend: []
429
+ },
430
+ auditLogs: {
431
+ backend: [
432
+ "src/modules/audit-log"
433
+ ],
434
+ frontend: []
435
+ },
436
+ notifications: {
437
+ backend: [
438
+ "src/modules/notification"
439
+ ],
440
+ frontend: []
441
+ },
442
+ dataExport: {
443
+ backend: [
444
+ "src/modules/export"
445
+ ],
446
+ frontend: []
447
+ },
448
+ dashboard: {
449
+ backend: [],
450
+ frontend: []
451
+ }
452
+ };
453
+ var NESTJS_NLAYER_MAP = {
454
+ auth: {
455
+ backend: [
456
+ "src/auth"
457
+ ],
458
+ frontend: []
459
+ },
460
+ userManagement: {
461
+ backend: [],
462
+ frontend: []
463
+ },
464
+ departments: {
465
+ backend: [
466
+ "src/modules/departments"
467
+ ],
468
+ frontend: []
469
+ },
470
+ fileUpload: {
471
+ backend: [
472
+ "src/modules/files"
473
+ ],
474
+ frontend: []
475
+ },
476
+ auditLogs: {
477
+ backend: [],
478
+ frontend: []
479
+ },
480
+ notifications: {
481
+ backend: [
482
+ "src/modules/notifications"
483
+ ],
484
+ frontend: []
485
+ },
486
+ dataExport: {
487
+ backend: [
488
+ "src/modules/export"
489
+ ],
490
+ frontend: []
491
+ },
492
+ dashboard: {
493
+ backend: [],
494
+ frontend: []
495
+ }
496
+ };
497
+ var NESTJS_FEATURE_MAP = {
498
+ auth: {
499
+ backend: [
500
+ "src/features/auth"
501
+ ],
502
+ frontend: []
503
+ },
504
+ userManagement: {
505
+ backend: [
506
+ "src/features/users"
507
+ ],
508
+ frontend: []
509
+ },
510
+ departments: {
511
+ backend: [
512
+ "src/features/departments"
513
+ ],
514
+ frontend: []
515
+ },
516
+ fileUpload: {
517
+ backend: [
518
+ "src/features/files"
519
+ ],
520
+ frontend: []
521
+ },
522
+ auditLogs: {
523
+ backend: [],
524
+ frontend: []
525
+ },
526
+ notifications: {
527
+ backend: [
528
+ "src/features/notifications"
529
+ ],
530
+ frontend: []
531
+ },
532
+ dataExport: {
533
+ backend: [
534
+ "src/features/export"
535
+ ],
536
+ frontend: []
537
+ },
538
+ dashboard: {
539
+ backend: [],
540
+ frontend: []
541
+ }
542
+ };
543
+ var VUE_FRONTEND_MAP = {
544
+ auth: [
545
+ "src/pages/login.vue",
546
+ "src/pages/forgot-password.vue",
547
+ "src/pages/reset-password.vue",
548
+ "src/pages/profile.vue",
549
+ "src/stores/auth.js",
550
+ "src/services/authApi.js"
551
+ ],
552
+ userManagement: [
553
+ "src/pages/users",
554
+ "src/stores/user.js",
555
+ "src/services/userApi.js"
556
+ ],
557
+ departments: [
558
+ "src/pages/departments",
559
+ "src/stores/department.js",
560
+ "src/services/departmentApi.js"
561
+ ],
562
+ fileUpload: [
563
+ "src/pages/files",
564
+ "src/services/fileService.js"
565
+ ],
566
+ auditLogs: [
567
+ "src/pages/audit-logs",
568
+ "src/services/auditLogService.js"
569
+ ],
570
+ notifications: [
571
+ "src/pages/notifications",
572
+ "src/stores/notification.js",
573
+ "src/stores/persistentNotification.js",
574
+ "src/services/notificationApi.js"
575
+ ],
576
+ dataExport: [
577
+ "src/services/exportService.js"
578
+ ],
579
+ dashboard: [
580
+ "src/pages/dashboard.vue"
581
+ ]
582
+ };
583
+ var REACT_FRONTEND_MAP = {
584
+ auth: [
585
+ "src/pages/Login.tsx",
586
+ "src/pages/ForgotPassword.tsx",
587
+ "src/pages/ResetPassword.tsx",
588
+ "src/pages/Profile.tsx",
589
+ "src/stores/authStore.ts",
590
+ "src/services/authApi.ts"
591
+ ],
592
+ userManagement: [
593
+ "src/pages/Users.tsx",
594
+ "src/stores/userStore.ts",
595
+ "src/services/userApi.ts"
596
+ ],
597
+ departments: [
598
+ "src/pages/Departments.tsx",
599
+ "src/stores/departmentStore.ts",
600
+ "src/services/departmentApi.ts"
601
+ ],
602
+ fileUpload: [
603
+ "src/pages/FilesPage.tsx",
604
+ "src/services/fileService.ts"
605
+ ],
606
+ auditLogs: [
607
+ "src/pages/AuditLogsPage.tsx",
608
+ "src/pages/AuditLogs.tsx",
609
+ "src/services/auditLogService.ts"
610
+ ],
611
+ notifications: [
612
+ "src/pages/Notifications.tsx",
613
+ "src/stores/notificationStore.ts",
614
+ "src/stores/persistentNotificationStore.ts",
615
+ "src/services/notificationApi.ts"
616
+ ],
617
+ dataExport: [
618
+ "src/services/exportService.ts"
619
+ ],
620
+ dashboard: [
621
+ "src/pages/Dashboard.tsx",
622
+ "src/pages/Dashboard.scss"
623
+ ]
624
+ };
625
+ function getBackendFileMap(backend, architecture) {
626
+ const key = `${backend}-${architecture}`;
627
+ switch (key) {
628
+ case "dotnet-clean":
629
+ return DOTNET_CLEAN_MAP;
630
+ case "dotnet-nlayer":
631
+ return DOTNET_NLAYER_MAP;
632
+ case "dotnet-feature":
633
+ return DOTNET_FEATURE_MAP;
634
+ case "spring-clean":
635
+ return SPRING_CLEAN_MAP;
636
+ case "spring-nlayer":
637
+ return SPRING_NLAYER_MAP;
638
+ case "spring-feature":
639
+ return SPRING_FEATURE_MAP;
640
+ case "nestjs-clean":
641
+ return NESTJS_CLEAN_MAP;
642
+ case "nestjs-nlayer":
643
+ return NESTJS_NLAYER_MAP;
644
+ case "nestjs-feature":
645
+ return NESTJS_FEATURE_MAP;
646
+ default:
647
+ return {};
648
+ }
649
+ }
650
+ function getFrontendFileMap(frontendFramework) {
651
+ return frontendFramework === "vue" ? VUE_FRONTEND_MAP : REACT_FRONTEND_MAP;
652
+ }
653
+ function getFeaturesLabel(features, allFeatures) {
654
+ if (features.length === allFeatures.length) {
655
+ return "All features";
656
+ }
657
+ if (features.length === 0) {
658
+ return "None (bare template)";
659
+ }
660
+ return features.map((f) => FEATURE_OPTIONS.find((o) => o.value === f)?.label || f).join(", ");
661
+ }
662
+
7
663
  // src/cli.ts
8
664
  var validProjectTypes = ["fullstack", "backend", "frontend"];
9
665
  var validBackends = ["dotnet", "spring", "nestjs"];
666
+ var validArchitectures = ["clean", "nlayer", "feature"];
10
667
  var validFrontendFrameworks = ["vue", "react"];
11
668
  var validUILibraries = ["vuetify", "primevue", "primereact", "mui"];
669
+ var validFeatures = [...ALL_FEATURES];
12
670
  function parseArgs() {
13
671
  const args = process.argv.slice(2);
14
672
  const result = {};
@@ -55,6 +713,16 @@ function parseArgs() {
55
713
  i++;
56
714
  continue;
57
715
  }
716
+ if (arg === "-a" || arg === "--architecture") {
717
+ const value = args[++i];
718
+ if (isValidArchitecture(value)) {
719
+ result.architecture = value;
720
+ } else {
721
+ console.warn(`Warning: Invalid architecture "${value}". Valid options: ${validArchitectures.join(", ")}`);
722
+ }
723
+ i++;
724
+ continue;
725
+ }
58
726
  if (arg === "-f" || arg === "--framework") {
59
727
  const value = args[++i];
60
728
  if (isValidFrontendFramework(value)) {
@@ -85,6 +753,24 @@ function parseArgs() {
85
753
  i++;
86
754
  continue;
87
755
  }
756
+ if (arg === "--features" || arg === "--feat") {
757
+ const value = args[++i];
758
+ if (value) {
759
+ if (value === "all") {
760
+ result.features = [...ALL_FEATURES];
761
+ } else {
762
+ const requested = value.split(",").map((s) => s.trim());
763
+ const valid = requested.filter((f) => validFeatures.includes(f));
764
+ const invalid = requested.filter((f) => !validFeatures.includes(f));
765
+ if (invalid.length > 0) {
766
+ console.warn(`Warning: Invalid features "${invalid.join(", ")}". Valid options: ${validFeatures.join(", ")}`);
767
+ }
768
+ result.features = applyFeatureDependencies(valid);
769
+ }
770
+ }
771
+ i++;
772
+ continue;
773
+ }
88
774
  if (!arg.startsWith("-") && !result.projectPath) {
89
775
  result.projectPath = arg;
90
776
  }
@@ -98,6 +784,9 @@ function isValidProjectType(value) {
98
784
  function isValidBackend(value) {
99
785
  return value !== void 0 && validBackends.includes(value);
100
786
  }
787
+ function isValidArchitecture(value) {
788
+ return value !== void 0 && validArchitectures.includes(value);
789
+ }
101
790
  function isValidFrontendFramework(value) {
102
791
  return value !== void 0 && validFrontendFrameworks.includes(value);
103
792
  }
@@ -184,6 +873,31 @@ async function runInteractivePrompts(cliArgs) {
184
873
  if (p.isCancel(result)) return result;
185
874
  backend = result;
186
875
  }
876
+ let architecture = cliArgs.architecture || "clean";
877
+ if (projectType !== "frontend" && !cliArgs.architecture) {
878
+ const result = await p.select({
879
+ message: "Which architecture pattern would you like to use?",
880
+ options: [
881
+ {
882
+ value: "nlayer",
883
+ label: "N-Layer (3-Tier)",
884
+ hint: "Simple, traditional. Best for CRUD apps & prototypes"
885
+ },
886
+ {
887
+ value: "clean",
888
+ label: "Clean Architecture",
889
+ hint: "Enterprise-grade. Best for complex business domains"
890
+ },
891
+ {
892
+ value: "feature",
893
+ label: "Package by Feature",
894
+ hint: "Modular. Best for medium-large apps & team scalability"
895
+ }
896
+ ]
897
+ });
898
+ if (p.isCancel(result)) return result;
899
+ architecture = result;
900
+ }
187
901
  let frontendFramework = cliArgs.framework || "vue";
188
902
  if (projectType !== "backend" && !cliArgs.framework) {
189
903
  const result = await p.select({
@@ -237,6 +951,26 @@ async function runInteractivePrompts(cliArgs) {
237
951
  if (p.isCancel(result)) return result;
238
952
  ui = result;
239
953
  }
954
+ let features = cliArgs.features || [...ALL_FEATURES];
955
+ if (!cliArgs.features) {
956
+ const result = await p.multiselect({
957
+ message: "Which features would you like to include?",
958
+ options: FEATURE_OPTIONS.map((opt) => ({
959
+ value: opt.value,
960
+ label: opt.label,
961
+ hint: opt.hint
962
+ })),
963
+ initialValues: [...ALL_FEATURES],
964
+ required: false
965
+ });
966
+ if (p.isCancel(result)) return result;
967
+ features = applyFeatureDependencies(result);
968
+ if (features.length < result.length) {
969
+ const removed = result.filter((f) => !features.includes(f));
970
+ const removedLabels = removed.map((f) => FEATURE_OPTIONS.find((o) => o.value === f)?.label || f);
971
+ console.log(pc.yellow(` \u26A0 Auto-removed due to dependencies: ${removedLabels.join(", ")}`));
972
+ }
973
+ }
240
974
  let projectName = cliArgs.projectName;
241
975
  const needsNamespace = projectType !== "frontend" && (backend === "dotnet" || backend === "spring");
242
976
  if (needsNamespace && !projectName) {
@@ -283,11 +1017,13 @@ async function runInteractivePrompts(cliArgs) {
283
1017
  ];
284
1018
  if (projectType !== "frontend") {
285
1019
  summaryLines.push(`${pc.cyan("Backend:")} ${getBackendLabel(backend)}`);
1020
+ summaryLines.push(`${pc.cyan("Architecture:")} ${getArchitectureLabel(architecture)}`);
286
1021
  }
287
1022
  if (projectType !== "backend") {
288
1023
  summaryLines.push(`${pc.cyan("Frontend:")} ${getFrontendLabel(frontendFramework)}`);
289
1024
  summaryLines.push(`${pc.cyan("UI Library:")} ${getUILabel(ui)}`);
290
1025
  }
1026
+ summaryLines.push(`${pc.cyan("Features:")} ${getFeaturesLabel(features, ALL_FEATURES)}`);
291
1027
  if (needsNamespace && projectName) {
292
1028
  summaryLines.push(`${pc.cyan("Namespace:")} ${projectName}`);
293
1029
  }
@@ -307,11 +1043,13 @@ async function runInteractivePrompts(cliArgs) {
307
1043
  projectPath,
308
1044
  projectType,
309
1045
  backend,
1046
+ architecture,
310
1047
  frontendFramework,
311
1048
  ui,
312
1049
  projectName,
313
1050
  installDeps,
314
- placeInRoot
1051
+ placeInRoot,
1052
+ features
315
1053
  };
316
1054
  }
317
1055
  function toPascalCase(str) {
@@ -325,6 +1063,14 @@ function getBackendLabel(backend) {
325
1063
  };
326
1064
  return labels[backend];
327
1065
  }
1066
+ function getArchitectureLabel(architecture) {
1067
+ const labels = {
1068
+ clean: "Clean Architecture",
1069
+ nlayer: "N-Layer (3-Tier)",
1070
+ feature: "Package by Feature"
1071
+ };
1072
+ return labels[architecture];
1073
+ }
328
1074
  function getFrontendLabel(framework) {
329
1075
  const labels = {
330
1076
  vue: "Vue 3",
@@ -680,7 +1426,8 @@ async function generateProject(config) {
680
1426
  spinner2.start("Downloading templates...");
681
1427
  try {
682
1428
  if (config.projectType !== "frontend") {
683
- const sourceFolder = `backend-${config.backend}`;
1429
+ const archSuffix = config.architecture === "clean" ? "" : `-${config.architecture}`;
1430
+ const sourceFolder = `backend-${config.backend}${archSuffix}`;
684
1431
  let destFolder;
685
1432
  if (config.projectType === "fullstack") {
686
1433
  destFolder = "backend";
@@ -710,6 +1457,16 @@ async function generateProject(config) {
710
1457
  spinner2.stop("Download failed");
711
1458
  throw error;
712
1459
  }
1460
+ const deselectedFeatures = ALL_FEATURES.filter((f) => !config.features.includes(f));
1461
+ if (deselectedFeatures.length > 0) {
1462
+ spinner2.start("Removing deselected feature files...");
1463
+ try {
1464
+ await removeDeselectedFeatures(absolutePath, config, deselectedFeatures);
1465
+ spinner2.stop(`Removed ${deselectedFeatures.length} deselected feature(s)`);
1466
+ } catch (error) {
1467
+ spinner2.stop("Feature removal had warnings");
1468
+ }
1469
+ }
713
1470
  spinner2.start("Updating configuration files...");
714
1471
  try {
715
1472
  await updateFolderReferences(absolutePath, config);
@@ -754,6 +1511,48 @@ async function generateProject(config) {
754
1511
  await createAppSettingsFromExample(absolutePath, config);
755
1512
  }
756
1513
  }
1514
+ async function removeDeselectedFeatures(projectPath, config, deselectedFeatures) {
1515
+ if (config.projectType !== "frontend") {
1516
+ const backendMap = getBackendFileMap(config.backend, config.architecture);
1517
+ let backendDir;
1518
+ if (config.projectType === "fullstack") {
1519
+ backendDir = path5.join(projectPath, "backend");
1520
+ } else {
1521
+ backendDir = config.placeInRoot ? projectPath : path5.join(projectPath, "backend");
1522
+ }
1523
+ for (const feature of deselectedFeatures) {
1524
+ const featureMap = backendMap[feature];
1525
+ if (featureMap) {
1526
+ for (const filePath of featureMap.backend) {
1527
+ const fullPath = path5.join(backendDir, filePath);
1528
+ if (fs5.existsSync(fullPath)) {
1529
+ fs5.rmSync(fullPath, { recursive: true, force: true });
1530
+ }
1531
+ }
1532
+ }
1533
+ }
1534
+ }
1535
+ if (config.projectType !== "backend") {
1536
+ const frontendMap = getFrontendFileMap(config.frontendFramework);
1537
+ let frontendDir;
1538
+ if (config.projectType === "fullstack") {
1539
+ frontendDir = path5.join(projectPath, "frontend");
1540
+ } else {
1541
+ frontendDir = config.placeInRoot ? projectPath : path5.join(projectPath, "frontend");
1542
+ }
1543
+ for (const feature of deselectedFeatures) {
1544
+ const featurePaths = frontendMap[feature];
1545
+ if (featurePaths) {
1546
+ for (const filePath of featurePaths) {
1547
+ const fullPath = path5.join(frontendDir, filePath);
1548
+ if (fs5.existsSync(fullPath)) {
1549
+ fs5.rmSync(fullPath, { recursive: true, force: true });
1550
+ }
1551
+ }
1552
+ }
1553
+ }
1554
+ }
1555
+ }
757
1556
  function cleanupFullstackDockerFiles(projectPath) {
758
1557
  const filesToDelete = [
759
1558
  "backend/Dockerfile",
@@ -907,15 +1706,18 @@ async function main() {
907
1706
  const needsNamespace = projectType !== "frontend" && (backend === "dotnet" || backend === "spring");
908
1707
  if (cliArgs.projectPath && cliArgs.backend && (!needsNamespace || cliArgs.projectName)) {
909
1708
  const frontendFramework = cliArgs.framework || "vue";
1709
+ const architecture = cliArgs.architecture || "clean";
910
1710
  config = {
911
1711
  projectPath: cliArgs.projectPath,
912
1712
  projectType,
913
1713
  backend,
1714
+ architecture,
914
1715
  frontendFramework,
915
1716
  ui: cliArgs.ui || (frontendFramework === "vue" ? "vuetify" : "mui"),
916
1717
  projectName: cliArgs.projectName,
917
1718
  installDeps: cliArgs.install || false,
918
- placeInRoot: cliArgs.root || false
1719
+ placeInRoot: cliArgs.root || false,
1720
+ features: cliArgs.features || [...ALL_FEATURES]
919
1721
  };
920
1722
  } else {
921
1723
  const result = await runInteractivePrompts(cliArgs);
@@ -944,15 +1746,18 @@ ${pc3.bold("Usage:")}
944
1746
  ${pc3.cyan("npm create apptemplate@latest")} ${pc3.gray("[project-directory]")} ${pc3.gray("[options]")}
945
1747
 
946
1748
  ${pc3.bold("Options:")}
947
- ${pc3.yellow("-t, --type")} Project type: fullstack, backend, frontend ${pc3.gray("(default: fullstack)")}
948
- ${pc3.yellow("-b, --backend")} Backend framework: dotnet, spring, nestjs
949
- ${pc3.yellow("-f, --framework")} Frontend framework: vue, react ${pc3.gray("(default: vue)")}
950
- ${pc3.yellow("-u, --ui")} UI library: vuetify, primevue (Vue) | mui, primereact (React)
951
- ${pc3.yellow("-n, --name")} Project namespace (Company.Project format, .NET/Spring only)
952
- ${pc3.yellow("-r, --root")} Place files in project root ${pc3.gray("(backend/frontend-only)")}
953
- ${pc3.yellow("-i, --install")} Install dependencies after creation
954
- ${pc3.yellow("-h, --help")} Show this help message
955
- ${pc3.yellow("-v, --version")} Show version number
1749
+ ${pc3.yellow("-t, --type")} Project type: fullstack, backend, frontend ${pc3.gray("(default: fullstack)")}
1750
+ ${pc3.yellow("-b, --backend")} Backend framework: dotnet, spring, nestjs
1751
+ ${pc3.yellow("-a, --architecture")} Architecture: clean, nlayer, feature ${pc3.gray("(default: clean)")}
1752
+ ${pc3.yellow("-f, --framework")} Frontend framework: vue, react ${pc3.gray("(default: vue)")}
1753
+ ${pc3.yellow("-u, --ui")} UI library: vuetify, primevue (Vue) | mui, primereact (React)
1754
+ ${pc3.yellow("-n, --name")} Project namespace (Company.Project format, .NET/Spring only)
1755
+ ${pc3.yellow("-r, --root")} Place files in project root ${pc3.gray("(backend/frontend-only)")}
1756
+ ${pc3.yellow("--features")} Features to include (comma-separated or "all")
1757
+ Options: auth,userManagement,departments,fileUpload,auditLogs,notifications,dataExport,dashboard
1758
+ ${pc3.yellow("-i, --install")} Install dependencies after creation
1759
+ ${pc3.yellow("-h, --help")} Show this help message
1760
+ ${pc3.yellow("-v, --version")} Show version number
956
1761
 
957
1762
  ${pc3.bold("Examples:")}
958
1763
  ${pc3.gray("# Interactive mode")}