@digilogiclabs/create-saas-app 2.2.0 → 2.2.1

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.
Files changed (77) hide show
  1. package/bin/index.js +2 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/cli/commands/add.d.ts +6 -0
  4. package/dist/cli/commands/add.d.ts.map +1 -0
  5. package/dist/cli/commands/add.js +39 -0
  6. package/dist/cli/commands/add.js.map +1 -0
  7. package/dist/cli/commands/create.d.ts +45 -0
  8. package/dist/cli/commands/create.d.ts.map +1 -0
  9. package/dist/cli/commands/create.js +175 -0
  10. package/dist/cli/commands/create.js.map +1 -0
  11. package/dist/cli/commands/index.d.ts +4 -0
  12. package/dist/cli/commands/index.d.ts.map +1 -0
  13. package/dist/cli/commands/index.js +20 -0
  14. package/dist/cli/commands/index.js.map +1 -0
  15. package/dist/cli/commands/update.d.ts +6 -0
  16. package/dist/cli/commands/update.d.ts.map +1 -0
  17. package/dist/cli/commands/update.js +68 -0
  18. package/dist/cli/commands/update.js.map +1 -0
  19. package/dist/cli/index.d.ts +4 -0
  20. package/dist/cli/index.d.ts.map +1 -0
  21. package/dist/cli/index.js +61 -0
  22. package/dist/cli/index.js.map +1 -0
  23. package/dist/cli/prompts/index.d.ts +2 -0
  24. package/dist/cli/prompts/index.d.ts.map +1 -0
  25. package/dist/cli/prompts/index.js +18 -0
  26. package/dist/cli/prompts/index.js.map +1 -0
  27. package/dist/cli/prompts/project-setup.d.ts +5 -0
  28. package/dist/cli/prompts/project-setup.d.ts.map +1 -0
  29. package/dist/cli/prompts/project-setup.js +359 -0
  30. package/dist/cli/prompts/project-setup.js.map +1 -0
  31. package/dist/cli/utils/git.d.ts +9 -0
  32. package/dist/cli/utils/git.d.ts.map +1 -0
  33. package/dist/cli/utils/git.js +77 -0
  34. package/dist/cli/utils/git.js.map +1 -0
  35. package/dist/cli/utils/index.d.ts +5 -0
  36. package/dist/cli/utils/index.d.ts.map +1 -0
  37. package/dist/cli/utils/index.js +21 -0
  38. package/dist/cli/utils/index.js.map +1 -0
  39. package/dist/cli/utils/logger.d.ts +16 -0
  40. package/dist/cli/utils/logger.d.ts.map +1 -0
  41. package/dist/cli/utils/logger.js +55 -0
  42. package/dist/cli/utils/logger.js.map +1 -0
  43. package/dist/cli/utils/package-manager.d.ts +8 -0
  44. package/dist/cli/utils/package-manager.d.ts.map +1 -0
  45. package/dist/cli/utils/package-manager.js +92 -0
  46. package/dist/cli/utils/package-manager.js.map +1 -0
  47. package/dist/cli/utils/spinner.d.ts +7 -0
  48. package/dist/cli/utils/spinner.d.ts.map +1 -0
  49. package/dist/cli/utils/spinner.js +48 -0
  50. package/dist/cli/utils/spinner.js.map +1 -0
  51. package/dist/cli/validators/dependencies.d.ts +15 -0
  52. package/dist/cli/validators/dependencies.d.ts.map +1 -0
  53. package/dist/cli/validators/dependencies.js +108 -0
  54. package/dist/cli/validators/dependencies.js.map +1 -0
  55. package/dist/cli/validators/index.d.ts +3 -0
  56. package/dist/cli/validators/index.d.ts.map +1 -0
  57. package/dist/cli/validators/index.js +19 -0
  58. package/dist/cli/validators/index.js.map +1 -0
  59. package/dist/cli/validators/project-name.d.ts +5 -0
  60. package/dist/cli/validators/project-name.d.ts.map +1 -0
  61. package/dist/cli/validators/project-name.js +151 -0
  62. package/dist/cli/validators/project-name.js.map +1 -0
  63. package/dist/generators/file-processor.d.ts +28 -0
  64. package/dist/generators/file-processor.d.ts.map +1 -0
  65. package/dist/generators/file-processor.js +224 -0
  66. package/dist/generators/file-processor.js.map +1 -0
  67. package/dist/generators/index.d.ts +4 -0
  68. package/dist/generators/index.d.ts.map +1 -0
  69. package/dist/generators/index.js +20 -0
  70. package/dist/generators/index.js.map +1 -0
  71. package/dist/generators/package-installer.d.ts +29 -0
  72. package/dist/generators/package-installer.d.ts.map +1 -0
  73. package/dist/generators/package-installer.js +177 -0
  74. package/dist/generators/package-installer.js.map +1 -0
  75. package/package.json +1 -1
  76. package/dist/index.js +0 -1837
  77. package/dist/index.js.map +0 -1
package/dist/index.js DELETED
@@ -1,1837 +0,0 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
-
30
- // src/cli/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
33
- default: () => index_default
34
- });
35
- module.exports = __toCommonJS(index_exports);
36
- var import_commander = require("commander");
37
-
38
- // src/cli/commands/create.ts
39
- var import_path5 = __toESM(require("path"));
40
-
41
- // src/cli/utils/logger.ts
42
- var import_chalk = __toESM(require("chalk"));
43
- var logger = {
44
- info: (message, ...args) => {
45
- console.log(import_chalk.default.blue("\u2139"), message, ...args);
46
- },
47
- success: (message, ...args) => {
48
- console.log(import_chalk.default.green("\u2713"), message, ...args);
49
- },
50
- warn: (message, ...args) => {
51
- console.log(import_chalk.default.yellow("\u26A0"), message, ...args);
52
- },
53
- error: (message, ...args) => {
54
- console.log(import_chalk.default.red("\u2717"), message, ...args);
55
- },
56
- debug: (message, ...args) => {
57
- if (process.env.DEBUG) {
58
- console.log(import_chalk.default.gray("\u{1F41B}"), message, ...args);
59
- }
60
- },
61
- log: (message, ...args) => {
62
- console.log(message, ...args);
63
- },
64
- newLine: () => {
65
- console.log();
66
- },
67
- divider: () => {
68
- console.log(import_chalk.default.gray("\u2500".repeat(50)));
69
- },
70
- title: (message) => {
71
- console.log();
72
- console.log(import_chalk.default.bold.cyan(message));
73
- console.log(import_chalk.default.gray("\u2500".repeat(message.length)));
74
- },
75
- step: (step, total, message) => {
76
- console.log(import_chalk.default.cyan(`[${step}/${total}]`), message);
77
- },
78
- highlight: (message) => {
79
- console.log(import_chalk.default.bold.magenta(message));
80
- },
81
- code: (code) => {
82
- console.log(import_chalk.default.gray(" " + code));
83
- },
84
- list: (items) => {
85
- items.forEach((item) => {
86
- console.log(import_chalk.default.gray(" \u2022"), item);
87
- });
88
- }
89
- };
90
-
91
- // src/cli/utils/spinner.ts
92
- var import_ora = __toESM(require("ora"));
93
- var import_chalk2 = __toESM(require("chalk"));
94
- function spinner(text) {
95
- return (0, import_ora.default)({
96
- text: import_chalk2.default.gray(text),
97
- spinner: "dots",
98
- color: "cyan"
99
- }).start();
100
- }
101
-
102
- // src/cli/validators/project-name.ts
103
- var import_validate_npm_package_name = __toESM(require("validate-npm-package-name"));
104
- var import_path = __toESM(require("path"));
105
- var import_fs_extra = __toESM(require("fs-extra"));
106
- function validateProjectName(name) {
107
- if (!name || name.trim().length === 0) {
108
- logger.error("Project name cannot be empty");
109
- return false;
110
- }
111
- const validation = (0, import_validate_npm_package_name.default)(name);
112
- if (!validation.validForNewPackages) {
113
- logger.error("Invalid project name:");
114
- if (validation.errors) {
115
- validation.errors.forEach((error) => logger.error(` \u2022 ${error}`));
116
- }
117
- if (validation.warnings) {
118
- validation.warnings.forEach((warning) => logger.warn(` \u2022 ${warning}`));
119
- }
120
- return false;
121
- }
122
- const reservedNames = [
123
- "node_modules",
124
- "package.json",
125
- "package-lock.json",
126
- "yarn.lock",
127
- "pnpm-lock.yaml",
128
- ".git",
129
- ".gitignore",
130
- "README.md",
131
- "LICENSE",
132
- "src",
133
- "dist",
134
- "build",
135
- "public",
136
- "static",
137
- "assets",
138
- "components",
139
- "pages",
140
- "app",
141
- "lib",
142
- "utils",
143
- "hooks",
144
- "types",
145
- "styles",
146
- "config",
147
- "test",
148
- "tests",
149
- "__tests__",
150
- "spec",
151
- "specs",
152
- "docs",
153
- "documentation"
154
- ];
155
- if (reservedNames.includes(name.toLowerCase())) {
156
- logger.error(`Project name "${name}" is reserved. Please choose a different name.`);
157
- return false;
158
- }
159
- if (name.startsWith(".")) {
160
- logger.error("Project name cannot start with a dot");
161
- return false;
162
- }
163
- if (name.startsWith("-")) {
164
- logger.error("Project name cannot start with a hyphen");
165
- return false;
166
- }
167
- if (name.endsWith("-")) {
168
- logger.error("Project name cannot end with a hyphen");
169
- return false;
170
- }
171
- if (name.includes("..")) {
172
- logger.error("Project name cannot contain consecutive dots");
173
- return false;
174
- }
175
- if (name.includes("//")) {
176
- logger.error("Project name cannot contain consecutive slashes");
177
- return false;
178
- }
179
- if (name.length > 214) {
180
- logger.error("Project name is too long (max 214 characters)");
181
- return false;
182
- }
183
- return true;
184
- }
185
- async function validateProjectPath(projectPath) {
186
- try {
187
- if (await import_fs_extra.default.pathExists(projectPath)) {
188
- const stats = await import_fs_extra.default.stat(projectPath);
189
- if (stats.isDirectory()) {
190
- const files = await import_fs_extra.default.readdir(projectPath);
191
- if (files.length > 0) {
192
- logger.error(`Directory "${import_path.default.basename(projectPath)}" already exists and is not empty`);
193
- return false;
194
- }
195
- } else {
196
- logger.error(`A file with the name "${import_path.default.basename(projectPath)}" already exists`);
197
- return false;
198
- }
199
- }
200
- const parentDir = import_path.default.dirname(projectPath);
201
- try {
202
- await import_fs_extra.default.access(parentDir, import_fs_extra.default.constants.W_OK);
203
- } catch {
204
- logger.error(`Cannot write to directory "${parentDir}"`);
205
- return false;
206
- }
207
- return true;
208
- } catch (error) {
209
- logger.error("Failed to validate project path:", error);
210
- return false;
211
- }
212
- }
213
- function sanitizeProjectName(name) {
214
- return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
215
- }
216
- function suggestProjectName(name) {
217
- const sanitized = sanitizeProjectName(name);
218
- if (validateProjectName(sanitized)) {
219
- return sanitized;
220
- }
221
- const suggested = `my-${sanitized}`;
222
- if (validateProjectName(suggested)) {
223
- return suggested;
224
- }
225
- return "my-saas-app";
226
- }
227
-
228
- // src/cli/validators/dependencies.ts
229
- var import_semver = __toESM(require("semver"));
230
- var import_execa = __toESM(require("execa"));
231
- async function validateNodeVersion() {
232
- const nodeVersion = process.version;
233
- const minVersion = "16.0.0";
234
- if (!import_semver.default.gte(nodeVersion, minVersion)) {
235
- logger.error(
236
- `Node.js version ${minVersion} or higher is required. Current version: ${nodeVersion}`
237
- );
238
- return false;
239
- }
240
- return true;
241
- }
242
-
243
- // src/cli/prompts/project-setup.ts
244
- var import_inquirer = __toESM(require("inquirer"));
245
- var TIER_CHOICES = [
246
- {
247
- name: "\u{1F680} Micro - Single feature MVP (auth only, minimal deps)",
248
- value: "micro"
249
- },
250
- {
251
- name: "\u{1F4BC} Starter - Basic SaaS (auth + payments) [Recommended]",
252
- value: "starter"
253
- },
254
- {
255
- name: "\u26A1 Pro - Full SaaS (auth + payments + AI)",
256
- value: "pro"
257
- },
258
- {
259
- name: "\u{1F3E2} Enterprise - Full platform (all features + observability)",
260
- value: "enterprise"
261
- }
262
- ];
263
- async function getProjectPrompts(initialConfig) {
264
- logger.title("\u{1F6E0}\uFE0F Project Configuration");
265
- const questions = [
266
- {
267
- type: "input",
268
- name: "name",
269
- message: "Project name:",
270
- default: initialConfig.name,
271
- validate: (input) => {
272
- if (!validateProjectName(input)) {
273
- const suggestion = suggestProjectName(input);
274
- return `Invalid project name. Try: ${suggestion}`;
275
- }
276
- return true;
277
- },
278
- filter: (input) => input.trim()
279
- },
280
- {
281
- type: "list",
282
- name: "platform",
283
- message: "Select platform:",
284
- choices: [
285
- { name: "\u{1F310} Web (Next.js)", value: "web" },
286
- { name: "\u{1F4F1} Mobile (Expo)", value: "mobile" },
287
- { name: "\u{1F680} Both (Full-stack)", value: "both" }
288
- ],
289
- default: initialConfig.platform
290
- },
291
- {
292
- type: "list",
293
- name: "tier",
294
- message: "Select project tier:",
295
- choices: TIER_CHOICES,
296
- default: initialConfig.tier || "starter"
297
- },
298
- {
299
- type: "list",
300
- name: "template",
301
- message: "Select template:",
302
- choices: (answers2) => {
303
- if (answers2.platform === "web") {
304
- return [
305
- // ═══════════════════════════════════════════════════════════════
306
- // STARTER TEMPLATES (Recommended for new projects)
307
- // ═══════════════════════════════════════════════════════════════
308
- new import_inquirer.default.Separator("\u2500\u2500\u2500 Starter Templates \u2500\u2500\u2500"),
309
- {
310
- name: "\u{1F4BC} Micro-SaaS - Single feature MVP (subscriptions, analytics)",
311
- value: "micro-saas"
312
- },
313
- {
314
- name: "\u{1F4B3} UI + Auth + Payments - Complete SaaS foundation [Recommended]",
315
- value: "ui-auth-payments"
316
- },
317
- // ═══════════════════════════════════════════════════════════════
318
- // VERTICAL TEMPLATES (Industry-specific)
319
- // ═══════════════════════════════════════════════════════════════
320
- new import_inquirer.default.Separator("\u2500\u2500\u2500 Vertical Templates \u2500\u2500\u2500"),
321
- {
322
- name: "\u{1F6D2} Marketplace - Multi-vendor platform (products, orders, vendors)",
323
- value: "marketplace"
324
- },
325
- {
326
- name: "\u{1F916} AI Platform - LLM-powered SaaS (chat, model selection)",
327
- value: "ai-platform"
328
- },
329
- {
330
- name: "\u{1F4E1} IoT Dashboard - Device monitoring & telemetry",
331
- value: "iot-dashboard"
332
- },
333
- // ═══════════════════════════════════════════════════════════════
334
- // CORE TEMPLATES (Building blocks)
335
- // ═══════════════════════════════════════════════════════════════
336
- new import_inquirer.default.Separator("\u2500\u2500\u2500 Core Templates \u2500\u2500\u2500"),
337
- {
338
- name: "\u{1F4C4} Base - Clean Next.js 15 starter",
339
- value: "base"
340
- },
341
- {
342
- name: "\u{1F3A8} UI Only - Components library showcase (no auth)",
343
- value: "ui-only"
344
- },
345
- {
346
- name: "\u{1F510} UI + Auth - Modern authentication flow",
347
- value: "ui-auth"
348
- },
349
- // ═══════════════════════════════════════════════════════════════
350
- // AI-POWERED TEMPLATES
351
- // ═══════════════════════════════════════════════════════════════
352
- new import_inquirer.default.Separator("\u2500\u2500\u2500 AI-Powered Templates \u2500\u2500\u2500"),
353
- {
354
- name: "\u{1F916} AI + Auth - Text & chat AI integration",
355
- value: "ui-auth-ai"
356
- },
357
- {
358
- name: "\u{1F916} AI + Auth + Payments - Full AI SaaS platform",
359
- value: "ui-auth-payments-ai"
360
- },
361
- // ═══════════════════════════════════════════════════════════════
362
- // MEDIA TEMPLATES
363
- // ═══════════════════════════════════════════════════════════════
364
- new import_inquirer.default.Separator("\u2500\u2500\u2500 Media Templates \u2500\u2500\u2500"),
365
- {
366
- name: "\u{1F3B5} Audio SaaS - Music streaming & audio processing",
367
- value: "ui-auth-payments-audio"
368
- },
369
- {
370
- name: "\u{1F3AC} Video SaaS - Video streaming & media platform",
371
- value: "ui-auth-payments-video"
372
- },
373
- // ═══════════════════════════════════════════════════════════════
374
- // DEVELOPMENT TEMPLATES
375
- // ═══════════════════════════════════════════════════════════════
376
- new import_inquirer.default.Separator("\u2500\u2500\u2500 Development Templates \u2500\u2500\u2500"),
377
- {
378
- name: "\u{1F9EA} UI Package Test - Component testing environment",
379
- value: "ui-package-test"
380
- }
381
- ];
382
- } else if (answers2.platform === "mobile") {
383
- return [
384
- {
385
- name: "\u{1F4F1} Base - React Native + Expo starter",
386
- value: "base"
387
- },
388
- {
389
- name: "\u{1F4B3} UI + Auth + Payments - Full mobile SaaS (v0.22.0)",
390
- value: "ui-auth-payments"
391
- },
392
- {
393
- name: "\u{1F916} AI + Auth + Payments - Mobile AI SaaS with generation tools",
394
- value: "ui-auth-payments-ai"
395
- }
396
- ];
397
- } else {
398
- return [
399
- {
400
- name: "\u{1F680} Full Stack - Web + Mobile SaaS platform",
401
- value: "ui-auth-payments"
402
- },
403
- {
404
- name: "\u{1F916} AI Full Stack - Web + Mobile AI platform",
405
- value: "ui-auth-payments-ai"
406
- }
407
- ];
408
- }
409
- },
410
- default: initialConfig.template
411
- },
412
- {
413
- type: "list",
414
- name: "auth",
415
- message: "Select authentication provider:",
416
- choices: [
417
- { name: "\u{1F511} Keycloak - Self-hosted OIDC (recommended)", value: "keycloak" },
418
- { name: "\u26A1 Supabase - Open source alternative", value: "supabase" },
419
- { name: "\u{1F525} Firebase - Google's platform", value: "firebase" }
420
- ],
421
- default: initialConfig.auth
422
- },
423
- {
424
- type: "list",
425
- name: "database",
426
- message: "Select database provider:",
427
- choices: (answers2) => {
428
- const choices = [
429
- {
430
- name: "\u{1F418} PostgreSQL + Drizzle - Self-hosted, full control (recommended)",
431
- value: "postgresql"
432
- },
433
- { name: "\u26A1 Supabase - PostgreSQL with real-time & dashboard", value: "supabase" },
434
- { name: "\u{1F525} Firebase - NoSQL with real-time", value: "firebase" }
435
- ];
436
- if (answers2.auth === "firebase") {
437
- return choices.reverse();
438
- }
439
- return choices;
440
- },
441
- default: (answers2) => {
442
- if (answers2.auth === "keycloak") return "postgresql";
443
- if (answers2.auth === "firebase") return "firebase";
444
- return "supabase";
445
- }
446
- },
447
- {
448
- type: "list",
449
- name: "theme",
450
- message: "Select theme:",
451
- choices: [
452
- { name: "\u{1F3A8} Default - Clean and modern", value: "default" },
453
- { name: "\u{1F3E2} Corporate - Professional business theme", value: "corporate" },
454
- { name: "\u{1F680} Startup - Bold and innovative", value: "startup" }
455
- ],
456
- default: initialConfig.theme
457
- },
458
- {
459
- type: "list",
460
- name: "themeColor",
461
- message: "Select theme color:",
462
- choices: [
463
- { name: "\u{1F535} Blue - Classic and trustworthy", value: "blue" },
464
- { name: "\u{1F7E2} Green - Fresh and natural", value: "green" },
465
- { name: "\u{1F7E3} Purple - Creative and modern", value: "purple" },
466
- { name: "\u{1F7E0} Orange - Energetic and friendly", value: "orange" },
467
- { name: "\u{1F534} Red - Bold and attention-grabbing", value: "red" },
468
- { name: "\u26AB Slate - Professional and minimal", value: "slate" }
469
- ],
470
- default: initialConfig.themeColor
471
- },
472
- {
473
- type: "list",
474
- name: "defaultTheme",
475
- message: "Select default theme mode:",
476
- choices: [
477
- { name: "\u{1F319} System - Follow system preference", value: "system" },
478
- { name: "\u2600\uFE0F Light - Always light mode", value: "light" },
479
- { name: "\u{1F319} Dark - Always dark mode", value: "dark" }
480
- ],
481
- default: initialConfig.defaultTheme
482
- },
483
- {
484
- type: "confirm",
485
- name: "install",
486
- message: "Install dependencies?",
487
- default: initialConfig.install
488
- },
489
- {
490
- type: "confirm",
491
- name: "git",
492
- message: "Initialize git repository?",
493
- default: initialConfig.git
494
- }
495
- ];
496
- const answers = await import_inquirer.default.prompt(questions);
497
- return {
498
- ...initialConfig,
499
- ...answers
500
- };
501
- }
502
-
503
- // src/generators/template-generator.ts
504
- var import_path2 = __toESM(require("path"));
505
- var import_fs_extra2 = __toESM(require("fs-extra"));
506
- var import_mustache = __toESM(require("mustache"));
507
- var import_glob = require("glob");
508
- var TIER_FEATURES = {
509
- micro: {
510
- platformCore: true,
511
- appSdk: true,
512
- auth: true,
513
- payments: false,
514
- ai: false,
515
- observability: false,
516
- workers: false,
517
- beta: true
518
- },
519
- starter: {
520
- platformCore: true,
521
- appSdk: true,
522
- auth: true,
523
- payments: true,
524
- ai: false,
525
- observability: false,
526
- workers: false,
527
- beta: true
528
- },
529
- pro: {
530
- platformCore: true,
531
- appSdk: true,
532
- auth: true,
533
- payments: true,
534
- ai: true,
535
- observability: true,
536
- workers: false,
537
- beta: true
538
- },
539
- enterprise: {
540
- platformCore: true,
541
- appSdk: true,
542
- auth: true,
543
- payments: true,
544
- ai: true,
545
- observability: true,
546
- workers: true,
547
- beta: true
548
- }
549
- };
550
- var TemplateGenerator = class {
551
- constructor(config) {
552
- this.config = config;
553
- this.templatesDir = import_path2.default.join(__dirname, "..", "templates");
554
- this.context = this.createTemplateContext();
555
- }
556
- createTemplateContext() {
557
- const projectName = this.config.name;
558
- const packageName = projectName.toLowerCase().replace(/[^a-z0-9-]/g, "-");
559
- const className = this.toPascalCase(projectName);
560
- const camelCaseName = this.toCamelCase(projectName);
561
- const kebabCaseName = packageName;
562
- const titleCaseName = this.toTitleCase(projectName);
563
- const slugNameCamelCase = this.toCamelCase(packageName);
564
- const tier = this.config.tier || "starter";
565
- const features = TIER_FEATURES[tier];
566
- const dependencies = this.getDependencies();
567
- const generatedDate = (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
568
- year: "numeric",
569
- month: "short",
570
- day: "numeric"
571
- });
572
- const tierDescriptions = {
573
- micro: "minimal",
574
- starter: "standard",
575
- pro: "AI-powered",
576
- enterprise: "enterprise-grade"
577
- };
578
- return {
579
- projectName,
580
- platform: this.config.platform,
581
- template: this.config.template,
582
- tier,
583
- auth: this.config.auth,
584
- database: this.config.database,
585
- theme: this.config.theme,
586
- themeColor: this.config.themeColor,
587
- defaultTheme: this.config.defaultTheme,
588
- ai: {
589
- enabled: this.config.ai.enabled || features.ai,
590
- capabilities: this.config.ai.capabilities,
591
- provider: this.config.ai.provider,
592
- hasText: this.config.ai.capabilities.includes("text"),
593
- hasAudio: this.config.ai.capabilities.includes("audio"),
594
- hasVideo: this.config.ai.capabilities.includes("video")
595
- },
596
- features,
597
- packageName,
598
- className,
599
- camelCaseName,
600
- kebabCaseName,
601
- titleCaseName,
602
- slugNameCamelCase,
603
- description: `A ${tierDescriptions[tier]} SaaS application built with DLL Platform`,
604
- author: "Digi Logic Labs",
605
- year: (/* @__PURE__ */ new Date()).getFullYear(),
606
- generatedDate,
607
- generatorVersion: "2.0.0",
608
- // DLL package versions
609
- platformCoreVersion: dependencies["@digilogiclabs/platform-core"]?.replace("^", "") || "1.1.0",
610
- appSdkVersion: dependencies["@digilogiclabs/app-sdk"]?.replace("^", "") || "1.0.0",
611
- uiVersion: dependencies["@digilogiclabs/saas-factory-ui"]?.replace("^", "") || "1.0.1",
612
- authVersion: dependencies["@digilogiclabs/saas-factory-auth"]?.replace("^", "") || "1.0.0",
613
- paymentsVersion: dependencies["@digilogiclabs/saas-factory-payments"]?.replace("^", "") || "1.0.0",
614
- aiVersion: dependencies["@digilogiclabs/saas-factory-ai"]?.replace("^", "") || "1.0.0",
615
- dependencies,
616
- devDependencies: this.getDevDependencies(),
617
- scripts: this.getScripts()
618
- };
619
- }
620
- getDependencies() {
621
- const baseDeps = {};
622
- const tier = this.config.tier || "starter";
623
- const features = TIER_FEATURES[tier];
624
- if (features.platformCore) {
625
- baseDeps["@digilogiclabs/platform-core"] = "^1.9.0";
626
- }
627
- if (features.appSdk && (this.config.platform === "web" || this.config.platform === "both")) {
628
- baseDeps["@digilogiclabs/app-sdk"] = "^1.0.0";
629
- }
630
- if ([
631
- "ui-only",
632
- "ui-auth",
633
- "ui-auth-payments",
634
- "ui-auth-payments-audio",
635
- "ui-auth-payments-video",
636
- "ui-auth-payments-ai",
637
- "ui-auth-payments-ai-rag",
638
- // New vertical templates
639
- "micro-saas",
640
- "marketplace",
641
- "ai-platform",
642
- "iot-dashboard"
643
- ].includes(this.config.template)) {
644
- baseDeps["@digilogiclabs/saas-factory-ui"] = "^1.1.0";
645
- }
646
- if (features.auth && this.config.template !== "base" && this.config.template !== "ui-only") {
647
- baseDeps["@digilogiclabs/saas-factory-auth"] = "^3.0.0";
648
- }
649
- if (features.payments && (this.config.template.includes("payments") || ["starter", "pro", "enterprise"].includes(tier))) {
650
- baseDeps["@digilogiclabs/saas-factory-payments"] = "^3.0.0";
651
- }
652
- if (features.ai && (this.config.ai.enabled || this.config.template.includes("-ai") || this.config.template === "ai-platform")) {
653
- baseDeps["@digilogiclabs/saas-factory-ai"] = "^6.0.0";
654
- baseDeps["@digilogiclabs/saas-factory-ai-types"] = "^1.0.0";
655
- if (this.config.template === "ai-platform") {
656
- baseDeps["ai"] = "^4.0.0";
657
- baseDeps["@ai-sdk/openai"] = "^1.0.0";
658
- baseDeps["@ai-sdk/anthropic"] = "^1.0.0";
659
- }
660
- }
661
- if (this.config.template === "iot-dashboard") {
662
- baseDeps["recharts"] = "^2.14.0";
663
- baseDeps["date-fns"] = "^4.1.0";
664
- }
665
- if (this.config.template === "marketplace") {
666
- baseDeps["stripe"] = "^17.4.0";
667
- }
668
- if (this.config.platform === "web" || this.config.platform === "both") {
669
- Object.assign(baseDeps, {
670
- next: "^15.5.0",
671
- react: "^19.0.0",
672
- "react-dom": "^19.0.0",
673
- tailwindcss: "^4.0.0",
674
- typescript: "^5.8.0",
675
- clsx: "^2.1.0",
676
- "class-variance-authority": "^0.7.0",
677
- "tailwind-merge": "^2.6.0",
678
- "next-themes": "^0.4.0",
679
- "lucide-react": "^0.460.0"
680
- });
681
- }
682
- if (this.config.platform === "mobile" || this.config.platform === "both") {
683
- Object.assign(baseDeps, {
684
- expo: "~52.0.0",
685
- "react-native": "0.76.0",
686
- "@expo/vector-icons": "^14.0.0",
687
- "@react-navigation/native": "^7.0.0",
688
- "@react-navigation/bottom-tabs": "^7.0.0",
689
- "react-native-screens": "~4.0.0",
690
- "react-native-safe-area-context": "4.12.0",
691
- "react-native-gesture-handler": "~2.20.0",
692
- "expo-router": "~4.0.0"
693
- });
694
- if (features.payments) {
695
- baseDeps["@stripe/stripe-react-native"] = "^0.39.0";
696
- }
697
- }
698
- if (this.config.auth === "firebase") {
699
- baseDeps["firebase"] = "^11.0.0";
700
- } else if (this.config.auth === "supabase") {
701
- baseDeps["@supabase/supabase-js"] = "^2.46.0";
702
- if (this.config.platform === "web" || this.config.platform === "both") {
703
- baseDeps["@supabase/ssr"] = "^0.5.0";
704
- }
705
- } else if (this.config.auth === "keycloak") {
706
- if (this.config.platform === "web" || this.config.platform === "both") {
707
- baseDeps["next-auth"] = "^5.0.0-beta.30";
708
- }
709
- }
710
- if (features.payments || features.ai || features.observability) {
711
- baseDeps["ioredis"] = "^5.6.0";
712
- }
713
- if (this.config.database === "postgresql") {
714
- baseDeps["drizzle-orm"] = "^0.43.0";
715
- baseDeps["postgres"] = "^3.4.0";
716
- baseDeps["dotenv"] = "^16.4.0";
717
- }
718
- if (features.payments) {
719
- baseDeps["stripe"] = "^17.4.0";
720
- }
721
- if (features.payments) {
722
- baseDeps["resend"] = "^4.0.0";
723
- }
724
- baseDeps["server-only"] = "^0.0.1";
725
- return baseDeps;
726
- }
727
- getDevDependencies() {
728
- const baseDeps = {
729
- "@types/node": "^22.0.0",
730
- eslint: "^9.0.0",
731
- prettier: "^3.4.0",
732
- typescript: "^5.7.0"
733
- };
734
- if (this.config.platform === "web" || this.config.platform === "both") {
735
- Object.assign(baseDeps, {
736
- "@types/react": "^19.0.0",
737
- "@types/react-dom": "^19.0.0"
738
- });
739
- }
740
- if (this.config.database === "postgresql") {
741
- baseDeps["drizzle-kit"] = "^0.30.0";
742
- }
743
- const tier = this.config.tier || "starter";
744
- if (["pro", "enterprise"].includes(tier)) {
745
- Object.assign(baseDeps, {
746
- vitest: "^2.1.0",
747
- "@testing-library/react": "^16.0.0",
748
- "@testing-library/jest-dom": "^6.6.0"
749
- });
750
- }
751
- return baseDeps;
752
- }
753
- getScripts() {
754
- const scripts = {};
755
- if (this.config.platform === "web" || this.config.platform === "both") {
756
- Object.assign(scripts, {
757
- dev: "next dev",
758
- build: "next build",
759
- start: "next start",
760
- lint: "next lint"
761
- });
762
- }
763
- if (this.config.platform === "mobile" || this.config.platform === "both") {
764
- Object.assign(scripts, {
765
- start: "expo start",
766
- android: "expo start --android",
767
- ios: "expo start --ios",
768
- web: "expo start --web"
769
- });
770
- }
771
- return scripts;
772
- }
773
- async generate(outputPath) {
774
- try {
775
- await import_fs_extra2.default.ensureDir(outputPath);
776
- if (this.config.platform === "web") {
777
- await this.generateWebProject(outputPath);
778
- } else if (this.config.platform === "mobile") {
779
- await this.generateMobileProject(outputPath);
780
- } else if (this.config.platform === "both") {
781
- await this.generateWebProject(outputPath);
782
- await this.generateMobileProject(import_path2.default.join(outputPath, "mobile"));
783
- }
784
- await this.copySharedResources(outputPath);
785
- await this.patchPackageJson(outputPath);
786
- logger.debug("Template generation completed");
787
- } catch (error) {
788
- logger.error("Template generation failed:", error);
789
- throw error;
790
- }
791
- }
792
- async generateWebProject(outputPath) {
793
- const templatePath = import_path2.default.join(this.templatesDir, "web", this.config.template);
794
- await this.copyTemplate(templatePath, outputPath);
795
- }
796
- async generateMobileProject(outputPath) {
797
- let mobileTemplate = this.config.template;
798
- if (!await import_fs_extra2.default.pathExists(import_path2.default.join(this.templatesDir, "mobile", mobileTemplate))) {
799
- if (mobileTemplate.includes("-ai-rag")) {
800
- mobileTemplate = "ui-auth-payments-ai-rag";
801
- } else if (mobileTemplate.includes("-ai") || mobileTemplate === "ui-auth-payments-ai") {
802
- mobileTemplate = "ui-auth-payments-ai";
803
- } else if (mobileTemplate.startsWith("ui-auth-payments")) {
804
- mobileTemplate = "ui-auth-payments";
805
- } else if (mobileTemplate.startsWith("ui-auth")) {
806
- mobileTemplate = "ui-auth-payments";
807
- } else if (mobileTemplate.startsWith("ui-")) {
808
- mobileTemplate = "base";
809
- }
810
- }
811
- const templatePath = import_path2.default.join(this.templatesDir, "mobile", mobileTemplate);
812
- await this.copyTemplate(templatePath, outputPath);
813
- await this.createMobileAssets(outputPath);
814
- }
815
- async copyTemplate(templatePath, outputPath) {
816
- const templateDir = import_path2.default.join(templatePath, "template");
817
- if (!await import_fs_extra2.default.pathExists(templateDir)) {
818
- throw new Error(`Template not found: ${templateDir}`);
819
- }
820
- await this.copyAndProcessFiles(templateDir, outputPath);
821
- }
822
- async copyAndProcessFiles(sourceDir, targetDir) {
823
- if (!await import_fs_extra2.default.pathExists(sourceDir)) {
824
- logger.debug(`Template source directory not found: ${sourceDir}`);
825
- return;
826
- }
827
- const files = await (0, import_glob.glob)("**/*", {
828
- cwd: sourceDir,
829
- dot: true,
830
- nodir: true
831
- });
832
- for (const file of files) {
833
- const sourcePath = import_path2.default.join(sourceDir, file);
834
- const targetPath = import_path2.default.join(targetDir, file);
835
- await import_fs_extra2.default.ensureDir(import_path2.default.dirname(targetPath));
836
- const content = await import_fs_extra2.default.readFile(sourcePath, "utf-8");
837
- if (this.isTextFile(file)) {
838
- const hasMustacheVars = content.includes("{{") && content.includes("}}");
839
- const ext = import_path2.default.extname(file).toLowerCase();
840
- if ([".tsx", ".jsx"].includes(ext) && !hasMustacheVars) {
841
- await import_fs_extra2.default.writeFile(targetPath, content);
842
- } else if (hasMustacheVars) {
843
- if ([".tsx", ".jsx"].includes(ext)) {
844
- const templateLiterals = [];
845
- let protectedContent = content.replace(/\$\{([^}]+)\}/g, (match, _variable) => {
846
- templateLiterals.push(match);
847
- return `__TEMPLATE_LITERAL_${templateLiterals.length - 1}__`;
848
- });
849
- const jsxObjectLiterals = [];
850
- protectedContent = this.protectJSXObjectLiterals(protectedContent, jsxObjectLiterals);
851
- const processedContent = import_mustache.default.render(protectedContent, this.context);
852
- let finalContent = processedContent.replace(
853
- /__TEMPLATE_LITERAL_(\d+)__/g,
854
- (match, index) => {
855
- return templateLiterals[parseInt(index)];
856
- }
857
- );
858
- finalContent = finalContent.replace(/__JSX_OBJECT_(\d+)__/g, (match, index) => {
859
- return jsxObjectLiterals[parseInt(index)];
860
- });
861
- await import_fs_extra2.default.writeFile(targetPath, finalContent);
862
- } else {
863
- const processedContent = import_mustache.default.render(content, this.context);
864
- await import_fs_extra2.default.writeFile(targetPath, processedContent);
865
- }
866
- } else {
867
- await import_fs_extra2.default.writeFile(targetPath, content);
868
- }
869
- } else {
870
- await import_fs_extra2.default.copy(sourcePath, targetPath);
871
- }
872
- }
873
- }
874
- async copySharedResources(outputPath) {
875
- if (this.config.template === "ui-only") {
876
- logger.debug("Skipping shared resources for UI-only template");
877
- return;
878
- }
879
- const sharedPath = import_path2.default.join(this.templatesDir, "shared");
880
- const isWeb = this.config.platform === "web" || this.config.platform === "both";
881
- const tier = this.config.tier || "starter";
882
- const features = TIER_FEATURES[tier];
883
- if (isWeb) {
884
- const authPath = import_path2.default.join(sharedPath, "auth", this.config.auth, "web");
885
- await this.copyAndProcessFiles(authPath, outputPath);
886
- }
887
- if (isWeb) {
888
- const dbPath = import_path2.default.join(sharedPath, "database", this.config.database, "web");
889
- await this.copyAndProcessFiles(dbPath, outputPath);
890
- }
891
- const themePath = import_path2.default.join(sharedPath, "themes", this.config.theme);
892
- await this.copyAndProcessFiles(themePath, outputPath);
893
- if (features.beta && isWeb) {
894
- const betaPath = import_path2.default.join(sharedPath, "beta", "web");
895
- if (await import_fs_extra2.default.pathExists(betaPath)) {
896
- await this.copyAndProcessFiles(betaPath, import_path2.default.join(outputPath, "src"));
897
- logger.debug("Copied beta gate templates");
898
- }
899
- }
900
- if (isWeb) {
901
- const configPath = import_path2.default.join(sharedPath, "config", "web");
902
- if (await import_fs_extra2.default.pathExists(configPath)) {
903
- await this.copyAndProcessFiles(configPath, outputPath);
904
- logger.debug("Copied environment config template");
905
- }
906
- }
907
- if (isWeb && (features.payments || features.ai || features.observability)) {
908
- const redisPath = import_path2.default.join(sharedPath, "redis", "web");
909
- if (await import_fs_extra2.default.pathExists(redisPath)) {
910
- await this.copyAndProcessFiles(redisPath, outputPath);
911
- logger.debug("Copied Redis client template");
912
- }
913
- }
914
- if (isWeb) {
915
- const securityPath = import_path2.default.join(sharedPath, "security", "web");
916
- if (await import_fs_extra2.default.pathExists(securityPath)) {
917
- await this.copyAndProcessFiles(securityPath, outputPath);
918
- logger.debug("Copied API security wrapper template");
919
- }
920
- }
921
- if (isWeb && this.config.auth === "keycloak") {
922
- const middlewarePath = import_path2.default.join(sharedPath, "middleware", "web");
923
- if (await import_fs_extra2.default.pathExists(middlewarePath)) {
924
- await this.copyAndProcessFiles(middlewarePath, outputPath);
925
- logger.debug("Copied middleware template");
926
- }
927
- }
928
- if (isWeb) {
929
- const healthPath = import_path2.default.join(sharedPath, "health", "web");
930
- if (await import_fs_extra2.default.pathExists(healthPath)) {
931
- await this.copyAndProcessFiles(healthPath, outputPath);
932
- logger.debug("Copied health check endpoint template");
933
- }
934
- }
935
- if (isWeb && features.payments) {
936
- const paymentsPath = import_path2.default.join(sharedPath, "payments", "web");
937
- if (await import_fs_extra2.default.pathExists(paymentsPath)) {
938
- await this.copyAndProcessFiles(paymentsPath, outputPath);
939
- logger.debug("Copied Stripe webhook handler template");
940
- }
941
- }
942
- if (isWeb && features.auth) {
943
- const auditPath = import_path2.default.join(sharedPath, "audit", "web");
944
- if (await import_fs_extra2.default.pathExists(auditPath)) {
945
- await this.copyAndProcessFiles(auditPath, outputPath);
946
- logger.debug("Copied audit logging template");
947
- }
948
- }
949
- if (isWeb && features.auth) {
950
- const emailPath = import_path2.default.join(sharedPath, "email", "web");
951
- if (await import_fs_extra2.default.pathExists(emailPath)) {
952
- await this.copyAndProcessFiles(emailPath, outputPath);
953
- logger.debug("Copied email branding template");
954
- }
955
- }
956
- if (isWeb) {
957
- const platformPath = import_path2.default.join(sharedPath, "platform", "web");
958
- if (await import_fs_extra2.default.pathExists(platformPath)) {
959
- await this.copyAndProcessFiles(platformPath, outputPath);
960
- logger.debug("Copied platform-core initialization template");
961
- }
962
- }
963
- if (isWeb) {
964
- const legalPath = import_path2.default.join(sharedPath, "legal", "web");
965
- if (await import_fs_extra2.default.pathExists(legalPath)) {
966
- await this.copyAndProcessFiles(legalPath, outputPath);
967
- logger.debug("Copied legal page templates (terms, privacy)");
968
- }
969
- }
970
- if (isWeb) {
971
- const errorPagesPath = import_path2.default.join(sharedPath, "error-pages", "web");
972
- if (await import_fs_extra2.default.pathExists(errorPagesPath)) {
973
- await this.copyAndProcessFiles(errorPagesPath, outputPath);
974
- logger.debug("Copied error page templates (not-found, error, global-error)");
975
- }
976
- }
977
- if (isWeb) {
978
- const seoPath = import_path2.default.join(sharedPath, "seo", "web");
979
- if (await import_fs_extra2.default.pathExists(seoPath)) {
980
- await this.copyAndProcessFiles(seoPath, outputPath);
981
- logger.debug("Copied SEO templates (sitemap, robots)");
982
- }
983
- }
984
- if (isWeb) {
985
- const utilsPath = import_path2.default.join(sharedPath, "utils", "web");
986
- if (await import_fs_extra2.default.pathExists(utilsPath)) {
987
- await this.copyAndProcessFiles(utilsPath, outputPath);
988
- logger.debug("Copied utility templates (utils, api-response)");
989
- }
990
- }
991
- if (isWeb) {
992
- const cookiePath = import_path2.default.join(sharedPath, "cookie-consent", "web");
993
- if (await import_fs_extra2.default.pathExists(cookiePath)) {
994
- await this.copyAndProcessFiles(cookiePath, outputPath);
995
- logger.debug("Copied cookie consent component template");
996
- }
997
- }
998
- if (isWeb) {
999
- const obsPath = import_path2.default.join(sharedPath, "observability", "web");
1000
- if (await import_fs_extra2.default.pathExists(obsPath)) {
1001
- await this.copyAndProcessFiles(obsPath, outputPath);
1002
- logger.debug("Copied observability template");
1003
- }
1004
- }
1005
- if (isWeb) {
1006
- const cachePath = import_path2.default.join(sharedPath, "cache", "web");
1007
- if (await import_fs_extra2.default.pathExists(cachePath)) {
1008
- await this.copyAndProcessFiles(cachePath, outputPath);
1009
- logger.debug("Copied cache utility template");
1010
- }
1011
- }
1012
- if (isWeb) {
1013
- const loadingPath = import_path2.default.join(sharedPath, "loading", "web");
1014
- if (await import_fs_extra2.default.pathExists(loadingPath)) {
1015
- await this.copyAndProcessFiles(loadingPath, outputPath);
1016
- logger.debug("Copied loading skeleton templates");
1017
- }
1018
- }
1019
- if (isWeb && this.config.auth === "keycloak") {
1020
- const adminPath = import_path2.default.join(sharedPath, "admin", "web");
1021
- if (await import_fs_extra2.default.pathExists(adminPath)) {
1022
- await this.copyAndProcessFiles(adminPath, outputPath);
1023
- logger.debug("Copied admin layout + nav templates");
1024
- }
1025
- }
1026
- if (isWeb) {
1027
- const envExample = this.generateEnvExample(features);
1028
- await import_fs_extra2.default.writeFile(import_path2.default.join(outputPath, ".env.example"), envExample);
1029
- logger.debug("Generated .env.example");
1030
- }
1031
- }
1032
- /**
1033
- * Patch the generated package.json to merge aligned dependency versions.
1034
- * Base templates may have stale versions — this ensures the output
1035
- * always matches production-aligned versions from getDependencies().
1036
- */
1037
- async patchPackageJson(outputPath) {
1038
- const pkgPath = import_path2.default.join(outputPath, "package.json");
1039
- if (!await import_fs_extra2.default.pathExists(pkgPath)) return;
1040
- const pkg = await import_fs_extra2.default.readJson(pkgPath);
1041
- const deps = this.context.dependencies;
1042
- const devDeps = this.context.devDependencies;
1043
- pkg.dependencies = { ...pkg.dependencies || {}, ...deps };
1044
- pkg.devDependencies = { ...pkg.devDependencies || {}, ...devDeps };
1045
- for (const key of Object.keys(pkg.devDependencies)) {
1046
- if (key === "typescript") continue;
1047
- if (pkg.dependencies[key]) {
1048
- delete pkg.devDependencies[key];
1049
- }
1050
- }
1051
- await import_fs_extra2.default.writeJson(pkgPath, pkg, { spaces: 2 });
1052
- logger.debug("Patched package.json with aligned dependency versions");
1053
- }
1054
- /**
1055
- * Generate .env.example dynamically based on auth, database, and tier selections.
1056
- */
1057
- generateEnvExample(features) {
1058
- const lines = [
1059
- "# \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
1060
- `# ${this.config.name} \u2014 Environment Variables`,
1061
- "# Copy this file to .env.local and fill in the values.",
1062
- "# \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
1063
- "",
1064
- "# \u2500\u2500 App \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1065
- "NODE_ENV=development",
1066
- "NEXT_PUBLIC_APP_URL=http://localhost:3000",
1067
- ""
1068
- ];
1069
- if (this.config.auth === "keycloak") {
1070
- lines.push(
1071
- "# \u2500\u2500 Auth (Keycloak + Auth.js) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1072
- "AUTH_SECRET= # openssl rand -base64 32",
1073
- "KEYCLOAK_ISSUER=https://auth.example.com/realms/my-realm",
1074
- "KEYCLOAK_CLIENT_ID=my-app",
1075
- "KEYCLOAK_CLIENT_SECRET=",
1076
- ""
1077
- );
1078
- } else if (this.config.auth === "supabase") {
1079
- lines.push(
1080
- "# \u2500\u2500 Auth (Supabase) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1081
- "NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co",
1082
- "NEXT_PUBLIC_SUPABASE_ANON_KEY=",
1083
- "SUPABASE_SERVICE_ROLE_KEY=",
1084
- ""
1085
- );
1086
- } else if (this.config.auth === "firebase") {
1087
- lines.push(
1088
- "# \u2500\u2500 Auth (Firebase) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1089
- "NEXT_PUBLIC_FIREBASE_API_KEY=",
1090
- "NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=",
1091
- "NEXT_PUBLIC_FIREBASE_PROJECT_ID=",
1092
- ""
1093
- );
1094
- }
1095
- if (this.config.database === "postgresql") {
1096
- lines.push(
1097
- "# \u2500\u2500 Database (PostgreSQL) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1098
- "DATABASE_URL=postgres://user:password@localhost:5432/myapp",
1099
- ""
1100
- );
1101
- } else if (this.config.database === "supabase") {
1102
- lines.push(
1103
- "# \u2500\u2500 Database (Supabase) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1104
- "NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co",
1105
- "NEXT_PUBLIC_SUPABASE_ANON_KEY=",
1106
- "SUPABASE_SERVICE_ROLE_KEY=",
1107
- ""
1108
- );
1109
- }
1110
- if (features.payments || features.ai || features.observability) {
1111
- lines.push(
1112
- "# \u2500\u2500 Cache (Redis) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1113
- "REDIS_URL=redis://localhost:6379",
1114
- "REDIS_KEY_PREFIX=myapp: # Isolate keys per app",
1115
- ""
1116
- );
1117
- }
1118
- if (features.payments) {
1119
- lines.push(
1120
- "# \u2500\u2500 Payments (Stripe) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1121
- "STRIPE_PUBLISHABLE_KEY=pk_test_...",
1122
- "STRIPE_SECRET_KEY=sk_test_...",
1123
- "STRIPE_WEBHOOK_SECRET=whsec_...",
1124
- ""
1125
- );
1126
- }
1127
- if (features.payments) {
1128
- lines.push(
1129
- "# \u2500\u2500 Email (Resend) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1130
- "RESEND_API_KEY=re_...",
1131
- "EMAIL_FROM=noreply@example.com",
1132
- ""
1133
- );
1134
- }
1135
- if (features.ai) {
1136
- lines.push(
1137
- "# \u2500\u2500 AI \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1138
- "OPENAI_API_KEY=sk-...",
1139
- "# ANTHROPIC_API_KEY=sk-ant-...",
1140
- ""
1141
- );
1142
- }
1143
- lines.push(
1144
- "# \u2500\u2500 Security \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1145
- "ADMIN_SECRET= # openssl rand -base64 32",
1146
- "CRON_SECRET= # openssl rand -base64 32",
1147
- ""
1148
- );
1149
- if (features.beta) {
1150
- lines.push(
1151
- "# \u2500\u2500 Beta Access \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1152
- "BETA_ENABLED=true",
1153
- "BETA_CODES=CODE1,CODE2 # Comma-separated invite codes",
1154
- ""
1155
- );
1156
- }
1157
- return lines.join("\n");
1158
- }
1159
- protectJSXObjectLiterals(content, jsxObjectLiterals) {
1160
- let result = "";
1161
- let i = 0;
1162
- while (i < content.length) {
1163
- if (content.substr(i, 2) === "{{") {
1164
- let braceCount = 0;
1165
- const start = i;
1166
- let j = i;
1167
- while (j < content.length) {
1168
- if (content[j] === "{") {
1169
- braceCount++;
1170
- } else if (content[j] === "}") {
1171
- braceCount--;
1172
- if (braceCount === 0) {
1173
- break;
1174
- }
1175
- }
1176
- j++;
1177
- }
1178
- if (braceCount === 0 && j < content.length) {
1179
- const fullMatch = content.substring(start, j + 1);
1180
- const innerContent = content.substring(start + 2, j - 1);
1181
- if (this.isJSXObjectLiteral(innerContent)) {
1182
- jsxObjectLiterals.push(fullMatch);
1183
- result += `__JSX_OBJECT_${jsxObjectLiterals.length - 1}__`;
1184
- i = j + 1;
1185
- } else {
1186
- result += content[i];
1187
- i++;
1188
- }
1189
- } else {
1190
- result += content[i];
1191
- i++;
1192
- }
1193
- } else {
1194
- result += content[i];
1195
- i++;
1196
- }
1197
- }
1198
- return result;
1199
- }
1200
- isJSXObjectLiteral(content) {
1201
- return content.includes(":") || // Property assignments
1202
- content.includes(",") || // Multiple properties
1203
- content.includes("[") || // Arrays
1204
- content.includes("(") || // Function calls
1205
- content.match(/\s+/) !== null || // Whitespace
1206
- content.includes("\\n") || // Newlines
1207
- content.length > 20;
1208
- }
1209
- isTextFile(filename) {
1210
- const textExtensions = [
1211
- ".js",
1212
- ".jsx",
1213
- ".ts",
1214
- ".tsx",
1215
- ".json",
1216
- ".md",
1217
- ".txt",
1218
- ".yml",
1219
- ".yaml",
1220
- ".xml",
1221
- ".html",
1222
- ".css",
1223
- ".scss",
1224
- ".sass",
1225
- ".less",
1226
- ".mjs",
1227
- ".cjs",
1228
- ".env",
1229
- ".gitignore",
1230
- ".eslintrc",
1231
- ".prettierrc",
1232
- ".editorconfig",
1233
- ".nvmrc",
1234
- // Infrastructure files (Terraform, Kubernetes)
1235
- ".tf",
1236
- ".tfvars",
1237
- ".hcl"
1238
- ];
1239
- const ext = import_path2.default.extname(filename).toLowerCase();
1240
- return textExtensions.includes(ext) || !ext;
1241
- }
1242
- toPascalCase(str) {
1243
- return str.replace(/[^a-zA-Z0-9]/g, " ").split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
1244
- }
1245
- toCamelCase(str) {
1246
- const pascal = this.toPascalCase(str);
1247
- return pascal.charAt(0).toLowerCase() + pascal.slice(1);
1248
- }
1249
- toTitleCase(str) {
1250
- return str.replace(/[^a-zA-Z0-9]/g, " ").split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1251
- }
1252
- async createMobileAssets(outputPath) {
1253
- const assetsDir = import_path2.default.join(outputPath, "assets");
1254
- await import_fs_extra2.default.ensureDir(assetsDir);
1255
- const placeholderImage = Buffer.from(
1256
- "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==",
1257
- "base64"
1258
- );
1259
- const assetFiles = ["icon.png", "splash.png", "adaptive-icon.png", "favicon.png"];
1260
- for (const assetFile of assetFiles) {
1261
- await import_fs_extra2.default.writeFile(import_path2.default.join(assetsDir, assetFile), placeholderImage);
1262
- }
1263
- logger.debug(`Created assets directory with ${assetFiles.length} placeholder files`);
1264
- }
1265
- };
1266
-
1267
- // src/generators/package-installer.ts
1268
- var import_path4 = __toESM(require("path"));
1269
- var import_fs_extra4 = __toESM(require("fs-extra"));
1270
- var import_execa3 = __toESM(require("execa"));
1271
-
1272
- // src/cli/utils/package-manager.ts
1273
- var import_execa2 = __toESM(require("execa"));
1274
- var import_fs_extra3 = __toESM(require("fs-extra"));
1275
- var import_path3 = __toESM(require("path"));
1276
- async function detectPackageManager(projectPath) {
1277
- const cwd = projectPath || process.cwd();
1278
- const lockFiles = [
1279
- { file: "pnpm-lock.yaml", manager: "pnpm" },
1280
- { file: "yarn.lock", manager: "yarn" },
1281
- { file: "package-lock.json", manager: "npm" }
1282
- ];
1283
- for (const { file, manager } of lockFiles) {
1284
- if (await import_fs_extra3.default.pathExists(import_path3.default.join(cwd, file))) {
1285
- return manager;
1286
- }
1287
- }
1288
- const managers = ["pnpm", "yarn", "npm"];
1289
- for (const manager of managers) {
1290
- try {
1291
- await (0, import_execa2.default)(manager, ["--version"]);
1292
- return manager;
1293
- } catch {
1294
- }
1295
- }
1296
- return "npm";
1297
- }
1298
- async function getInstallCommand(packageManager) {
1299
- switch (packageManager) {
1300
- case "yarn":
1301
- return ["yarn", "install"];
1302
- case "pnpm":
1303
- return ["pnpm", "install"];
1304
- default:
1305
- return ["npm", "install"];
1306
- }
1307
- }
1308
- async function getAddCommand(packageManager, packages, dev = false) {
1309
- const devFlag = dev ? packageManager === "npm" ? "--save-dev" : "-D" : "";
1310
- switch (packageManager) {
1311
- case "yarn":
1312
- return ["yarn", "add", ...dev ? ["-D"] : [], ...packages];
1313
- case "pnpm":
1314
- return ["pnpm", "add", ...dev ? ["-D"] : [], ...packages];
1315
- default:
1316
- return ["npm", "install", ...devFlag ? [devFlag] : [], ...packages];
1317
- }
1318
- }
1319
- async function isPackageManagerAvailable(packageManager) {
1320
- try {
1321
- await (0, import_execa2.default)(packageManager, ["--version"]);
1322
- return true;
1323
- } catch {
1324
- return false;
1325
- }
1326
- }
1327
-
1328
- // src/generators/package-installer.ts
1329
- var PackageInstaller = class {
1330
- constructor(projectPath) {
1331
- this.projectPath = projectPath;
1332
- }
1333
- async install() {
1334
- try {
1335
- const packageJsonPath = import_path4.default.join(this.projectPath, "package.json");
1336
- if (!await import_fs_extra4.default.pathExists(packageJsonPath)) {
1337
- throw new Error("package.json not found in project directory");
1338
- }
1339
- const packageManager = await detectPackageManager(this.projectPath);
1340
- if (!await isPackageManagerAvailable(packageManager)) {
1341
- throw new Error(`Package manager "${packageManager}" is not available`);
1342
- }
1343
- logger.debug(`Installing dependencies with ${packageManager}...`);
1344
- const command = await getInstallCommand(packageManager);
1345
- if (packageManager === "npm") {
1346
- command.push("--legacy-peer-deps");
1347
- }
1348
- await (0, import_execa3.default)(command[0], command.slice(1), {
1349
- cwd: this.projectPath,
1350
- stdio: "pipe"
1351
- });
1352
- logger.debug("Dependencies installed successfully");
1353
- } catch (error) {
1354
- logger.error("Package installation failed:", error);
1355
- throw error;
1356
- }
1357
- }
1358
- async installDependencies(dependencies, dev = false) {
1359
- try {
1360
- if (dependencies.length === 0) {
1361
- return;
1362
- }
1363
- const packageManager = await detectPackageManager(this.projectPath);
1364
- if (!await isPackageManagerAvailable(packageManager)) {
1365
- throw new Error(`Package manager "${packageManager}" is not available`);
1366
- }
1367
- logger.debug(`Installing ${dev ? "dev " : ""}dependencies: ${dependencies.join(", ")}`);
1368
- const command = packageManager === "yarn" ? ["yarn", "add", ...dev ? ["-D"] : [], ...dependencies] : packageManager === "pnpm" ? ["pnpm", "add", ...dev ? ["-D"] : [], ...dependencies] : [
1369
- "npm",
1370
- "install",
1371
- ...dev ? ["--save-dev"] : [],
1372
- "--legacy-peer-deps",
1373
- ...dependencies
1374
- ];
1375
- await (0, import_execa3.default)(command[0], command.slice(1), {
1376
- cwd: this.projectPath,
1377
- stdio: "pipe"
1378
- });
1379
- logger.debug(`${dev ? "Dev d" : "D"}ependencies installed successfully`);
1380
- } catch (error) {
1381
- logger.error(`Failed to install ${dev ? "dev " : ""}dependencies:`, error);
1382
- throw error;
1383
- }
1384
- }
1385
- async updatePackageJson(updates) {
1386
- try {
1387
- const packageJsonPath = import_path4.default.join(this.projectPath, "package.json");
1388
- if (!await import_fs_extra4.default.pathExists(packageJsonPath)) {
1389
- throw new Error("package.json not found");
1390
- }
1391
- const packageJson = await import_fs_extra4.default.readJson(packageJsonPath);
1392
- const updatedPackageJson = { ...packageJson, ...updates };
1393
- await import_fs_extra4.default.writeJson(packageJsonPath, updatedPackageJson, { spaces: 2 });
1394
- logger.debug("package.json updated successfully");
1395
- } catch (error) {
1396
- logger.error("Failed to update package.json:", error);
1397
- throw error;
1398
- }
1399
- }
1400
- async addScript(name, command) {
1401
- try {
1402
- const packageJsonPath = import_path4.default.join(this.projectPath, "package.json");
1403
- const packageJson = await import_fs_extra4.default.readJson(packageJsonPath);
1404
- if (!packageJson.scripts) {
1405
- packageJson.scripts = {};
1406
- }
1407
- packageJson.scripts[name] = command;
1408
- await import_fs_extra4.default.writeJson(packageJsonPath, packageJson, { spaces: 2 });
1409
- logger.debug(`Script "${name}" added to package.json`);
1410
- } catch (error) {
1411
- logger.error(`Failed to add script "${name}":`, error);
1412
- throw error;
1413
- }
1414
- }
1415
- async removeScript(name) {
1416
- try {
1417
- const packageJsonPath = import_path4.default.join(this.projectPath, "package.json");
1418
- const packageJson = await import_fs_extra4.default.readJson(packageJsonPath);
1419
- if (packageJson.scripts && packageJson.scripts[name]) {
1420
- delete packageJson.scripts[name];
1421
- await import_fs_extra4.default.writeJson(packageJsonPath, packageJson, { spaces: 2 });
1422
- logger.debug(`Script "${name}" removed from package.json`);
1423
- }
1424
- } catch (error) {
1425
- logger.error(`Failed to remove script "${name}":`, error);
1426
- throw error;
1427
- }
1428
- }
1429
- async getDependencies() {
1430
- try {
1431
- const packageJsonPath = import_path4.default.join(this.projectPath, "package.json");
1432
- const packageJson = await import_fs_extra4.default.readJson(packageJsonPath);
1433
- return {
1434
- dependencies: packageJson.dependencies || {},
1435
- devDependencies: packageJson.devDependencies || {}
1436
- };
1437
- } catch (error) {
1438
- logger.error("Failed to read dependencies:", error);
1439
- return { dependencies: {}, devDependencies: {} };
1440
- }
1441
- }
1442
- async hasLockFile() {
1443
- const lockFiles = ["package-lock.json", "yarn.lock", "pnpm-lock.yaml"];
1444
- for (const lockFile of lockFiles) {
1445
- if (await import_fs_extra4.default.pathExists(import_path4.default.join(this.projectPath, lockFile))) {
1446
- return true;
1447
- }
1448
- }
1449
- return false;
1450
- }
1451
- async cleanInstall() {
1452
- try {
1453
- const nodeModulesPath = import_path4.default.join(this.projectPath, "node_modules");
1454
- const lockFiles = ["package-lock.json", "yarn.lock", "pnpm-lock.yaml"];
1455
- if (await import_fs_extra4.default.pathExists(nodeModulesPath)) {
1456
- await import_fs_extra4.default.remove(nodeModulesPath);
1457
- }
1458
- for (const lockFile of lockFiles) {
1459
- const lockPath = import_path4.default.join(this.projectPath, lockFile);
1460
- if (await import_fs_extra4.default.pathExists(lockPath)) {
1461
- await import_fs_extra4.default.remove(lockPath);
1462
- }
1463
- }
1464
- await this.install();
1465
- logger.debug("Clean install completed");
1466
- } catch (error) {
1467
- logger.error("Clean install failed:", error);
1468
- throw error;
1469
- }
1470
- }
1471
- };
1472
-
1473
- // src/cli/utils/git.ts
1474
- var import_execa4 = __toESM(require("execa"));
1475
- var import_fs_extra5 = __toESM(require("fs-extra"));
1476
- async function initializeGit(projectPath) {
1477
- try {
1478
- await (0, import_execa4.default)("git", ["--version"]);
1479
- await (0, import_execa4.default)("git", ["init"], { cwd: projectPath });
1480
- await (0, import_execa4.default)("git", ["add", "."], { cwd: projectPath });
1481
- await (0, import_execa4.default)("git", ["commit", "-m", "Initial commit from @digilogiclabs/create-saas-app"], {
1482
- cwd: projectPath
1483
- });
1484
- logger.debug("Git repository initialized successfully");
1485
- } catch (error) {
1486
- logger.warn("Git initialization failed. You can initialize it manually later.");
1487
- logger.debug("Git error:", error);
1488
- }
1489
- }
1490
-
1491
- // src/cli/commands/create.ts
1492
- async function createProject(platform, template, name, options = {}) {
1493
- try {
1494
- if (!await validateNodeVersion()) {
1495
- process.exit(1);
1496
- }
1497
- const validPlatforms = ["web", "mobile", "both"];
1498
- if (!validPlatforms.includes(platform)) {
1499
- logger.error(`Invalid platform: ${platform}. Must be one of: ${validPlatforms.join(", ")}`);
1500
- process.exit(1);
1501
- }
1502
- const parseAiCapabilities = (withAi) => {
1503
- if (!withAi) return { enabled: false, capabilities: [] };
1504
- if (withAi === true) return { enabled: true, capabilities: ["text"] };
1505
- if (withAi === "all")
1506
- return { enabled: true, capabilities: ["text", "audio", "video", "rag"] };
1507
- return { enabled: true, capabilities: withAi.split(",").map((c) => c.trim()) };
1508
- };
1509
- const aiConfig = parseAiCapabilities(options.withAi);
1510
- const selectTemplateForAI = (capabilities, baseTemplate) => {
1511
- if (!aiConfig.enabled) return baseTemplate || "base";
1512
- const hasAudio = capabilities.includes("audio");
1513
- const hasVideo = capabilities.includes("video");
1514
- const hasText = capabilities.includes("text");
1515
- const hasRAG = capabilities.includes("rag") || capabilities.includes("knowledge");
1516
- if (hasRAG) return "ui-auth-payments-ai-rag";
1517
- if (hasVideo || hasAudio || hasText) return "ui-auth-payments-ai";
1518
- return baseTemplate || "ui-auth-payments-ai";
1519
- };
1520
- const determineTier = () => {
1521
- if (options.tier) return options.tier;
1522
- if (aiConfig.enabled) return "pro";
1523
- return "starter";
1524
- };
1525
- let config = {
1526
- platform,
1527
- template: selectTemplateForAI(aiConfig.capabilities, template),
1528
- name: name || "my-saas-app",
1529
- tier: determineTier(),
1530
- auth: options.auth || "supabase",
1531
- database: options.database || "supabase",
1532
- theme: options.theme || "default",
1533
- themeColor: options.themeColor || "blue",
1534
- defaultTheme: options.defaultTheme || "system",
1535
- ai: {
1536
- enabled: aiConfig.enabled,
1537
- capabilities: aiConfig.capabilities,
1538
- provider: options.aiProvider || "openai"
1539
- },
1540
- install: options.install !== false,
1541
- git: options.git !== false
1542
- };
1543
- if (!options.yes) {
1544
- config = await getProjectPrompts(config);
1545
- }
1546
- if (!validateProjectName(config.name)) {
1547
- process.exit(1);
1548
- }
1549
- const projectPath = import_path5.default.resolve(process.cwd(), config.name);
1550
- if (!await validateProjectPath(projectPath)) {
1551
- process.exit(1);
1552
- }
1553
- logger.title("\u{1F680} Creating your SaaS application");
1554
- logger.info(`Platform: ${config.platform}`);
1555
- logger.info(`Tier: ${config.tier}`);
1556
- logger.info(`Template: ${config.template}`);
1557
- logger.info(`Project: ${config.name}`);
1558
- logger.info(`Auth: ${config.auth}`);
1559
- logger.info(`Database: ${config.database}`);
1560
- logger.info(`Theme: ${config.theme}`);
1561
- logger.info(`Theme Color: ${config.themeColor}`);
1562
- logger.info(`Default Theme: ${config.defaultTheme}`);
1563
- if (config.ai.enabled) {
1564
- logger.info(`AI: Enabled (${config.ai.capabilities.join(", ")}) - ${config.ai.provider}`);
1565
- }
1566
- logger.newLine();
1567
- const templateSpinner = spinner("Generating project structure...");
1568
- try {
1569
- const generator = new TemplateGenerator(config);
1570
- await generator.generate(projectPath);
1571
- templateSpinner.succeed("Project structure created");
1572
- } catch (_error) {
1573
- templateSpinner.fail("Failed to generate project structure");
1574
- throw _error;
1575
- }
1576
- if (config.install) {
1577
- const installSpinner = spinner("Installing dependencies...");
1578
- try {
1579
- const installer = new PackageInstaller(projectPath);
1580
- await installer.install();
1581
- installSpinner.succeed("Dependencies installed");
1582
- } catch (_error) {
1583
- installSpinner.fail("Failed to install dependencies");
1584
- logger.warn("You can install dependencies manually by running:");
1585
- logger.code(`cd ${config.name} && npm install`);
1586
- }
1587
- }
1588
- if (config.git) {
1589
- const gitSpinner = spinner("Initializing git repository...");
1590
- try {
1591
- await initializeGit(projectPath);
1592
- gitSpinner.succeed("Git repository initialized");
1593
- } catch (_error) {
1594
- gitSpinner.fail("Failed to initialize git repository");
1595
- logger.warn("You can initialize git manually by running:");
1596
- logger.code(`cd ${config.name} && git init`);
1597
- }
1598
- }
1599
- logger.newLine();
1600
- logger.success("\u{1F389} Project created successfully!");
1601
- logger.newLine();
1602
- logger.title("Next steps:");
1603
- logger.code(`cd ${config.name}`);
1604
- if (!config.install) {
1605
- logger.code("npm install");
1606
- }
1607
- if (config.platform === "web" || config.platform === "both") {
1608
- logger.code("npm run dev");
1609
- logger.info("Open http://localhost:3000 in your browser");
1610
- }
1611
- if (config.platform === "mobile" || config.platform === "both") {
1612
- logger.code("npx expo start");
1613
- logger.info("Scan QR code with Expo Go app");
1614
- }
1615
- logger.newLine();
1616
- logger.info("\u{1F4DA} Documentation: https://docs.digilogiclabs.com");
1617
- logger.info("\u{1F4AC} Support: https://discord.gg/digilogiclabs");
1618
- } catch (error) {
1619
- logger.error("Failed to create project:");
1620
- logger.error(error instanceof Error ? error.message : String(error));
1621
- process.exit(1);
1622
- }
1623
- }
1624
-
1625
- // src/cli/commands/add.ts
1626
- async function addFeature(feature, _options = {}) {
1627
- try {
1628
- logger.title(`Adding ${feature} feature`);
1629
- const validFeatures = [
1630
- "auth",
1631
- "billing",
1632
- "analytics",
1633
- "teams",
1634
- "notifications",
1635
- "admin",
1636
- "api",
1637
- "docs"
1638
- ];
1639
- if (!validFeatures.includes(feature)) {
1640
- logger.error(`Invalid feature: ${feature}`);
1641
- logger.info("Available features:");
1642
- logger.list(validFeatures);
1643
- process.exit(1);
1644
- }
1645
- const addSpinner = spinner(`Adding ${feature} feature...`);
1646
- await new Promise((resolve) => setTimeout(resolve, 2e3));
1647
- addSpinner.succeed(`${feature} feature added successfully`);
1648
- logger.newLine();
1649
- logger.success("Feature added successfully!");
1650
- logger.info("Don't forget to update your environment variables if needed.");
1651
- } catch (error) {
1652
- logger.error("Failed to add feature:");
1653
- logger.error(error instanceof Error ? error.message : String(error));
1654
- process.exit(1);
1655
- }
1656
- }
1657
-
1658
- // src/cli/commands/update.ts
1659
- var import_execa5 = __toESM(require("execa"));
1660
- var DIGILOGIC_PACKAGES = [
1661
- "@digilogiclabs/saas-factory-ui",
1662
- "@digilogiclabs/saas-factory-auth",
1663
- "@digilogiclabs/saas-factory-billing",
1664
- "@digilogiclabs/saas-factory-analytics"
1665
- ];
1666
- async function updateDependencies(options = {}) {
1667
- try {
1668
- logger.title("Updating Digi Logic Labs dependencies");
1669
- const packageManager = await detectPackageManager();
1670
- logger.info(`Using package manager: ${packageManager}`);
1671
- if (options.check) {
1672
- const checkSpinner = spinner("Checking for updates...");
1673
- try {
1674
- const result = await (0, import_execa5.default)(packageManager, ["outdated", ...DIGILOGIC_PACKAGES], {
1675
- reject: false
1676
- });
1677
- checkSpinner.succeed("Update check completed");
1678
- if (result.stdout) {
1679
- logger.info("Available updates:");
1680
- logger.log(result.stdout);
1681
- } else {
1682
- logger.success("All Digi Logic Labs packages are up to date!");
1683
- }
1684
- } catch (error) {
1685
- checkSpinner.fail("Failed to check for updates");
1686
- throw error;
1687
- }
1688
- } else {
1689
- const updateSpinner = spinner("Updating packages...");
1690
- try {
1691
- const command = await getAddCommand(packageManager, DIGILOGIC_PACKAGES);
1692
- await (0, import_execa5.default)(command[0], command.slice(1), {
1693
- stdio: "inherit"
1694
- });
1695
- updateSpinner.succeed("Packages updated successfully");
1696
- logger.newLine();
1697
- logger.success("All Digi Logic Labs packages have been updated!");
1698
- logger.info("Don't forget to test your application after updating.");
1699
- } catch (error) {
1700
- updateSpinner.fail("Failed to update packages");
1701
- throw error;
1702
- }
1703
- }
1704
- } catch (error) {
1705
- logger.error("Failed to update dependencies:");
1706
- logger.error(error instanceof Error ? error.message : String(error));
1707
- process.exit(1);
1708
- }
1709
- }
1710
-
1711
- // package.json
1712
- var package_default = {
1713
- name: "@digilogiclabs/create-saas-app",
1714
- version: "2.1.0",
1715
- description: "Create modern SaaS applications with DLL Platform - tier-aware scaffolding with platform-core and app-sdk",
1716
- main: "dist/cli/index.js",
1717
- bin: {
1718
- "create-saas-app": "bin/index.js"
1719
- },
1720
- scripts: {
1721
- build: "tsc --project tsconfig.build.json && npm run copy-templates",
1722
- "copy-templates": `node -e "require('fs-extra').copySync('src/templates', 'dist/templates')"`,
1723
- dev: "tsup --watch",
1724
- test: "jest --passWithNoTests",
1725
- "test:templates": "tsx scripts/test-all-templates.ts",
1726
- lint: "eslint src",
1727
- "lint:fix": "eslint src --fix",
1728
- "type-check": "tsc --noEmit --skipLibCheck",
1729
- validate: "tsx scripts/validate-templates.ts",
1730
- changeset: "changeset",
1731
- version: "changeset version",
1732
- release: "npm run build && changeset publish",
1733
- prepublishOnly: "npm run build && npm run lint && npm run type-check"
1734
- },
1735
- keywords: [
1736
- "cli",
1737
- "saas",
1738
- "template",
1739
- "nextjs",
1740
- "react-native",
1741
- "expo",
1742
- "typescript",
1743
- "tailwind",
1744
- "firebase",
1745
- "supabase",
1746
- "digilogiclabs"
1747
- ],
1748
- author: "Digi Logic Labs",
1749
- license: "MIT",
1750
- repository: {
1751
- type: "git",
1752
- url: "git+https://github.com/DigiLogicLabs/dll-platform.git",
1753
- directory: "packages/cli"
1754
- },
1755
- bugs: {
1756
- url: "https://github.com/DigiLogicLabs/dll-platform/issues"
1757
- },
1758
- homepage: "https://github.com/DigiLogicLabs/dll-platform/tree/main/packages/cli#readme",
1759
- files: [
1760
- "dist",
1761
- "bin",
1762
- "src/templates",
1763
- "README.md",
1764
- "CHANGELOG.md",
1765
- "LICENSE"
1766
- ],
1767
- engines: {
1768
- node: ">=18.0.0"
1769
- },
1770
- dependencies: {
1771
- chalk: "^4.1.2",
1772
- commander: "^11.1.0",
1773
- cosmiconfig: "^8.3.6",
1774
- execa: "^5.1.1",
1775
- "fs-extra": "^11.1.1",
1776
- glob: "^10.3.10",
1777
- inquirer: "^9.2.12",
1778
- listr2: "^7.0.2",
1779
- mustache: "^4.2.0",
1780
- ora: "^5.4.1",
1781
- semver: "^7.5.4",
1782
- "validate-npm-package-name": "^5.0.0",
1783
- zod: "^3.22.4"
1784
- },
1785
- devDependencies: {
1786
- "@changesets/cli": "^2.26.2",
1787
- "@supabase/supabase-js": "^2.39.2",
1788
- "@types/fs-extra": "^11.0.4",
1789
- "@types/inquirer": "^9.0.7",
1790
- "@types/jest": "^29.5.6",
1791
- "@types/mustache": "^4.2.5",
1792
- "@types/node": "^20.8.7",
1793
- "@types/semver": "^7.5.4",
1794
- "@types/validate-npm-package-name": "^4.0.2",
1795
- eslint: "^8.57.1",
1796
- "eslint-config-prettier": "^9.1.2",
1797
- "eslint-plugin-prettier": "^5.5.4",
1798
- firebase: "^10.7.1",
1799
- globals: "^16.3.0",
1800
- jest: "^29.7.0",
1801
- prettier: "^3.0.3",
1802
- "ts-jest": "^29.1.1",
1803
- tsup: "^8.0.0",
1804
- tsx: "^4.20.0",
1805
- typescript: "^5.7.0",
1806
- "typescript-eslint": "^8.40.0"
1807
- },
1808
- peerDependencies: {
1809
- typescript: ">=4.9.0"
1810
- },
1811
- publishConfig: {
1812
- access: "public"
1813
- },
1814
- directories: {
1815
- doc: "docs",
1816
- test: "tests"
1817
- }
1818
- };
1819
-
1820
- // src/cli/index.ts
1821
- var program = new import_commander.Command();
1822
- program.name("create-saas-app").description("Create modern SaaS applications with Digi Logic Labs packages").version(package_default.version);
1823
- program.command("create").alias("c").description("Create a new SaaS application").argument("<platform>", "Platform: web, mobile, or both").argument("[template]", "Template: base, dashboard, saas (web) | base, tabs, stack (mobile)").argument("[name]", "Project name").option("-a, --auth <provider>", "Auth provider: firebase, supabase").option("-d, --database <provider>", "Database provider: supabase, firebase").option("-t, --theme <theme>", "Theme: default, corporate, startup").option("-c, --theme-color <color>", "Theme color: blue, green, purple, orange, red, slate").option("--default-theme <mode>", "Default theme mode: light, dark, system").option("--with-ai [capabilities]", "Enable AI features (text,audio,video,rag,knowledge or all)").option("--ai-provider <provider>", "AI provider: openai, anthropic, gemini").option("--no-install", "Skip package installation").option("--no-git", "Skip git initialization").option("-y, --yes", "Skip interactive prompts").action(createProject);
1824
- program.command("add").alias("a").description("Add features to existing project").argument("<feature>", "Feature to add: auth, billing, analytics, etc.").option("-p, --provider <provider>", "Service provider").action(addFeature);
1825
- program.command("update").alias("u").description("Update Digi Logic Labs dependencies").option("--check", "Check for updates without installing").action(updateDependencies);
1826
- program.on("command:*", () => {
1827
- logger.error(`Unknown command: ${program.args.join(" ")}`);
1828
- logger.info('Run "create-saas-app --help" for available commands');
1829
- process.exit(1);
1830
- });
1831
- if (!process.argv.slice(2).length) {
1832
- program.outputHelp();
1833
- process.exit(0);
1834
- }
1835
- program.parse();
1836
- var index_default = program;
1837
- //# sourceMappingURL=index.js.map