@abuhannaa/create-apptemplate 1.0.11 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,676 +4,13 @@
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
- "src/Core/App.Template.Application/DTOs/UserDto.cs"
94
- // Required by Auth
95
- ],
96
- frontend: []
97
- // Frontend auth files handled by pattern below
98
- },
99
- userManagement: {
100
- backend: [
101
- "src/Core/App.Template.Application/Features/UserManagement",
102
- // 'src/Core/App.Template.Application/DTOs/UserDto.cs', // Moved to auth
103
- "src/Presentation/App.Template.WebAPI/Controllers/UsersController.cs"
104
- ],
105
- frontend: []
106
- },
107
- departments: {
108
- backend: [
109
- "src/Core/App.Template.Application/Features/DepartmentManagement",
110
- "src/Core/App.Template.Application/DTOs/DepartmentDto.cs",
111
- "src/Core/App.Template.Application/Interfaces/IOrganizationService.cs",
112
- "src/Infrastructure/App.Template.Infrastructure/Services/OrganizationService.cs",
113
- "src/Presentation/App.Template.WebAPI/Controllers/DepartmentsController.cs"
114
- ],
115
- frontend: []
116
- },
117
- fileUpload: {
118
- backend: [
119
- "src/Core/App.Template.Application/Features/FileManagement",
120
- "src/Core/App.Template.Application/DTOs/UploadedFileDto.cs",
121
- "src/Core/App.Template.Application/Interfaces/IFileStorageService.cs",
122
- "src/Infrastructure/App.Template.Infrastructure/Services/FileStorageService.cs",
123
- "src/Presentation/App.Template.WebAPI/Controllers/FilesController.cs"
124
- ],
125
- frontend: []
126
- },
127
- auditLogs: {
128
- backend: [
129
- "src/Core/App.Template.Application/Features/AuditLogManagement",
130
- "src/Core/App.Template.Application/DTOs/AuditLogDto.cs",
131
- "src/Infrastructure/App.Template.Infrastructure/Persistence/AuditEntry.cs",
132
- "src/Presentation/App.Template.WebAPI/Controllers/AuditLogsController.cs"
133
- ],
134
- frontend: []
135
- },
136
- notifications: {
137
- backend: [
138
- "src/Core/App.Template.Application/Features/NotificationManagement",
139
- "src/Core/App.Template.Application/DTOs/NotificationDto.cs",
140
- "src/Core/App.Template.Application/Interfaces/INotificationService.cs",
141
- "src/Infrastructure/App.Template.Infrastructure/Services/NotificationService.cs",
142
- "src/Infrastructure/App.Template.Infrastructure/Hubs",
143
- "src/Infrastructure/App.Template.Infrastructure/SignalR",
144
- "src/Presentation/App.Template.WebAPI/Controllers/NotificationsController.cs"
145
- ],
146
- frontend: []
147
- },
148
- dataExport: {
149
- backend: [
150
- "src/Core/App.Template.Application/Interfaces/IExportService.cs",
151
- "src/Infrastructure/App.Template.Infrastructure/Services/ExportService.cs",
152
- "src/Presentation/App.Template.WebAPI/Controllers/ExportController.cs"
153
- ],
154
- frontend: []
155
- },
156
- dashboard: {
157
- backend: [],
158
- frontend: []
159
- }
160
- };
161
- var DOTNET_NLAYER_MAP = {
162
- auth: {
163
- backend: [
164
- "src/App.Template.Api/Controllers/AuthController.cs",
165
- "src/App.Template.Api/Services/AuthService.cs",
166
- "src/App.Template.Api/Services/JwtTokenGenerator.cs",
167
- "src/App.Template.Api/Models/Dtos/AuthDtos.cs",
168
- "src/App.Template.Api/Models/Dtos/UserDtos.cs"
169
- // Required by Auth
170
- ],
171
- frontend: []
172
- },
173
- userManagement: {
174
- backend: [
175
- "src/App.Template.Api/Controllers/UsersController.cs",
176
- "src/App.Template.Api/Services/UserService.cs",
177
- "src/App.Template.Api/Services/IUserService.cs",
178
- "src/App.Template.Api/Repositories/UserRepository.cs",
179
- "src/App.Template.Api/Repositories/IUserRepository.cs"
180
- // 'src/App.Template.Api/Models/Dtos/UserDtos.cs', // Moved to auth
181
- ],
182
- frontend: []
183
- },
184
- departments: {
185
- backend: [
186
- "src/App.Template.Api/Controllers/DepartmentsController.cs",
187
- "src/App.Template.Api/Models/Dtos/DepartmentDtos.cs"
188
- ],
189
- frontend: []
190
- },
191
- fileUpload: {
192
- backend: [
193
- "src/App.Template.Api/Controllers/FilesController.cs"
194
- ],
195
- frontend: []
196
- },
197
- auditLogs: {
198
- backend: [],
199
- frontend: []
200
- },
201
- notifications: {
202
- backend: [
203
- "src/App.Template.Api/Controllers/NotificationsController.cs",
204
- "src/App.Template.Api/Infrastructure/Hubs"
205
- ],
206
- frontend: []
207
- },
208
- dataExport: {
209
- backend: [
210
- "src/App.Template.Api/Controllers/ExportController.cs"
211
- ],
212
- frontend: []
213
- },
214
- dashboard: {
215
- backend: [],
216
- frontend: []
217
- }
218
- };
219
- var DOTNET_FEATURE_MAP = {
220
- auth: {
221
- backend: [
222
- "src/App.Template.Api/Features/Auth"
223
- ],
224
- frontend: []
225
- },
226
- userManagement: {
227
- backend: [
228
- // 'src/App.Template.Api/Features/Users', // Don't remove whole folder, Auth needs Entity/Repo/DTOs
229
- "src/App.Template.Api/Features/Users/UsersController.cs",
230
- "src/App.Template.Api/Features/Users/UserService.cs",
231
- "src/App.Template.Api/Features/Users/IUserService.cs"
232
- ],
233
- frontend: []
234
- },
235
- departments: {
236
- backend: [
237
- "src/App.Template.Api/Features/Departments"
238
- ],
239
- frontend: []
240
- },
241
- fileUpload: {
242
- backend: [
243
- "src/App.Template.Api/Features/Files"
244
- ],
245
- frontend: []
246
- },
247
- auditLogs: {
248
- backend: [],
249
- frontend: []
250
- },
251
- notifications: {
252
- backend: [
253
- "src/App.Template.Api/Features/Notifications"
254
- ],
255
- frontend: []
256
- },
257
- dataExport: {
258
- backend: [
259
- "src/App.Template.Api/Features/Export"
260
- ],
261
- frontend: []
262
- },
263
- dashboard: {
264
- backend: [],
265
- frontend: []
266
- }
267
- };
268
- var SPRING_CLEAN_MAP = {
269
- auth: {
270
- backend: [
271
- "api/src/main/java/apptemplate/api/controllers/AuthController.java"
272
- ],
273
- frontend: []
274
- },
275
- userManagement: {
276
- backend: [
277
- "api/src/main/java/apptemplate/api/controllers/UsersController.java"
278
- ],
279
- frontend: []
280
- },
281
- departments: {
282
- backend: [
283
- "api/src/main/java/apptemplate/api/controllers/DepartmentsController.java"
284
- ],
285
- frontend: []
286
- },
287
- fileUpload: {
288
- backend: [
289
- "api/src/main/java/apptemplate/api/controllers/FilesController.java"
290
- ],
291
- frontend: []
292
- },
293
- auditLogs: {
294
- backend: [
295
- "api/src/main/java/apptemplate/api/controllers/AuditLogsController.java"
296
- ],
297
- frontend: []
298
- },
299
- notifications: {
300
- backend: [
301
- "api/src/main/java/apptemplate/api/controllers/NotificationsController.java"
302
- ],
303
- frontend: []
304
- },
305
- dataExport: {
306
- backend: [
307
- "api/src/main/java/apptemplate/api/controllers/ExportController.java"
308
- ],
309
- frontend: []
310
- },
311
- dashboard: {
312
- backend: [],
313
- frontend: []
314
- }
315
- };
316
- var SPRING_NLAYER_MAP = {
317
- auth: {
318
- backend: [
319
- "src/main/java/com/apptemplate/api/controller/AuthController.java"
320
- ],
321
- frontend: []
322
- },
323
- userManagement: {
324
- backend: [
325
- "src/main/java/com/apptemplate/api/controller/UserController.java"
326
- ],
327
- frontend: []
328
- },
329
- departments: {
330
- backend: [
331
- "src/main/java/com/apptemplate/api/controller/DepartmentController.java"
332
- ],
333
- frontend: []
334
- },
335
- fileUpload: {
336
- backend: [
337
- "src/main/java/com/apptemplate/api/controller/FileController.java"
338
- ],
339
- frontend: []
340
- },
341
- auditLogs: {
342
- backend: [
343
- "src/main/java/com/apptemplate/api/audit"
344
- ],
345
- frontend: []
346
- },
347
- notifications: {
348
- backend: [
349
- "src/main/java/com/apptemplate/api/controller/NotificationController.java"
350
- ],
351
- frontend: []
352
- },
353
- dataExport: {
354
- backend: [
355
- "src/main/java/com/apptemplate/api/controller/ExportController.java"
356
- ],
357
- frontend: []
358
- },
359
- dashboard: {
360
- backend: [],
361
- frontend: []
362
- }
363
- };
364
- var SPRING_FEATURE_MAP = {
365
- auth: {
366
- backend: [
367
- "src/main/java/com/apptemplate/api/features/auth"
368
- ],
369
- frontend: []
370
- },
371
- userManagement: {
372
- backend: [
373
- "src/main/java/com/apptemplate/api/features/users"
374
- ],
375
- frontend: []
376
- },
377
- departments: {
378
- backend: [
379
- "src/main/java/com/apptemplate/api/features/departments"
380
- ],
381
- frontend: []
382
- },
383
- fileUpload: {
384
- backend: [
385
- "src/main/java/com/apptemplate/api/features/files"
386
- ],
387
- frontend: []
388
- },
389
- auditLogs: {
390
- backend: [
391
- "src/main/java/com/apptemplate/api/common/audit"
392
- ],
393
- frontend: []
394
- },
395
- notifications: {
396
- backend: [
397
- "src/main/java/com/apptemplate/api/features/notifications"
398
- ],
399
- frontend: []
400
- },
401
- dataExport: {
402
- backend: [
403
- "src/main/java/com/apptemplate/api/features/export"
404
- ],
405
- frontend: []
406
- },
407
- dashboard: {
408
- backend: [],
409
- frontend: []
410
- }
411
- };
412
- var NESTJS_CLEAN_MAP = {
413
- auth: {
414
- backend: [
415
- "src/modules/auth"
416
- ],
417
- frontend: []
418
- },
419
- userManagement: {
420
- backend: [
421
- "src/modules/user-management"
422
- ],
423
- frontend: []
424
- },
425
- departments: {
426
- backend: [
427
- "src/modules/department-management"
428
- ],
429
- frontend: []
430
- },
431
- fileUpload: {
432
- backend: [
433
- "src/modules/file-management"
434
- ],
435
- frontend: []
436
- },
437
- auditLogs: {
438
- backend: [
439
- "src/modules/audit-log"
440
- ],
441
- frontend: []
442
- },
443
- notifications: {
444
- backend: [
445
- "src/modules/notification"
446
- ],
447
- frontend: []
448
- },
449
- dataExport: {
450
- backend: [
451
- "src/modules/export"
452
- ],
453
- frontend: []
454
- },
455
- dashboard: {
456
- backend: [],
457
- frontend: []
458
- }
459
- };
460
- var NESTJS_NLAYER_MAP = {
461
- auth: {
462
- backend: [
463
- "src/auth"
464
- ],
465
- frontend: []
466
- },
467
- userManagement: {
468
- backend: [],
469
- frontend: []
470
- },
471
- departments: {
472
- backend: [
473
- "src/modules/departments"
474
- ],
475
- frontend: []
476
- },
477
- fileUpload: {
478
- backend: [
479
- "src/modules/files"
480
- ],
481
- frontend: []
482
- },
483
- auditLogs: {
484
- backend: [],
485
- frontend: []
486
- },
487
- notifications: {
488
- backend: [
489
- "src/modules/notifications"
490
- ],
491
- frontend: []
492
- },
493
- dataExport: {
494
- backend: [
495
- "src/modules/export"
496
- ],
497
- frontend: []
498
- },
499
- dashboard: {
500
- backend: [],
501
- frontend: []
502
- }
503
- };
504
- var NESTJS_FEATURE_MAP = {
505
- auth: {
506
- backend: [
507
- "src/features/auth"
508
- ],
509
- frontend: []
510
- },
511
- userManagement: {
512
- backend: [
513
- "src/features/users"
514
- ],
515
- frontend: []
516
- },
517
- departments: {
518
- backend: [
519
- "src/features/departments"
520
- ],
521
- frontend: []
522
- },
523
- fileUpload: {
524
- backend: [
525
- "src/features/files"
526
- ],
527
- frontend: []
528
- },
529
- auditLogs: {
530
- backend: [],
531
- frontend: []
532
- },
533
- notifications: {
534
- backend: [
535
- "src/features/notifications"
536
- ],
537
- frontend: []
538
- },
539
- dataExport: {
540
- backend: [
541
- "src/features/export"
542
- ],
543
- frontend: []
544
- },
545
- dashboard: {
546
- backend: [],
547
- frontend: []
548
- }
549
- };
550
- var VUE_FRONTEND_MAP = {
551
- auth: [
552
- "src/pages/login.vue",
553
- "src/pages/forgot-password.vue",
554
- "src/pages/reset-password.vue",
555
- "src/pages/profile.vue",
556
- "src/stores/auth.js",
557
- "src/services/authApi.js"
558
- ],
559
- userManagement: [
560
- "src/pages/users",
561
- "src/stores/user.js",
562
- "src/services/userApi.js"
563
- ],
564
- departments: [
565
- "src/pages/departments",
566
- "src/stores/department.js",
567
- "src/services/departmentApi.js"
568
- ],
569
- fileUpload: [
570
- "src/pages/files",
571
- "src/services/fileService.js"
572
- ],
573
- auditLogs: [
574
- "src/pages/audit-logs",
575
- "src/services/auditLogService.js"
576
- ],
577
- notifications: [
578
- "src/pages/notifications",
579
- "src/stores/notification.js",
580
- "src/stores/persistentNotification.js",
581
- "src/services/notificationApi.js"
582
- ],
583
- dataExport: [
584
- "src/services/exportService.js"
585
- ],
586
- dashboard: [
587
- "src/pages/dashboard.vue"
588
- ]
589
- };
590
- var REACT_FRONTEND_MAP = {
591
- auth: [
592
- "src/pages/Login.tsx",
593
- "src/pages/ForgotPassword.tsx",
594
- "src/pages/ResetPassword.tsx",
595
- "src/pages/Profile.tsx",
596
- "src/stores/authStore.ts",
597
- "src/services/authApi.ts"
598
- ],
599
- userManagement: [
600
- "src/pages/Users.tsx",
601
- "src/stores/userStore.ts",
602
- "src/services/userApi.ts"
603
- ],
604
- departments: [
605
- "src/pages/Departments.tsx",
606
- "src/stores/departmentStore.ts",
607
- "src/services/departmentApi.ts"
608
- ],
609
- fileUpload: [
610
- "src/pages/FilesPage.tsx",
611
- "src/services/fileService.ts"
612
- ],
613
- auditLogs: [
614
- "src/pages/AuditLogsPage.tsx",
615
- "src/pages/AuditLogs.tsx",
616
- "src/services/auditLogService.ts"
617
- ],
618
- notifications: [
619
- "src/pages/Notifications.tsx",
620
- "src/stores/notificationStore.ts",
621
- "src/stores/persistentNotificationStore.ts",
622
- "src/services/notificationApi.ts"
623
- ],
624
- dataExport: [
625
- "src/services/exportService.ts"
626
- ],
627
- dashboard: [
628
- "src/pages/Dashboard.tsx",
629
- "src/pages/Dashboard.scss"
630
- ]
631
- };
632
- function getBackendFileMap(backend, architecture) {
633
- const key = `${backend}-${architecture}`;
634
- switch (key) {
635
- case "dotnet-clean":
636
- return DOTNET_CLEAN_MAP;
637
- case "dotnet-nlayer":
638
- return DOTNET_NLAYER_MAP;
639
- case "dotnet-feature":
640
- return DOTNET_FEATURE_MAP;
641
- case "spring-clean":
642
- return SPRING_CLEAN_MAP;
643
- case "spring-nlayer":
644
- return SPRING_NLAYER_MAP;
645
- case "spring-feature":
646
- return SPRING_FEATURE_MAP;
647
- case "nestjs-clean":
648
- return NESTJS_CLEAN_MAP;
649
- case "nestjs-nlayer":
650
- return NESTJS_NLAYER_MAP;
651
- case "nestjs-feature":
652
- return NESTJS_FEATURE_MAP;
653
- default:
654
- return {};
655
- }
656
- }
657
- function getFrontendFileMap(frontendFramework) {
658
- return frontendFramework === "vue" ? VUE_FRONTEND_MAP : REACT_FRONTEND_MAP;
659
- }
660
- function getFeaturesLabel(features, allFeatures) {
661
- if (features.length === allFeatures.length) {
662
- return "All features";
663
- }
664
- if (features.length === 0) {
665
- return "None (bare template)";
666
- }
667
- return features.map((f) => FEATURE_OPTIONS.find((o) => o.value === f)?.label || f).join(", ");
668
- }
669
-
670
7
  // src/cli.ts
671
8
  var validProjectTypes = ["fullstack", "backend", "frontend"];
672
9
  var validBackends = ["dotnet", "spring", "nestjs"];
673
10
  var validArchitectures = ["clean", "nlayer", "feature"];
674
11
  var validFrontendFrameworks = ["vue", "react"];
675
12
  var validUILibraries = ["vuetify", "primevue", "primereact", "mui"];
676
- var validFeatures = [...ALL_FEATURES];
13
+ var validVariants = ["minimal", "full"];
677
14
  function parseArgs() {
678
15
  const args = process.argv.slice(2);
679
16
  const result = {};
@@ -760,20 +97,12 @@ function parseArgs() {
760
97
  i++;
761
98
  continue;
762
99
  }
763
- if (arg === "--features" || arg === "--feat") {
100
+ if (arg === "-V" || arg === "--variant") {
764
101
  const value = args[++i];
765
- if (value) {
766
- if (value === "all") {
767
- result.features = [...ALL_FEATURES];
768
- } else {
769
- const requested = value.split(",").map((s) => s.trim());
770
- const valid = requested.filter((f) => validFeatures.includes(f));
771
- const invalid = requested.filter((f) => !validFeatures.includes(f));
772
- if (invalid.length > 0) {
773
- console.warn(`Warning: Invalid features "${invalid.join(", ")}". Valid options: ${validFeatures.join(", ")}`);
774
- }
775
- result.features = applyFeatureDependencies(valid);
776
- }
102
+ if (isValidVariant(value)) {
103
+ result.variant = value;
104
+ } else {
105
+ console.warn(`Warning: Invalid variant "${value}". Valid options: ${validVariants.join(", ")}`);
777
106
  }
778
107
  i++;
779
108
  continue;
@@ -800,6 +129,9 @@ function isValidFrontendFramework(value) {
800
129
  function isValidUI(value) {
801
130
  return value !== void 0 && validUILibraries.includes(value);
802
131
  }
132
+ function isValidVariant(value) {
133
+ return value !== void 0 && validVariants.includes(value);
134
+ }
803
135
  function isValidProjectName(value) {
804
136
  if (!value) return false;
805
137
  const pattern = /^[A-Za-z][A-Za-z0-9]*(\.[A-Za-z][A-Za-z0-9]*)+$/;
@@ -958,25 +290,25 @@ async function runInteractivePrompts(cliArgs) {
958
290
  if (p.isCancel(result)) return result;
959
291
  ui = result;
960
292
  }
961
- let features = cliArgs.features || [...ALL_FEATURES];
962
- if (!cliArgs.features) {
963
- const result = await p.multiselect({
964
- message: "Which features would you like to include?",
965
- options: FEATURE_OPTIONS.map((opt) => ({
966
- value: opt.value,
967
- label: opt.label,
968
- hint: opt.hint
969
- })),
970
- initialValues: [...ALL_FEATURES],
971
- required: false
293
+ let variant = cliArgs.variant || "full";
294
+ if (!cliArgs.variant) {
295
+ const result = await p.select({
296
+ message: "Which template variant would you like?",
297
+ options: [
298
+ {
299
+ value: "full",
300
+ label: "Full",
301
+ hint: "User management, departments, dashboard, all features"
302
+ },
303
+ {
304
+ value: "minimal",
305
+ label: "Minimal",
306
+ hint: "Auth, files, audit logs, notifications (no user/dept management)"
307
+ }
308
+ ]
972
309
  });
973
310
  if (p.isCancel(result)) return result;
974
- features = applyFeatureDependencies(result);
975
- if (features.length < result.length) {
976
- const removed = result.filter((f) => !features.includes(f));
977
- const removedLabels = removed.map((f) => FEATURE_OPTIONS.find((o) => o.value === f)?.label || f);
978
- console.log(pc.yellow(` \u26A0 Auto-removed due to dependencies: ${removedLabels.join(", ")}`));
979
- }
311
+ variant = result;
980
312
  }
981
313
  let projectName = cliArgs.projectName;
982
314
  const needsNamespace = projectType !== "frontend" && (backend === "dotnet" || backend === "spring");
@@ -1030,7 +362,7 @@ async function runInteractivePrompts(cliArgs) {
1030
362
  summaryLines.push(`${pc.cyan("Frontend:")} ${getFrontendLabel(frontendFramework)}`);
1031
363
  summaryLines.push(`${pc.cyan("UI Library:")} ${getUILabel(ui)}`);
1032
364
  }
1033
- summaryLines.push(`${pc.cyan("Features:")} ${getFeaturesLabel(features, ALL_FEATURES)}`);
365
+ summaryLines.push(`${pc.cyan("Template:")} ${getVariantLabel(variant)}`);
1034
366
  if (needsNamespace && projectName) {
1035
367
  summaryLines.push(`${pc.cyan("Namespace:")} ${projectName}`);
1036
368
  }
@@ -1056,7 +388,7 @@ async function runInteractivePrompts(cliArgs) {
1056
388
  projectName,
1057
389
  installDeps,
1058
390
  placeInRoot,
1059
- features
391
+ variant
1060
392
  };
1061
393
  }
1062
394
  function toPascalCase(str) {
@@ -1094,6 +426,13 @@ function getUILabel(ui) {
1094
426
  };
1095
427
  return labels[ui];
1096
428
  }
429
+ function getVariantLabel(variant) {
430
+ const labels = {
431
+ full: "Full (all features)",
432
+ minimal: "Minimal (no user/dept management)"
433
+ };
434
+ return labels[variant];
435
+ }
1097
436
 
1098
437
  // src/generator.ts
1099
438
  import * as p2 from "@clack/prompts";
@@ -1105,7 +444,18 @@ import fs5 from "fs";
1105
444
  import degit from "degit";
1106
445
  import path2 from "path";
1107
446
  import fs2 from "fs";
1108
- async function downloadTemplate(repo, folder, destPath) {
447
+ async function downloadBackendTemplate(repo, backend, architecture, variant, destPath) {
448
+ const folder = `backend/${backend}/${architecture}-architecture/${variant}`;
449
+ const source = `${repo}/${folder}`;
450
+ const emitter = degit(source, {
451
+ cache: false,
452
+ force: true,
453
+ verbose: false
454
+ });
455
+ await emitter.clone(destPath);
456
+ }
457
+ async function downloadFrontendTemplate(repo, framework, ui, variant, destPath) {
458
+ const folder = `frontend/${framework}/${ui}/${variant}`;
1109
459
  const source = `${repo}/${folder}`;
1110
460
  const emitter = degit(source, {
1111
461
  cache: false,
@@ -1433,8 +783,6 @@ async function generateProject(config) {
1433
783
  spinner2.start("Downloading templates...");
1434
784
  try {
1435
785
  if (config.projectType !== "frontend") {
1436
- const archSuffix = config.architecture === "clean" ? "" : `-${config.architecture}`;
1437
- const sourceFolder = `backend-${config.backend}${archSuffix}`;
1438
786
  let destFolder;
1439
787
  if (config.projectType === "fullstack") {
1440
788
  destFolder = "backend";
@@ -1442,11 +790,10 @@ async function generateProject(config) {
1442
790
  destFolder = config.placeInRoot ? "" : "backend";
1443
791
  }
1444
792
  const destPath = destFolder ? path5.join(absolutePath, destFolder) : absolutePath;
1445
- await downloadTemplate(REPO, sourceFolder, destPath);
1446
- spinner2.message(`Downloaded ${sourceFolder}`);
793
+ await downloadBackendTemplate(REPO, config.backend, config.architecture, config.variant, destPath);
794
+ spinner2.message(`Downloaded backend-${config.backend}-${config.architecture}-${config.variant}`);
1447
795
  }
1448
796
  if (config.projectType !== "backend") {
1449
- const sourceFolder = `frontend-${config.ui}`;
1450
797
  let destFolder;
1451
798
  if (config.projectType === "fullstack") {
1452
799
  destFolder = "frontend";
@@ -1454,8 +801,8 @@ async function generateProject(config) {
1454
801
  destFolder = config.placeInRoot ? "" : "frontend";
1455
802
  }
1456
803
  const destPath = destFolder ? path5.join(absolutePath, destFolder) : absolutePath;
1457
- await downloadTemplate(REPO, sourceFolder, destPath);
1458
- spinner2.message(`Downloaded ${sourceFolder}`);
804
+ await downloadFrontendTemplate(REPO, config.frontendFramework, config.ui, config.variant, destPath);
805
+ spinner2.message(`Downloaded frontend-${config.frontendFramework}-${config.ui}-${config.variant}`);
1459
806
  }
1460
807
  await copyRootFiles(REPO, absolutePath, config);
1461
808
  spinner2.message("Downloaded configuration files");
@@ -1464,16 +811,6 @@ async function generateProject(config) {
1464
811
  spinner2.stop("Download failed");
1465
812
  throw error;
1466
813
  }
1467
- const deselectedFeatures = ALL_FEATURES.filter((f) => !config.features.includes(f));
1468
- if (deselectedFeatures.length > 0) {
1469
- spinner2.start("Removing deselected feature files...");
1470
- try {
1471
- await removeDeselectedFeatures(absolutePath, config, deselectedFeatures);
1472
- spinner2.stop(`Removed ${deselectedFeatures.length} deselected feature(s)`);
1473
- } catch (error) {
1474
- spinner2.stop("Feature removal had warnings");
1475
- }
1476
- }
1477
814
  spinner2.start("Updating configuration files...");
1478
815
  try {
1479
816
  await updateFolderReferences(absolutePath, config);
@@ -1518,48 +855,6 @@ async function generateProject(config) {
1518
855
  await createAppSettingsFromExample(absolutePath, config);
1519
856
  }
1520
857
  }
1521
- async function removeDeselectedFeatures(projectPath, config, deselectedFeatures) {
1522
- if (config.projectType !== "frontend") {
1523
- const backendMap = getBackendFileMap(config.backend, config.architecture);
1524
- let backendDir;
1525
- if (config.projectType === "fullstack") {
1526
- backendDir = path5.join(projectPath, "backend");
1527
- } else {
1528
- backendDir = config.placeInRoot ? projectPath : path5.join(projectPath, "backend");
1529
- }
1530
- for (const feature of deselectedFeatures) {
1531
- const featureMap = backendMap[feature];
1532
- if (featureMap) {
1533
- for (const filePath of featureMap.backend) {
1534
- const fullPath = path5.join(backendDir, filePath);
1535
- if (fs5.existsSync(fullPath)) {
1536
- fs5.rmSync(fullPath, { recursive: true, force: true });
1537
- }
1538
- }
1539
- }
1540
- }
1541
- }
1542
- if (config.projectType !== "backend") {
1543
- const frontendMap = getFrontendFileMap(config.frontendFramework);
1544
- let frontendDir;
1545
- if (config.projectType === "fullstack") {
1546
- frontendDir = path5.join(projectPath, "frontend");
1547
- } else {
1548
- frontendDir = config.placeInRoot ? projectPath : path5.join(projectPath, "frontend");
1549
- }
1550
- for (const feature of deselectedFeatures) {
1551
- const featurePaths = frontendMap[feature];
1552
- if (featurePaths) {
1553
- for (const filePath of featurePaths) {
1554
- const fullPath = path5.join(frontendDir, filePath);
1555
- if (fs5.existsSync(fullPath)) {
1556
- fs5.rmSync(fullPath, { recursive: true, force: true });
1557
- }
1558
- }
1559
- }
1560
- }
1561
- }
1562
- }
1563
858
  function cleanupFullstackDockerFiles(projectPath) {
1564
859
  const filesToDelete = [
1565
860
  "backend/Dockerfile",
@@ -1724,7 +1019,7 @@ async function main() {
1724
1019
  projectName: cliArgs.projectName,
1725
1020
  installDeps: cliArgs.install || false,
1726
1021
  placeInRoot: cliArgs.root || false,
1727
- features: cliArgs.features || [...ALL_FEATURES]
1022
+ variant: cliArgs.variant || "full"
1728
1023
  };
1729
1024
  } else {
1730
1025
  const result = await runInteractivePrompts(cliArgs);
@@ -1760,8 +1055,9 @@ ${pc3.bold("Options:")}
1760
1055
  ${pc3.yellow("-u, --ui")} UI library: vuetify, primevue (Vue) | mui, primereact (React)
1761
1056
  ${pc3.yellow("-n, --name")} Project namespace (Company.Project format, .NET/Spring only)
1762
1057
  ${pc3.yellow("-r, --root")} Place files in project root ${pc3.gray("(backend/frontend-only)")}
1763
- ${pc3.yellow("--features")} Features to include (comma-separated or "all")
1764
- Options: auth,userManagement,departments,fileUpload,auditLogs,notifications,dataExport,dashboard
1058
+ ${pc3.yellow("-V, --variant")} Template variant: full, minimal ${pc3.gray("(default: full)")}
1059
+ full: All features (user management, departments, dashboard)
1060
+ minimal: Auth, files, audit logs, notifications only
1765
1061
  ${pc3.yellow("-i, --install")} Install dependencies after creation
1766
1062
  ${pc3.yellow("-h, --help")} Show this help message
1767
1063
  ${pc3.yellow("-v, --version")} Show version number
@@ -1773,8 +1069,8 @@ ${pc3.bold("Examples:")}
1773
1069
  ${pc3.gray("# Create fullstack project with .NET backend")}
1774
1070
  npm create apptemplate@latest my-app -b dotnet -n "MyCompany.MyApp" -i
1775
1071
 
1776
- ${pc3.gray("# Create backend-only project with Spring Boot")}
1777
- npm create apptemplate@latest my-api -t backend -b spring -n "MyCompany.MyApi"
1072
+ ${pc3.gray("# Create minimal backend-only project (no user management)")}
1073
+ npm create apptemplate@latest my-api -t backend -b spring -n "MyCompany.MyApi" -V minimal
1778
1074
 
1779
1075
  ${pc3.gray("# Create frontend-only project with PrimeVue")}
1780
1076
  npm create apptemplate@latest my-spa -t frontend -u primevue