@digilogiclabs/create-saas-app 2.12.1 → 2.13.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.
Files changed (113) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/index.js +1870 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/templates/mobile/base/template/package.json +2 -2
  6. package/dist/templates/mobile/ui-auth-payments/template/package.json +2 -2
  7. package/dist/templates/mobile/ui-auth-payments-ai/template/package.json +2 -2
  8. package/dist/templates/web/ai-platform/template/package.json +3 -3
  9. package/dist/templates/web/base/template/package.json +3 -3
  10. package/dist/templates/web/iot-dashboard/template/package.json +3 -3
  11. package/dist/templates/web/marketplace/template/package.json +3 -3
  12. package/dist/templates/web/micro-saas/template/package.json +3 -3
  13. package/dist/templates/web/ui-auth/template/package.json +3 -3
  14. package/dist/templates/web/ui-auth-ai/template/package.json +3 -3
  15. package/dist/templates/web/ui-auth-payments/template/package.json +3 -3
  16. package/dist/templates/web/ui-auth-payments-ai/template/package.json +3 -3
  17. package/dist/templates/web/ui-auth-payments-audio/template/package.json +3 -3
  18. package/dist/templates/web/ui-auth-payments-video/template/package.json +3 -3
  19. package/dist/templates/web/ui-only/template/package.json +2 -2
  20. package/dist/templates/web/ui-package-test/template/package.json +3 -3
  21. package/package.json +1 -1
  22. package/src/templates/mobile/base/template/package.json +2 -2
  23. package/src/templates/mobile/ui-auth-payments/template/package.json +2 -2
  24. package/src/templates/mobile/ui-auth-payments-ai/template/package.json +2 -2
  25. package/src/templates/web/ai-platform/template/package.json +3 -3
  26. package/src/templates/web/base/template/package.json +3 -3
  27. package/src/templates/web/iot-dashboard/template/package.json +3 -3
  28. package/src/templates/web/marketplace/template/package.json +3 -3
  29. package/src/templates/web/micro-saas/template/package.json +3 -3
  30. package/src/templates/web/ui-auth/template/package.json +3 -3
  31. package/src/templates/web/ui-auth-ai/template/package.json +3 -3
  32. package/src/templates/web/ui-auth-payments/template/package.json +3 -3
  33. package/src/templates/web/ui-auth-payments-ai/template/package.json +3 -3
  34. package/src/templates/web/ui-auth-payments-audio/template/package.json +3 -3
  35. package/src/templates/web/ui-auth-payments-video/template/package.json +3 -3
  36. package/src/templates/web/ui-only/template/package.json +2 -2
  37. package/src/templates/web/ui-package-test/template/package.json +3 -3
  38. package/dist/cli/commands/add.d.ts +0 -6
  39. package/dist/cli/commands/add.d.ts.map +0 -1
  40. package/dist/cli/commands/add.js +0 -39
  41. package/dist/cli/commands/add.js.map +0 -1
  42. package/dist/cli/commands/create.d.ts +0 -49
  43. package/dist/cli/commands/create.d.ts.map +0 -1
  44. package/dist/cli/commands/create.js +0 -173
  45. package/dist/cli/commands/create.js.map +0 -1
  46. package/dist/cli/commands/index.d.ts +0 -4
  47. package/dist/cli/commands/index.d.ts.map +0 -1
  48. package/dist/cli/commands/index.js +0 -20
  49. package/dist/cli/commands/index.js.map +0 -1
  50. package/dist/cli/commands/update.d.ts +0 -6
  51. package/dist/cli/commands/update.d.ts.map +0 -1
  52. package/dist/cli/commands/update.js +0 -68
  53. package/dist/cli/commands/update.js.map +0 -1
  54. package/dist/cli/index.d.ts +0 -4
  55. package/dist/cli/index.d.ts.map +0 -1
  56. package/dist/cli/index.js +0 -63
  57. package/dist/cli/index.js.map +0 -1
  58. package/dist/cli/prompts/index.d.ts +0 -2
  59. package/dist/cli/prompts/index.d.ts.map +0 -1
  60. package/dist/cli/prompts/index.js +0 -18
  61. package/dist/cli/prompts/index.js.map +0 -1
  62. package/dist/cli/prompts/project-setup.d.ts +0 -5
  63. package/dist/cli/prompts/project-setup.d.ts.map +0 -1
  64. package/dist/cli/prompts/project-setup.js +0 -359
  65. package/dist/cli/prompts/project-setup.js.map +0 -1
  66. package/dist/cli/utils/git.d.ts +0 -9
  67. package/dist/cli/utils/git.d.ts.map +0 -1
  68. package/dist/cli/utils/git.js +0 -77
  69. package/dist/cli/utils/git.js.map +0 -1
  70. package/dist/cli/utils/index.d.ts +0 -5
  71. package/dist/cli/utils/index.d.ts.map +0 -1
  72. package/dist/cli/utils/index.js +0 -21
  73. package/dist/cli/utils/index.js.map +0 -1
  74. package/dist/cli/utils/logger.d.ts +0 -16
  75. package/dist/cli/utils/logger.d.ts.map +0 -1
  76. package/dist/cli/utils/logger.js +0 -55
  77. package/dist/cli/utils/logger.js.map +0 -1
  78. package/dist/cli/utils/package-manager.d.ts +0 -8
  79. package/dist/cli/utils/package-manager.d.ts.map +0 -1
  80. package/dist/cli/utils/package-manager.js +0 -92
  81. package/dist/cli/utils/package-manager.js.map +0 -1
  82. package/dist/cli/utils/spinner.d.ts +0 -7
  83. package/dist/cli/utils/spinner.d.ts.map +0 -1
  84. package/dist/cli/utils/spinner.js +0 -48
  85. package/dist/cli/utils/spinner.js.map +0 -1
  86. package/dist/cli/validators/dependencies.d.ts +0 -15
  87. package/dist/cli/validators/dependencies.d.ts.map +0 -1
  88. package/dist/cli/validators/dependencies.js +0 -108
  89. package/dist/cli/validators/dependencies.js.map +0 -1
  90. package/dist/cli/validators/index.d.ts +0 -3
  91. package/dist/cli/validators/index.d.ts.map +0 -1
  92. package/dist/cli/validators/index.js +0 -19
  93. package/dist/cli/validators/index.js.map +0 -1
  94. package/dist/cli/validators/project-name.d.ts +0 -5
  95. package/dist/cli/validators/project-name.d.ts.map +0 -1
  96. package/dist/cli/validators/project-name.js +0 -151
  97. package/dist/cli/validators/project-name.js.map +0 -1
  98. package/dist/generators/file-processor.d.ts +0 -28
  99. package/dist/generators/file-processor.d.ts.map +0 -1
  100. package/dist/generators/file-processor.js +0 -224
  101. package/dist/generators/file-processor.js.map +0 -1
  102. package/dist/generators/index.d.ts +0 -4
  103. package/dist/generators/index.d.ts.map +0 -1
  104. package/dist/generators/index.js +0 -20
  105. package/dist/generators/index.js.map +0 -1
  106. package/dist/generators/package-installer.d.ts +0 -29
  107. package/dist/generators/package-installer.d.ts.map +0 -1
  108. package/dist/generators/package-installer.js +0 -177
  109. package/dist/generators/package-installer.js.map +0 -1
  110. package/dist/generators/template-generator.d.ts +0 -84
  111. package/dist/generators/template-generator.d.ts.map +0 -1
  112. package/dist/generators/template-generator.js +0 -937
  113. package/dist/generators/template-generator.js.map +0 -1
@@ -1,937 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.TemplateGenerator = void 0;
7
- const path_1 = __importDefault(require("path"));
8
- const fs_extra_1 = __importDefault(require("fs-extra"));
9
- const mustache_1 = __importDefault(require("mustache"));
10
- const glob_1 = require("glob");
11
- const logger_1 = require("../cli/utils/logger");
12
- /**
13
- * Tier-based feature configuration
14
- * Determines which DLL packages and features are included per tier
15
- */
16
- const TIER_FEATURES = {
17
- micro: {
18
- platformCore: true,
19
- auth: true,
20
- payments: false,
21
- ai: false,
22
- observability: false,
23
- workers: false,
24
- beta: true,
25
- },
26
- starter: {
27
- platformCore: true,
28
- auth: true,
29
- payments: true,
30
- ai: false,
31
- observability: false,
32
- workers: false,
33
- beta: true,
34
- },
35
- pro: {
36
- platformCore: true,
37
- auth: true,
38
- payments: true,
39
- ai: true,
40
- observability: true,
41
- workers: false,
42
- beta: true,
43
- },
44
- enterprise: {
45
- platformCore: true,
46
- auth: true,
47
- payments: true,
48
- ai: true,
49
- observability: true,
50
- workers: true,
51
- beta: true,
52
- },
53
- };
54
- class TemplateGenerator {
55
- constructor(config) {
56
- this.config = config;
57
- // Always use the templates directory relative to __dirname
58
- // In the built package, templates are copied to dist/templates
59
- // __dirname is dist/generators, so we need to go up one level
60
- this.templatesDir = path_1.default.join(__dirname, '..', 'templates');
61
- this.context = this.createTemplateContext();
62
- }
63
- createTemplateContext() {
64
- const projectName = this.config.name;
65
- const packageName = projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
66
- const className = this.toPascalCase(projectName);
67
- const camelCaseName = this.toCamelCase(projectName);
68
- const kebabCaseName = packageName;
69
- const titleCaseName = this.toTitleCase(projectName);
70
- const slugNameCamelCase = this.toCamelCase(packageName);
71
- // Get tier features
72
- const tier = this.config.tier || 'starter';
73
- const features = TIER_FEATURES[tier];
74
- const dependencies = this.getDependencies();
75
- const generatedDate = new Date().toLocaleDateString('en-US', {
76
- year: 'numeric',
77
- month: 'short',
78
- day: 'numeric',
79
- });
80
- // Determine tier description for template
81
- const tierDescriptions = {
82
- micro: 'minimal',
83
- starter: 'standard',
84
- pro: 'AI-powered',
85
- enterprise: 'enterprise-grade',
86
- };
87
- return {
88
- projectName,
89
- platform: this.config.platform,
90
- template: this.config.template,
91
- tier,
92
- auth: this.config.auth,
93
- database: this.config.database,
94
- theme: this.config.theme,
95
- themeColor: this.config.themeColor,
96
- defaultTheme: this.config.defaultTheme,
97
- landingStyle: this.config.landingStyle || 'product',
98
- motion: this.config.motion || 'standard',
99
- ai: {
100
- enabled: this.config.ai.enabled || features.ai,
101
- capabilities: this.config.ai.capabilities,
102
- provider: this.config.ai.provider,
103
- hasText: this.config.ai.capabilities.includes('text'),
104
- hasAudio: this.config.ai.capabilities.includes('audio'),
105
- hasVideo: this.config.ai.capabilities.includes('video'),
106
- },
107
- features,
108
- packageName,
109
- className,
110
- camelCaseName,
111
- kebabCaseName,
112
- titleCaseName,
113
- slugNameCamelCase,
114
- description: `A ${tierDescriptions[tier]} SaaS application built with DLL Platform`,
115
- author: 'Digi Logic Labs',
116
- year: new Date().getFullYear(),
117
- generatedDate,
118
- generatorVersion: '2.0.0',
119
- // DLL package versions
120
- platformCoreVersion: dependencies['@digilogiclabs/platform-core']?.replace('^', '') || '1.14.0',
121
- // appSdkVersion removed — @digilogiclabs/app-sdk is deprecated
122
- uiVersion: dependencies['@digilogiclabs/saas-factory-ui']?.replace('^', '') || '1.6.0',
123
- authVersion: '5.1.0', // deprecated — kept for backward-compatible mustache templates
124
- paymentsVersion: '5.1.0', // deprecated — kept for backward-compatible mustache templates
125
- aiVersion: dependencies['@digilogiclabs/saas-factory-ai']?.replace('^', '') || '8.0.0',
126
- dependencies,
127
- devDependencies: this.getDevDependencies(),
128
- scripts: this.getScripts(),
129
- };
130
- }
131
- getDependencies() {
132
- const baseDeps = {};
133
- const tier = this.config.tier || 'starter';
134
- const features = TIER_FEATURES[tier];
135
- // ═══════════════════════════════════════════════════════════════
136
- // DLL PLATFORM CORE PACKAGES (tier-based)
137
- // ═══════════════════════════════════════════════════════════════
138
- // Platform Core - Always included for all tiers (infrastructure abstraction)
139
- if (features.platformCore) {
140
- baseDeps['@digilogiclabs/platform-core'] = '^1.14.0';
141
- }
142
- // App SDK — deprecated and removed. Auth/payments hooks should use
143
- // @digilogiclabs/saas-factory-auth and @digilogiclabs/saas-factory-payments directly.
144
- // UI Components - included for most templates
145
- if ([
146
- 'base',
147
- 'ui-only',
148
- 'ui-auth',
149
- 'ui-auth-ai',
150
- 'ui-auth-payments',
151
- 'ui-auth-payments-audio',
152
- 'ui-auth-payments-video',
153
- 'ui-auth-payments-ai',
154
- // Vertical templates
155
- 'micro-saas',
156
- 'marketplace',
157
- 'ai-platform',
158
- 'iot-dashboard',
159
- ].includes(this.config.template)) {
160
- baseDeps['@digilogiclabs/saas-factory-ui'] = '^1.8.1';
161
- }
162
- // AI packages (tier: pro+ or AI-focused templates)
163
- if (features.ai ||
164
- this.config.ai.enabled ||
165
- this.config.template.includes('-ai') ||
166
- this.config.template === 'ai-platform') {
167
- baseDeps['@digilogiclabs/saas-factory-ai'] = '^8.0.1';
168
- baseDeps['@digilogiclabs/saas-factory-ai-types'] = '^1.0.0';
169
- // Add AI SDK for ai-platform template
170
- if (this.config.template === 'ai-platform') {
171
- baseDeps['ai'] = '^6.0.0';
172
- baseDeps['@ai-sdk/openai'] = '^3.0.0';
173
- baseDeps['@ai-sdk/anthropic'] = '^3.0.0';
174
- }
175
- }
176
- // IoT Dashboard specific dependencies
177
- if (this.config.template === 'iot-dashboard') {
178
- baseDeps['recharts'] = '^2.14.0';
179
- baseDeps['date-fns'] = '^4.1.0';
180
- }
181
- // Marketplace specific dependencies
182
- if (this.config.template === 'marketplace') {
183
- baseDeps['stripe'] = '^17.4.0';
184
- }
185
- // ═══════════════════════════════════════════════════════════════
186
- // PLATFORM DEPENDENCIES (web/mobile)
187
- // ═══════════════════════════════════════════════════════════════
188
- if (this.config.platform === 'web' || this.config.platform === 'both') {
189
- Object.assign(baseDeps, {
190
- next: '^15.5.0',
191
- react: '^19.0.0',
192
- 'react-dom': '^19.0.0',
193
- tailwindcss: '^4.1.0',
194
- '@tailwindcss/postcss': '^4.1.0',
195
- typescript: '^5.8.0',
196
- clsx: '^2.1.0',
197
- 'class-variance-authority': '^0.7.0',
198
- 'tailwind-merge': '^2.6.0',
199
- 'next-themes': '^0.4.0',
200
- 'lucide-react': '^0.460.0',
201
- zod: '^4.1.0',
202
- });
203
- }
204
- if (this.config.platform === 'mobile' || this.config.platform === 'both') {
205
- Object.assign(baseDeps, {
206
- expo: '~52.0.0',
207
- 'react-native': '0.76.0',
208
- '@expo/vector-icons': '^14.0.0',
209
- '@react-navigation/native': '^7.0.0',
210
- '@react-navigation/bottom-tabs': '^7.0.0',
211
- 'react-native-screens': '~4.0.0',
212
- 'react-native-safe-area-context': '4.12.0',
213
- 'react-native-gesture-handler': '~2.20.0',
214
- 'expo-router': '~4.0.0',
215
- });
216
- // Stripe for mobile if payments enabled
217
- if (features.payments) {
218
- baseDeps['@stripe/stripe-react-native'] = '^0.39.0';
219
- }
220
- }
221
- // ═══════════════════════════════════════════════════════════════
222
- // AUTH PROVIDER SDKS
223
- // ═══════════════════════════════════════════════════════════════
224
- if (this.config.auth === 'firebase') {
225
- baseDeps['firebase'] = '^11.0.0';
226
- }
227
- else if (this.config.auth === 'supabase') {
228
- baseDeps['@supabase/supabase-js'] = '^2.46.0';
229
- if (this.config.platform === 'web' || this.config.platform === 'both') {
230
- baseDeps['@supabase/ssr'] = '^0.5.0';
231
- }
232
- }
233
- else if (this.config.auth === 'keycloak') {
234
- if (this.config.platform === 'web' || this.config.platform === 'both') {
235
- baseDeps['next-auth'] = '^5.0.0-beta.30';
236
- }
237
- }
238
- // ═══════════════════════════════════════════════════════════════
239
- // INFRASTRUCTURE DEPENDENCIES
240
- // ═══════════════════════════════════════════════════════════════
241
- // Redis (starter+ tiers — rate limiting, caching)
242
- if (features.payments || features.ai || features.observability) {
243
- baseDeps['ioredis'] = '^5.6.0';
244
- }
245
- // Database (PostgreSQL + Drizzle)
246
- if (this.config.database === 'postgresql') {
247
- baseDeps['drizzle-orm'] = '^0.43.0';
248
- baseDeps['postgres'] = '^3.4.0';
249
- baseDeps['dotenv'] = '^16.4.0';
250
- }
251
- // Stripe (starter+ tiers — payments)
252
- if (features.payments) {
253
- baseDeps['stripe'] = '^17.4.0';
254
- if (this.config.platform === 'web' || this.config.platform === 'both') {
255
- baseDeps['@stripe/stripe-js'] = '^8.0.0';
256
- baseDeps['@stripe/react-stripe-js'] = '^5.6.0';
257
- }
258
- }
259
- // Resend (starter+ tiers — transactional email)
260
- if (features.payments) {
261
- baseDeps['resend'] = '^4.0.0';
262
- }
263
- // Server-only guard
264
- baseDeps['server-only'] = '^0.0.1';
265
- return baseDeps;
266
- }
267
- getDevDependencies() {
268
- const baseDeps = {
269
- '@types/node': '^22.0.0',
270
- eslint: '^9.0.0',
271
- prettier: '^3.4.0',
272
- typescript: '^5.7.0',
273
- };
274
- if (this.config.platform === 'web' || this.config.platform === 'both') {
275
- Object.assign(baseDeps, {
276
- '@types/react': '^19.0.0',
277
- '@types/react-dom': '^19.0.0',
278
- '@eslint/eslintrc': '^3.0.0',
279
- 'eslint-config-next': '^15.0.0',
280
- });
281
- }
282
- // Drizzle Kit for PostgreSQL
283
- if (this.config.database === 'postgresql') {
284
- baseDeps['drizzle-kit'] = '^0.30.0';
285
- }
286
- // Add testing dependencies for pro+ tiers
287
- const tier = this.config.tier || 'starter';
288
- if (['pro', 'enterprise'].includes(tier)) {
289
- Object.assign(baseDeps, {
290
- vitest: '^2.1.0',
291
- '@testing-library/react': '^16.0.0',
292
- '@testing-library/jest-dom': '^6.6.0',
293
- });
294
- }
295
- return baseDeps;
296
- }
297
- getScripts() {
298
- const scripts = {};
299
- if (this.config.platform === 'web' || this.config.platform === 'both') {
300
- Object.assign(scripts, {
301
- dev: 'next dev',
302
- build: 'next build',
303
- start: 'next start',
304
- lint: 'next lint',
305
- });
306
- }
307
- if (this.config.platform === 'mobile' || this.config.platform === 'both') {
308
- Object.assign(scripts, {
309
- start: 'expo start',
310
- android: 'expo start --android',
311
- ios: 'expo start --ios',
312
- web: 'expo start --web',
313
- });
314
- }
315
- return scripts;
316
- }
317
- async generate(outputPath) {
318
- try {
319
- // Ensure output directory exists
320
- await fs_extra_1.default.ensureDir(outputPath);
321
- // Generate based on platform
322
- if (this.config.platform === 'web') {
323
- await this.generateWebProject(outputPath);
324
- }
325
- else if (this.config.platform === 'mobile') {
326
- await this.generateMobileProject(outputPath);
327
- }
328
- else if (this.config.platform === 'both') {
329
- await this.generateWebProject(outputPath);
330
- await this.generateMobileProject(path_1.default.join(outputPath, 'mobile'));
331
- }
332
- // Copy shared resources
333
- await this.copySharedResources(outputPath);
334
- // Patch package.json with aligned dependency versions
335
- await this.patchPackageJson(outputPath);
336
- logger_1.logger.debug('Template generation completed');
337
- }
338
- catch (error) {
339
- logger_1.logger.error('Template generation failed:', error);
340
- throw error;
341
- }
342
- }
343
- async generateWebProject(outputPath) {
344
- const templatePath = path_1.default.join(this.templatesDir, 'web', this.config.template);
345
- await this.copyTemplate(templatePath, outputPath);
346
- }
347
- async generateMobileProject(outputPath) {
348
- // Map web templates to mobile templates where applicable
349
- let mobileTemplate = this.config.template;
350
- // If we don't have a mobile version, try to find the closest match
351
- if (!(await fs_extra_1.default.pathExists(path_1.default.join(this.templatesDir, 'mobile', mobileTemplate)))) {
352
- // Map web templates to available mobile templates
353
- if (mobileTemplate.includes('-ai') || mobileTemplate === 'ui-auth-payments-ai') {
354
- mobileTemplate = 'ui-auth-payments-ai';
355
- }
356
- else if (mobileTemplate.startsWith('ui-auth-payments')) {
357
- mobileTemplate = 'ui-auth-payments';
358
- }
359
- else if (mobileTemplate.startsWith('ui-auth')) {
360
- // Use the ui-auth-payments template for other ui-auth variants
361
- mobileTemplate = 'ui-auth-payments';
362
- }
363
- else if (mobileTemplate.startsWith('ui-')) {
364
- mobileTemplate = 'base';
365
- }
366
- }
367
- const templatePath = path_1.default.join(this.templatesDir, 'mobile', mobileTemplate);
368
- await this.copyTemplate(templatePath, outputPath);
369
- // Create assets directory and basic assets for mobile projects
370
- await this.createMobileAssets(outputPath);
371
- }
372
- async copyTemplate(templatePath, outputPath) {
373
- const templateDir = path_1.default.join(templatePath, 'template');
374
- if (!(await fs_extra_1.default.pathExists(templateDir))) {
375
- throw new Error(`Template not found: ${templateDir}`);
376
- }
377
- await this.copyAndProcessFiles(templateDir, outputPath);
378
- }
379
- async copyAndProcessFiles(sourceDir, targetDir) {
380
- if (!(await fs_extra_1.default.pathExists(sourceDir))) {
381
- logger_1.logger.debug(`Template source directory not found: ${sourceDir}`);
382
- return;
383
- }
384
- const files = await (0, glob_1.glob)('**/*', {
385
- cwd: sourceDir,
386
- dot: true,
387
- nodir: true,
388
- });
389
- for (const file of files) {
390
- const sourcePath = path_1.default.join(sourceDir, file);
391
- const targetPath = path_1.default.join(targetDir, file);
392
- await fs_extra_1.default.ensureDir(path_1.default.dirname(targetPath));
393
- const content = await fs_extra_1.default.readFile(sourcePath, 'utf-8');
394
- if (this.isTextFile(file)) {
395
- // Check if file contains mustache variables before processing
396
- const hasMustacheVars = content.includes('{{') && content.includes('}}');
397
- const ext = path_1.default.extname(file).toLowerCase();
398
- // For JSX/TSX files without mustache variables, skip processing to preserve template literals
399
- if (['.tsx', '.jsx'].includes(ext) && !hasMustacheVars) {
400
- await fs_extra_1.default.writeFile(targetPath, content);
401
- }
402
- else if (hasMustacheVars) {
403
- // Only process files that actually have mustache variables
404
- // For JSX/TSX files, we need to protect template literals during mustache processing
405
- if (['.tsx', '.jsx'].includes(ext)) {
406
- // Step 1: Replace template literals and JSX object literals with placeholders
407
- const templateLiterals = [];
408
- let protectedContent = content.replace(/\$\{([^}]+)\}/g, (match, _variable) => {
409
- templateLiterals.push(match);
410
- return `__TEMPLATE_LITERAL_${templateLiterals.length - 1}__`;
411
- });
412
- // Step 1.5: Protect JSX object literals {{ }} from mustache processing
413
- const jsxObjectLiterals = [];
414
- protectedContent = this.protectJSXObjectLiterals(protectedContent, jsxObjectLiterals);
415
- // Step 2: Process mustache variables
416
- const processedContent = mustache_1.default.render(protectedContent, this.context);
417
- // Step 3: Restore template literals and JSX object literals
418
- let finalContent = processedContent.replace(/__TEMPLATE_LITERAL_(\d+)__/g, (match, index) => {
419
- return templateLiterals[parseInt(index)];
420
- });
421
- // Step 4: Restore JSX object literals
422
- finalContent = finalContent.replace(/__JSX_OBJECT_(\d+)__/g, (match, index) => {
423
- return jsxObjectLiterals[parseInt(index)];
424
- });
425
- await fs_extra_1.default.writeFile(targetPath, finalContent);
426
- }
427
- else {
428
- // For non-JSX files, process normally
429
- const processedContent = mustache_1.default.render(content, this.context);
430
- await fs_extra_1.default.writeFile(targetPath, processedContent);
431
- }
432
- }
433
- else {
434
- // Copy other text files directly
435
- await fs_extra_1.default.writeFile(targetPath, content);
436
- }
437
- }
438
- else {
439
- await fs_extra_1.default.copy(sourcePath, targetPath);
440
- }
441
- }
442
- }
443
- async copySharedResources(outputPath) {
444
- // Skip copying shared resources for minimal/test templates
445
- if (this.config.template === 'ui-only' || this.config.template === 'ui-package-test') {
446
- logger_1.logger.debug('Skipping shared resources for minimal template');
447
- return;
448
- }
449
- const sharedPath = path_1.default.join(this.templatesDir, 'shared');
450
- const isWeb = this.config.platform === 'web' || this.config.platform === 'both';
451
- const tier = this.config.tier || 'starter';
452
- const features = TIER_FEATURES[tier];
453
- // ═══════════════════════════════════════════════════════════════
454
- // AUTH — provider-specific config (Keycloak: auth.config.ts + auth.ts + federated-logout)
455
- // ═══════════════════════════════════════════════════════════════
456
- if (isWeb) {
457
- const authPath = path_1.default.join(sharedPath, 'auth', this.config.auth, 'web');
458
- await this.copyAndProcessFiles(authPath, outputPath);
459
- }
460
- // ═══════════════════════════════════════════════════════════════
461
- // MOCK — mock mode utilities (mock user, NEXT_PUBLIC_MOCK_MODE)
462
- // ═══════════════════════════════════════════════════════════════
463
- if (isWeb) {
464
- const mockPath = path_1.default.join(sharedPath, 'mock', 'web');
465
- if (await fs_extra_1.default.pathExists(mockPath)) {
466
- await this.copyAndProcessFiles(mockPath, outputPath);
467
- logger_1.logger.debug('Copied mock mode utilities');
468
- }
469
- }
470
- // ═══════════════════════════════════════════════════════════════
471
- // DATABASE — provider-specific setup (PostgreSQL/Drizzle or Supabase)
472
- // ═══════════════════════════════════════════════════════════════
473
- if (isWeb) {
474
- const dbPath = path_1.default.join(sharedPath, 'database', this.config.database, 'web');
475
- await this.copyAndProcessFiles(dbPath, outputPath);
476
- }
477
- // ═══════════════════════════════════════════════════════════════
478
- // THEME — theme-specific configuration
479
- // ═══════════════════════════════════════════════════════════════
480
- const themePath = path_1.default.join(sharedPath, 'themes', this.config.theme);
481
- await this.copyAndProcessFiles(themePath, outputPath);
482
- // ═══════════════════════════════════════════════════════════════
483
- // DESIGN — design.config.ts (design system foundation)
484
- // ═══════════════════════════════════════════════════════════════
485
- if (isWeb) {
486
- const designPath = path_1.default.join(sharedPath, 'design', 'web');
487
- if (await fs_extra_1.default.pathExists(designPath)) {
488
- await this.copyAndProcessFiles(designPath, outputPath);
489
- logger_1.logger.debug('Copied design config template');
490
- }
491
- }
492
- // ═══════════════════════════════════════════════════════════════
493
- // QUALITY — Lighthouse CI + axe-core accessibility test stubs (pro+ only — needs testing deps)
494
- // ═══════════════════════════════════════════════════════════════
495
- if (isWeb && ['pro', 'enterprise'].includes(tier)) {
496
- const qualityPath = path_1.default.join(sharedPath, 'quality', 'web');
497
- if (await fs_extra_1.default.pathExists(qualityPath)) {
498
- await this.copyAndProcessFiles(qualityPath, outputPath);
499
- logger_1.logger.debug('Copied quality tooling stubs');
500
- }
501
- }
502
- // ═══════════════════════════════════════════════════════════════
503
- // LANDING — shared landing page components (LandingPage, PricingSection)
504
- // ═══════════════════════════════════════════════════════════════
505
- if (isWeb && features.payments) {
506
- const landingPath = path_1.default.join(sharedPath, 'landing', 'web');
507
- if (await fs_extra_1.default.pathExists(landingPath)) {
508
- await this.copyAndProcessFiles(landingPath, path_1.default.join(outputPath));
509
- logger_1.logger.debug('Copied landing page components');
510
- }
511
- }
512
- // ═══════════════════════════════════════════════════════════════
513
- // BETA GATE — all tiers (API routes + client wrapper)
514
- // ═══════════════════════════════════════════════════════════════
515
- if (features.beta && isWeb) {
516
- const betaPath = path_1.default.join(sharedPath, 'beta', 'web');
517
- if (await fs_extra_1.default.pathExists(betaPath)) {
518
- await this.copyAndProcessFiles(betaPath, outputPath);
519
- logger_1.logger.debug('Copied beta gate templates');
520
- }
521
- }
522
- // ═══════════════════════════════════════════════════════════════
523
- // ENVIRONMENT CONFIG — centralized env validation (all tiers)
524
- // ═══════════════════════════════════════════════════════════════
525
- if (isWeb) {
526
- const configPath = path_1.default.join(sharedPath, 'config', 'web');
527
- if (await fs_extra_1.default.pathExists(configPath)) {
528
- await this.copyAndProcessFiles(configPath, outputPath);
529
- logger_1.logger.debug('Copied environment config template');
530
- }
531
- }
532
- // ═══════════════════════════════════════════════════════════════
533
- // REDIS — client + rate limit store (starter+ tiers)
534
- // ═══════════════════════════════════════════════════════════════
535
- if (isWeb && (features.payments || features.ai || features.observability)) {
536
- const redisPath = path_1.default.join(sharedPath, 'redis', 'web');
537
- if (await fs_extra_1.default.pathExists(redisPath)) {
538
- await this.copyAndProcessFiles(redisPath, outputPath);
539
- logger_1.logger.debug('Copied Redis client template');
540
- }
541
- }
542
- // ═══════════════════════════════════════════════════════════════
543
- // API SECURITY — wrapper + rate limiting (all tiers)
544
- // ═══════════════════════════════════════════════════════════════
545
- if (isWeb) {
546
- const securityPath = path_1.default.join(sharedPath, 'security', 'web');
547
- if (await fs_extra_1.default.pathExists(securityPath)) {
548
- await this.copyAndProcessFiles(securityPath, outputPath);
549
- logger_1.logger.debug('Copied API security wrapper template');
550
- }
551
- }
552
- // ═══════════════════════════════════════════════════════════════
553
- // MIDDLEWARE — route protection + auth (all tiers)
554
- // ═══════════════════════════════════════════════════════════════
555
- if (isWeb && this.config.auth === 'keycloak') {
556
- const middlewarePath = path_1.default.join(sharedPath, 'middleware', 'web');
557
- if (await fs_extra_1.default.pathExists(middlewarePath)) {
558
- await this.copyAndProcessFiles(middlewarePath, outputPath);
559
- logger_1.logger.debug('Copied middleware template');
560
- }
561
- }
562
- // ═══════════════════════════════════════════════════════════════
563
- // HEALTH CHECK — container readiness (all tiers)
564
- // ═══════════════════════════════════════════════════════════════
565
- if (isWeb) {
566
- const healthPath = path_1.default.join(sharedPath, 'health', 'web');
567
- if (await fs_extra_1.default.pathExists(healthPath)) {
568
- await this.copyAndProcessFiles(healthPath, outputPath);
569
- logger_1.logger.debug('Copied health check endpoint template');
570
- }
571
- }
572
- // ═══════════════════════════════════════════════════════════════
573
- // STRIPE WEBHOOKS — payment lifecycle (starter+ tiers)
574
- // ═══════════════════════════════════════════════════════════════
575
- if (isWeb && features.payments) {
576
- const paymentsPath = path_1.default.join(sharedPath, 'payments', 'web');
577
- if (await fs_extra_1.default.pathExists(paymentsPath)) {
578
- await this.copyAndProcessFiles(paymentsPath, outputPath);
579
- logger_1.logger.debug('Copied Stripe webhook handler template');
580
- }
581
- }
582
- // ═══════════════════════════════════════════════════════════════
583
- // AUDIT LOGGING — admin visibility (all authenticated apps)
584
- // ═══════════════════════════════════════════════════════════════
585
- if (isWeb && features.auth) {
586
- const auditPath = path_1.default.join(sharedPath, 'audit', 'web');
587
- if (await fs_extra_1.default.pathExists(auditPath)) {
588
- await this.copyAndProcessFiles(auditPath, outputPath);
589
- logger_1.logger.debug('Copied audit logging template');
590
- }
591
- }
592
- // ═══════════════════════════════════════════════════════════════
593
- // EMAIL BRANDING — transactional emails (all authenticated apps)
594
- // ═══════════════════════════════════════════════════════════════
595
- if (isWeb && features.auth) {
596
- const emailPath = path_1.default.join(sharedPath, 'email', 'web');
597
- if (await fs_extra_1.default.pathExists(emailPath)) {
598
- await this.copyAndProcessFiles(emailPath, outputPath);
599
- logger_1.logger.debug('Copied email branding template');
600
- }
601
- }
602
- // ═══════════════════════════════════════════════════════════════
603
- // PLATFORM-CORE INIT — singleton adapter wiring (all tiers)
604
- // ═══════════════════════════════════════════════════════════════
605
- if (isWeb) {
606
- const platformPath = path_1.default.join(sharedPath, 'platform', 'web');
607
- if (await fs_extra_1.default.pathExists(platformPath)) {
608
- await this.copyAndProcessFiles(platformPath, outputPath);
609
- logger_1.logger.debug('Copied platform-core initialization template');
610
- }
611
- }
612
- // ═══════════════════════════════════════════════════════════════
613
- // LEGAL PAGES — Terms of Service + Privacy Policy (all tiers)
614
- // ═══════════════════════════════════════════════════════════════
615
- if (isWeb) {
616
- const legalPath = path_1.default.join(sharedPath, 'legal', 'web');
617
- if (await fs_extra_1.default.pathExists(legalPath)) {
618
- await this.copyAndProcessFiles(legalPath, outputPath);
619
- logger_1.logger.debug('Copied legal page templates (terms, privacy)');
620
- }
621
- }
622
- // ═══════════════════════════════════════════════════════════════
623
- // ERROR PAGES — 404, error boundary, global error (all tiers)
624
- // ═══════════════════════════════════════════════════════════════
625
- if (isWeb) {
626
- const errorPagesPath = path_1.default.join(sharedPath, 'error-pages', 'web');
627
- if (await fs_extra_1.default.pathExists(errorPagesPath)) {
628
- await this.copyAndProcessFiles(errorPagesPath, outputPath);
629
- logger_1.logger.debug('Copied error page templates (not-found, error, global-error)');
630
- }
631
- }
632
- // ═══════════════════════════════════════════════════════════════
633
- // SEO — sitemap.ts + robots.ts (all tiers)
634
- // ═══════════════════════════════════════════════════════════════
635
- if (isWeb) {
636
- const seoPath = path_1.default.join(sharedPath, 'seo', 'web');
637
- if (await fs_extra_1.default.pathExists(seoPath)) {
638
- await this.copyAndProcessFiles(seoPath, outputPath);
639
- logger_1.logger.debug('Copied SEO templates (sitemap, robots)');
640
- }
641
- }
642
- // ═══════════════════════════════════════════════════════════════
643
- // UTILS — common utility functions + API response helpers (all tiers)
644
- // ═══════════════════════════════════════════════════════════════
645
- if (isWeb) {
646
- const utilsPath = path_1.default.join(sharedPath, 'utils', 'web');
647
- if (await fs_extra_1.default.pathExists(utilsPath)) {
648
- await this.copyAndProcessFiles(utilsPath, outputPath);
649
- logger_1.logger.debug('Copied utility templates (utils, api-response)');
650
- }
651
- }
652
- // ═══════════════════════════════════════════════════════════════
653
- // COOKIE CONSENT — GDPR compliance banner (all tiers)
654
- // ═══════════════════════════════════════════════════════════════
655
- if (isWeb) {
656
- const cookiePath = path_1.default.join(sharedPath, 'cookie-consent', 'web');
657
- if (await fs_extra_1.default.pathExists(cookiePath)) {
658
- await this.copyAndProcessFiles(cookiePath, outputPath);
659
- logger_1.logger.debug('Copied cookie consent component template');
660
- }
661
- }
662
- // ═══════════════════════════════════════════════════════════════
663
- // OBSERVABILITY — audit + error reporting helpers (all tiers)
664
- // ═══════════════════════════════════════════════════════════════
665
- if (isWeb) {
666
- const obsPath = path_1.default.join(sharedPath, 'observability', 'web');
667
- if (await fs_extra_1.default.pathExists(obsPath)) {
668
- await this.copyAndProcessFiles(obsPath, outputPath);
669
- logger_1.logger.debug('Copied observability template');
670
- }
671
- }
672
- // ═══════════════════════════════════════════════════════════════
673
- // CACHE — TTL presets + HTTP cache headers (all tiers)
674
- // ═══════════════════════════════════════════════════════════════
675
- if (isWeb) {
676
- const cachePath = path_1.default.join(sharedPath, 'cache', 'web');
677
- if (await fs_extra_1.default.pathExists(cachePath)) {
678
- await this.copyAndProcessFiles(cachePath, outputPath);
679
- logger_1.logger.debug('Copied cache utility template');
680
- }
681
- }
682
- // ═══════════════════════════════════════════════════════════════
683
- // LOADING — skeleton components + default loading.tsx (all tiers)
684
- // ═══════════════════════════════════════════════════════════════
685
- if (isWeb) {
686
- const loadingPath = path_1.default.join(sharedPath, 'loading', 'web');
687
- if (await fs_extra_1.default.pathExists(loadingPath)) {
688
- await this.copyAndProcessFiles(loadingPath, outputPath);
689
- logger_1.logger.debug('Copied loading skeleton templates');
690
- }
691
- }
692
- // ═══════════════════════════════════════════════════════════════
693
- // ADMIN — layout + nav component (keycloak auth only)
694
- // ═══════════════════════════════════════════════════════════════
695
- if (isWeb && this.config.auth === 'keycloak') {
696
- const adminPath = path_1.default.join(sharedPath, 'admin', 'web');
697
- if (await fs_extra_1.default.pathExists(adminPath)) {
698
- await this.copyAndProcessFiles(adminPath, outputPath);
699
- logger_1.logger.debug('Copied admin layout + nav templates');
700
- }
701
- }
702
- // ═══════════════════════════════════════════════════════════════
703
- // CONTACT FORM — page + API route with Zod validation & email (all authenticated apps)
704
- // ═══════════════════════════════════════════════════════════════
705
- if (isWeb && features.auth) {
706
- const contactPath = path_1.default.join(sharedPath, 'contact', 'web');
707
- if (await fs_extra_1.default.pathExists(contactPath)) {
708
- await this.copyAndProcessFiles(contactPath, outputPath);
709
- logger_1.logger.debug('Copied contact form templates (page + API route)');
710
- }
711
- }
712
- // ═══════════════════════════════════════════════════════════════
713
- // .env.example — dynamic env var reference
714
- // ═══════════════════════════════════════════════════════════════
715
- if (isWeb) {
716
- const envExample = this.generateEnvExample(features);
717
- await fs_extra_1.default.writeFile(path_1.default.join(outputPath, '.env.example'), envExample);
718
- logger_1.logger.debug('Generated .env.example');
719
- }
720
- }
721
- /**
722
- * Patch the generated package.json to merge aligned dependency versions.
723
- * Base templates may have stale versions — this ensures the output
724
- * always matches production-aligned versions from getDependencies().
725
- */
726
- async patchPackageJson(outputPath) {
727
- const pkgPath = path_1.default.join(outputPath, 'package.json');
728
- if (!(await fs_extra_1.default.pathExists(pkgPath)))
729
- return;
730
- const pkg = await fs_extra_1.default.readJson(pkgPath);
731
- const deps = this.context.dependencies;
732
- const devDeps = this.context.devDependencies;
733
- // Merge dependencies — our versions take precedence over template defaults
734
- pkg.dependencies = { ...(pkg.dependencies || {}), ...deps };
735
- pkg.devDependencies = { ...(pkg.devDependencies || {}), ...devDeps };
736
- // Clean up any obsolete/duplicate deps that moved between deps and devDeps
737
- // (e.g., typescript might be in both)
738
- for (const key of Object.keys(pkg.devDependencies)) {
739
- if (key === 'typescript')
740
- continue; // typescript can be in both
741
- if (pkg.dependencies[key]) {
742
- delete pkg.devDependencies[key];
743
- }
744
- }
745
- await fs_extra_1.default.writeJson(pkgPath, pkg, { spaces: 2 });
746
- logger_1.logger.debug('Patched package.json with aligned dependency versions');
747
- }
748
- /**
749
- * Generate .env.example dynamically based on auth, database, and tier selections.
750
- */
751
- generateEnvExample(features) {
752
- const lines = [
753
- '# ═══════════════════════════════════════════════════════════════',
754
- `# ${this.config.name} — Environment Variables`,
755
- '# Copy this file to .env.local and fill in the values.',
756
- '# ═══════════════════════════════════════════════════════════════',
757
- '',
758
- '# ── App ──────────────────────────────────────────────────────',
759
- 'NODE_ENV=development',
760
- 'NEXT_PUBLIC_APP_URL=http://localhost:3000',
761
- '',
762
- ];
763
- // Auth
764
- if (this.config.auth === 'keycloak') {
765
- lines.push('# ── Auth (Keycloak + Auth.js) ─────────────────────────────────', 'AUTH_SECRET= # openssl rand -base64 32', 'KEYCLOAK_ISSUER=https://auth.example.com/realms/my-realm', 'KEYCLOAK_CLIENT_ID=my-app', 'KEYCLOAK_CLIENT_SECRET=', '');
766
- }
767
- else if (this.config.auth === 'supabase') {
768
- lines.push('# ── Auth (Supabase) ───────────────────────────────────────────', 'NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co', 'NEXT_PUBLIC_SUPABASE_ANON_KEY=', 'SUPABASE_SERVICE_ROLE_KEY=', '');
769
- }
770
- else if (this.config.auth === 'firebase') {
771
- lines.push('# ── Auth (Firebase) ───────────────────────────────────────────', 'NEXT_PUBLIC_FIREBASE_API_KEY=', 'NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=', 'NEXT_PUBLIC_FIREBASE_PROJECT_ID=', '');
772
- }
773
- // Database
774
- if (this.config.database === 'postgresql') {
775
- lines.push('# ── Database (PostgreSQL) ─────────────────────────────────────', 'DATABASE_URL=postgres://user:password@localhost:5432/myapp', '');
776
- }
777
- else if (this.config.database === 'supabase') {
778
- lines.push('# ── Database (Supabase) ───────────────────────────────────────', 'NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co', 'NEXT_PUBLIC_SUPABASE_ANON_KEY=', 'SUPABASE_SERVICE_ROLE_KEY=', '');
779
- }
780
- // Redis (starter+)
781
- if (features.payments || features.ai || features.observability) {
782
- lines.push('# ── Cache (Redis) ─────────────────────────────────────────────', 'REDIS_URL=redis://localhost:6379', 'REDIS_KEY_PREFIX=myapp: # Isolate keys per app', '');
783
- }
784
- // Payments (starter+)
785
- if (features.payments) {
786
- lines.push('# ── Payments (Stripe) ─────────────────────────────────────────', 'STRIPE_PUBLISHABLE_KEY=pk_test_...', 'STRIPE_SECRET_KEY=sk_test_...', 'STRIPE_WEBHOOK_SECRET=whsec_...', '');
787
- }
788
- // Email (all authenticated apps)
789
- if (features.auth) {
790
- lines.push('# ── Email (Resend) ────────────────────────────────────────────', 'RESEND_API_KEY=re_...', 'EMAIL_FROM=noreply@example.com', 'ADMIN_EMAIL=admin@example.com # Contact form submissions', '');
791
- }
792
- // AI (pro+)
793
- if (features.ai) {
794
- lines.push('# ── AI ────────────────────────────────────────────────────────', 'OPENAI_API_KEY=sk-...', '# ANTHROPIC_API_KEY=sk-ant-...', '');
795
- }
796
- // Admin / Cron secrets
797
- lines.push('# ── Security ──────────────────────────────────────────────────', 'ADMIN_SECRET= # openssl rand -base64 32', 'CRON_SECRET= # openssl rand -base64 32', '');
798
- // Beta (if enabled)
799
- if (features.beta) {
800
- lines.push('# ── Beta Access ───────────────────────────────────────────────', 'BETA_ENABLED=true', 'BETA_CODES=CODE1,CODE2 # Comma-separated invite codes', '');
801
- }
802
- return lines.join('\n');
803
- }
804
- protectJSXObjectLiterals(content, jsxObjectLiterals) {
805
- let result = '';
806
- let i = 0;
807
- while (i < content.length) {
808
- if (content.substr(i, 2) === '{{') {
809
- // Found potential JSX object literal
810
- let braceCount = 0;
811
- const start = i;
812
- let j = i;
813
- // Count braces to find the complete object literal
814
- while (j < content.length) {
815
- if (content[j] === '{') {
816
- braceCount++;
817
- }
818
- else if (content[j] === '}') {
819
- braceCount--;
820
- if (braceCount === 0) {
821
- break;
822
- }
823
- }
824
- j++;
825
- }
826
- if (braceCount === 0 && j < content.length) {
827
- // Found complete {{ ... }} block
828
- const fullMatch = content.substring(start, j + 1);
829
- const innerContent = content.substring(start + 2, j - 1);
830
- // Check if this looks like a JSX object literal (not a mustache variable)
831
- if (this.isJSXObjectLiteral(innerContent)) {
832
- jsxObjectLiterals.push(fullMatch);
833
- result += `__JSX_OBJECT_${jsxObjectLiterals.length - 1}__`;
834
- i = j + 1;
835
- }
836
- else {
837
- // Keep mustache variable as-is
838
- result += content[i];
839
- i++;
840
- }
841
- }
842
- else {
843
- // Incomplete braces, just add the character
844
- result += content[i];
845
- i++;
846
- }
847
- }
848
- else {
849
- result += content[i];
850
- i++;
851
- }
852
- }
853
- return result;
854
- }
855
- isJSXObjectLiteral(content) {
856
- // JSX object literals typically have:
857
- // - Property assignments with colons
858
- // - Commas separating properties
859
- // - Whitespace/newlines
860
- // - Array literals with square brackets
861
- // - Function calls
862
- // Simple heuristics to distinguish JSX from mustache variables
863
- return (content.includes(':') || // Property assignments
864
- content.includes(',') || // Multiple properties
865
- content.includes('[') || // Arrays
866
- content.includes('(') || // Function calls
867
- content.match(/\s+/) !== null || // Whitespace
868
- content.includes('\\n') || // Newlines
869
- content.length > 20 // Complex expressions
870
- );
871
- }
872
- isTextFile(filename) {
873
- const textExtensions = [
874
- '.js',
875
- '.jsx',
876
- '.ts',
877
- '.tsx',
878
- '.json',
879
- '.md',
880
- '.txt',
881
- '.yml',
882
- '.yaml',
883
- '.xml',
884
- '.html',
885
- '.css',
886
- '.scss',
887
- '.sass',
888
- '.less',
889
- '.mjs',
890
- '.cjs',
891
- '.env',
892
- '.gitignore',
893
- '.eslintrc',
894
- '.prettierrc',
895
- '.editorconfig',
896
- '.nvmrc',
897
- // Infrastructure files (Terraform, Kubernetes)
898
- '.tf',
899
- '.tfvars',
900
- '.hcl',
901
- ];
902
- const ext = path_1.default.extname(filename).toLowerCase();
903
- return textExtensions.includes(ext) || !ext;
904
- }
905
- toPascalCase(str) {
906
- return str
907
- .replace(/[^a-zA-Z0-9]/g, ' ')
908
- .split(' ')
909
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
910
- .join('');
911
- }
912
- toCamelCase(str) {
913
- const pascal = this.toPascalCase(str);
914
- return pascal.charAt(0).toLowerCase() + pascal.slice(1);
915
- }
916
- toTitleCase(str) {
917
- return str
918
- .replace(/[^a-zA-Z0-9]/g, ' ')
919
- .split(' ')
920
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
921
- .join(' ');
922
- }
923
- async createMobileAssets(outputPath) {
924
- const assetsDir = path_1.default.join(outputPath, 'assets');
925
- await fs_extra_1.default.ensureDir(assetsDir);
926
- // Create a simple placeholder image (1x1 pixel transparent PNG)
927
- const placeholderImage = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==', 'base64');
928
- // Create all required asset files
929
- const assetFiles = ['icon.png', 'splash.png', 'adaptive-icon.png', 'favicon.png'];
930
- for (const assetFile of assetFiles) {
931
- await fs_extra_1.default.writeFile(path_1.default.join(assetsDir, assetFile), placeholderImage);
932
- }
933
- logger_1.logger.debug(`Created assets directory with ${assetFiles.length} placeholder files`);
934
- }
935
- }
936
- exports.TemplateGenerator = TemplateGenerator;
937
- //# sourceMappingURL=template-generator.js.map