@adlas/create-app 1.0.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 (79) hide show
  1. package/README.md +476 -0
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +39 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/figma.d.ts +16 -0
  7. package/dist/commands/figma.d.ts.map +1 -0
  8. package/dist/commands/figma.js +172 -0
  9. package/dist/commands/figma.js.map +1 -0
  10. package/dist/commands/index.d.ts +5 -0
  11. package/dist/commands/index.d.ts.map +1 -0
  12. package/dist/commands/index.js +5 -0
  13. package/dist/commands/index.js.map +1 -0
  14. package/dist/commands/init.d.ts +8 -0
  15. package/dist/commands/init.d.ts.map +1 -0
  16. package/dist/commands/init.js +1471 -0
  17. package/dist/commands/init.js.map +1 -0
  18. package/dist/commands/swagger.d.ts +16 -0
  19. package/dist/commands/swagger.d.ts.map +1 -0
  20. package/dist/commands/swagger.js +404 -0
  21. package/dist/commands/swagger.js.map +1 -0
  22. package/dist/commands/update.d.ts +15 -0
  23. package/dist/commands/update.d.ts.map +1 -0
  24. package/dist/commands/update.js +93 -0
  25. package/dist/commands/update.js.map +1 -0
  26. package/package.json +63 -0
  27. package/templates/.vscode/extensions.json +9 -0
  28. package/templates/.vscode/launch.json +26 -0
  29. package/templates/.vscode/settings.json +67 -0
  30. package/templates/.vscode/tasks.json +21 -0
  31. package/templates/boilerplate/config/fonts.ts +10 -0
  32. package/templates/boilerplate/config/navigationUrls.ts +47 -0
  33. package/templates/boilerplate/config/site.ts +96 -0
  34. package/templates/boilerplate/libs/I18n.ts +15 -0
  35. package/templates/boilerplate/libs/I18nNavigation.ts +5 -0
  36. package/templates/boilerplate/libs/I18nRouting.ts +9 -0
  37. package/templates/boilerplate/libs/env.ts +21 -0
  38. package/templates/boilerplate/libs/react-query/ReactQueryProvider.tsx +21 -0
  39. package/templates/boilerplate/libs/react-query/index.ts +2 -0
  40. package/templates/boilerplate/libs/react-query/queryClient.ts +62 -0
  41. package/templates/boilerplate/libs/react-query/queryKeys.ts +5 -0
  42. package/templates/boilerplate/public/images/index.ts +1 -0
  43. package/templates/boilerplate/reset.d.ts +2 -0
  44. package/templates/boilerplate/styles/globals.css +308 -0
  45. package/templates/boilerplate/types/i18n.ts +10 -0
  46. package/templates/boilerplate/types/locale.ts +8 -0
  47. package/templates/boilerplate/utils/file/fileConfig.ts +123 -0
  48. package/templates/boilerplate/utils/file/fileValidation.ts +78 -0
  49. package/templates/boilerplate/utils/file/imageCompression.ts +182 -0
  50. package/templates/boilerplate/utils/file/index.ts +3 -0
  51. package/templates/boilerplate/utils/helpers.ts +55 -0
  52. package/templates/boilerplate/validations/auth.validation.ts +92 -0
  53. package/templates/boilerplate/validations/commonValidations.ts +258 -0
  54. package/templates/boilerplate/validations/zodErrorMap.ts +101 -0
  55. package/templates/configs/.env.example +8 -0
  56. package/templates/configs/.prettierignore +23 -0
  57. package/templates/configs/.prettierrc.cjs +26 -0
  58. package/templates/configs/.prettierrc.icons.cjs +11 -0
  59. package/templates/configs/Dockerfile +6 -0
  60. package/templates/configs/commitlint.config.ts +8 -0
  61. package/templates/configs/eslint.config.mjs +119 -0
  62. package/templates/configs/knip.config.ts +32 -0
  63. package/templates/configs/lefthook.yml +42 -0
  64. package/templates/configs/lint-staged.config.js +8 -0
  65. package/templates/configs/next.config.template.ts +77 -0
  66. package/templates/configs/next.config.ts +43 -0
  67. package/templates/configs/package.json +75 -0
  68. package/templates/configs/postcss.config.mjs +15 -0
  69. package/templates/configs/svgr.config.mjs +129 -0
  70. package/templates/configs/tsconfig.json +75 -0
  71. package/templates/docs/AI_QUICK_REFERENCE.md +379 -0
  72. package/templates/docs/ARCHITECTURE_PATTERNS.md +927 -0
  73. package/templates/docs/DOCUMENTATION_INDEX.md +411 -0
  74. package/templates/docs/FIGMA_TO_CODE_GUIDE.md +768 -0
  75. package/templates/docs/IMPLEMENTATION_GUIDE.md +892 -0
  76. package/templates/docs/PROJECT_OVERVIEW.md +302 -0
  77. package/templates/docs/REFACTOR_PROGRESS.md +1113 -0
  78. package/templates/docs/SHADCN_TO_HEROUI_MIGRATION.md +1375 -0
  79. package/templates/docs/UI_COMPONENTS_GUIDE.md +893 -0
@@ -0,0 +1,1471 @@
1
+ import prompts from 'prompts';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { execa } from 'execa';
5
+ import fs from 'fs-extra';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+ import { dirname } from 'path';
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+ export async function initCommand(projectName, options) {
12
+ console.log(chalk.blue.bold('\n🚀 Adlas Project Initializer\n'));
13
+ // Step 1: Get project configuration
14
+ const config = await getProjectConfig(projectName, options);
15
+ if (!config) {
16
+ console.log(chalk.yellow('\n👋 Cancelled\n'));
17
+ return;
18
+ }
19
+ const spinner = ora();
20
+ try {
21
+ // Step 2: Create Next.js project
22
+ spinner.start('Creating Next.js project...');
23
+ await createNextProject(config.projectName);
24
+ spinner.succeed('Next.js project created');
25
+ // Change to project directory
26
+ process.chdir(config.projectName);
27
+ // Remove default Next.js files that we'll replace
28
+ await cleanupDefaultAppFiles();
29
+ // Step 3: Install dependencies
30
+ if (!options.skipInstall) {
31
+ spinner.start('Installing HeroUI and dependencies...');
32
+ await installDependencies(config);
33
+ spinner.succeed('Dependencies installed');
34
+ }
35
+ // Step 4: Copy configuration files
36
+ spinner.start('Copying configuration files...');
37
+ await copyConfigFiles(config);
38
+ spinner.succeed('Configuration files copied');
39
+ // Step 5: Generate documentation
40
+ spinner.start('Generating project documentation...');
41
+ await generateDocumentation(config);
42
+ spinner.succeed('Documentation generated');
43
+ // Step 6: Setup project structure
44
+ spinner.start('Setting up project structure...');
45
+ await setupProjectStructure(config);
46
+ spinner.succeed('Project structure created');
47
+ // Step 7: Copy boilerplate code
48
+ spinner.start('Copying boilerplate code...');
49
+ await copyBoilerplate(config);
50
+ spinner.succeed('Boilerplate code copied');
51
+ // Step 7.5: Create type declarations for packages without types
52
+ if (config.pwa) {
53
+ spinner.start('Creating type declarations...');
54
+ await createTypeDeclarations();
55
+ spinner.succeed('Type declarations created');
56
+ }
57
+ // Step 8: Initialize git
58
+ if (!options.skipGit) {
59
+ spinner.start('Initializing git repository...');
60
+ await initGit(config);
61
+ spinner.succeed('Git initialized');
62
+ }
63
+ // Success message
64
+ console.log(chalk.green.bold('\n✨ Project created successfully!\n'));
65
+ // Next steps
66
+ printNextSteps(config);
67
+ }
68
+ catch (error) {
69
+ spinner.fail('Failed to create project');
70
+ console.error(chalk.red('\nError:'), error.message);
71
+ process.exit(1);
72
+ }
73
+ }
74
+ async function getProjectConfig(projectName, options) {
75
+ const questions = [];
76
+ if (!projectName) {
77
+ questions.push({
78
+ type: 'text',
79
+ name: 'projectName',
80
+ message: 'Project name:',
81
+ initial: 'my-adlas-app',
82
+ validate: (value) => {
83
+ if (!value)
84
+ return 'Project name is required';
85
+ if (!/^[a-z0-9-]+$/.test(value)) {
86
+ return 'Project name must contain only lowercase letters, numbers, and hyphens';
87
+ }
88
+ return true;
89
+ }
90
+ });
91
+ }
92
+ questions.push({
93
+ type: 'select',
94
+ name: 'projectType',
95
+ message: 'Select project type:',
96
+ choices: [
97
+ { title: 'CMS', value: 'cms', description: 'Content Management System (no authentication)' },
98
+ { title: 'Dashboard', value: 'dashboard', description: 'Admin dashboard with authentication' },
99
+ { title: 'E-commerce', value: 'ecommerce', description: 'Full e-commerce with user accounts' }
100
+ ],
101
+ initial: 0
102
+ }, {
103
+ type: (prev) => prev !== 'cms' ? 'confirm' : null,
104
+ name: 'hasAuth',
105
+ message: 'Include Authentication?',
106
+ initial: true
107
+ }, {
108
+ type: 'multiselect',
109
+ name: 'languages',
110
+ message: 'Select languages (minimum 2):',
111
+ choices: [
112
+ { title: 'English', value: 'en', selected: true },
113
+ { title: 'German', value: 'de', selected: true },
114
+ { title: 'Persian (Farsi)', value: 'fa', selected: false }
115
+ ],
116
+ min: 2,
117
+ validate: (value) => {
118
+ if (value.length < 2)
119
+ return 'Please select at least 2 languages';
120
+ return true;
121
+ }
122
+ }, {
123
+ type: 'confirm',
124
+ name: 'ssr',
125
+ message: 'Is this a Server-Side Rendering (SSR) project?',
126
+ initial: true
127
+ }, {
128
+ type: 'confirm',
129
+ name: 'figmaIntegration',
130
+ message: 'Do you have Figma designs to implement?',
131
+ initial: false
132
+ }, {
133
+ type: (prev) => prev ? 'text' : null,
134
+ name: 'figmaUrl',
135
+ message: 'Figma file URL:',
136
+ validate: (value) => {
137
+ if (!value)
138
+ return 'Figma URL is required';
139
+ if (!value.includes('figma.com'))
140
+ return 'Invalid Figma URL';
141
+ return true;
142
+ }
143
+ }, {
144
+ type: 'confirm',
145
+ name: 'swaggerIntegration',
146
+ message: 'Do you have Swagger/OpenAPI spec for API?',
147
+ initial: false
148
+ }, {
149
+ type: (prev) => prev ? 'text' : null,
150
+ name: 'swaggerUrl',
151
+ message: 'Swagger/OpenAPI spec URL:',
152
+ validate: (value) => {
153
+ if (!value)
154
+ return 'Swagger URL is required';
155
+ return true;
156
+ }
157
+ });
158
+ const answers = await prompts(questions);
159
+ if (Object.keys(answers).length === 0) {
160
+ return null;
161
+ }
162
+ // Build features array - i18n is always enabled
163
+ const features = ['i18n'];
164
+ if (answers.hasAuth) {
165
+ features.push('auth');
166
+ }
167
+ return {
168
+ projectName: projectName || answers.projectName,
169
+ projectType: options.projectType || answers.projectType,
170
+ features,
171
+ languages: answers.languages || ['en', 'de'],
172
+ ssr: answers.ssr !== undefined ? answers.ssr : true,
173
+ pwa: true, // Always enabled
174
+ figmaIntegration: answers.figmaIntegration || false,
175
+ figmaUrl: answers.figmaUrl || null,
176
+ swaggerIntegration: answers.swaggerIntegration || false,
177
+ swaggerUrl: answers.swaggerUrl || null
178
+ };
179
+ }
180
+ async function createNextProject(projectName) {
181
+ // Create Next.js project with full automation (no prompts!)
182
+ console.log(chalk.gray(`\n Creating Next.js project...\n`));
183
+ // Use create-next-app with --yes flag to skip all prompts
184
+ await execa('pnpm', [
185
+ 'create',
186
+ 'next-app@latest',
187
+ projectName,
188
+ '--typescript',
189
+ '--tailwind',
190
+ '--eslint',
191
+ '--app',
192
+ '--src-dir',
193
+ '--import-alias=@/*',
194
+ '--use-pnpm',
195
+ '--yes' // This skips all prompts!
196
+ ], {
197
+ stdio: 'inherit',
198
+ cwd: process.cwd()
199
+ });
200
+ }
201
+ async function cleanupDefaultAppFiles() {
202
+ const appDir = path.join(process.cwd(), 'src/app');
203
+ // Files to remove from default Next.js setup
204
+ const filesToRemove = [
205
+ 'favicon.ico',
206
+ 'globals.css',
207
+ 'page.tsx',
208
+ 'layout.tsx',
209
+ ];
210
+ for (const file of filesToRemove) {
211
+ const filePath = path.join(appDir, file);
212
+ if (await fs.pathExists(filePath)) {
213
+ await fs.remove(filePath);
214
+ }
215
+ }
216
+ }
217
+ async function installDependencies(config) {
218
+ // Install all required dependencies including HeroUI
219
+ const dependencies = [
220
+ '@heroui/react',
221
+ 'framer-motion',
222
+ 'zustand',
223
+ '@tanstack/react-query',
224
+ 'zod'
225
+ ];
226
+ // i18n is always enabled
227
+ dependencies.push('next-intl');
228
+ // PWA is always enabled
229
+ if (config.pwa) {
230
+ dependencies.push('next-pwa');
231
+ }
232
+ const devDependencies = [
233
+ '@svgr/webpack',
234
+ '@antfu/eslint-config',
235
+ 'eslint-plugin-jsx-a11y',
236
+ 'eslint-plugin-playwright',
237
+ 'eslint-plugin-storybook',
238
+ 'eslint-plugin-tailwindcss',
239
+ 'prettier-plugin-tailwindcss',
240
+ '@commitlint/cli',
241
+ '@commitlint/config-conventional',
242
+ 'lefthook',
243
+ 'lint-staged',
244
+ 'knip'
245
+ ];
246
+ console.log(chalk.gray(`\n Installing dependencies...\n`));
247
+ await execa('pnpm', ['add', ...dependencies], { stdio: 'inherit' });
248
+ console.log(chalk.gray(`\n Installing dev dependencies...\n`));
249
+ await execa('pnpm', ['add', '-D', ...devDependencies], { stdio: 'inherit' });
250
+ }
251
+ async function copyConfigFiles(config) {
252
+ const templateDir = path.join(__dirname, '../../templates/configs');
253
+ const configFiles = [
254
+ '.npmrc',
255
+ '.prettierignore',
256
+ '.prettierrc.cjs',
257
+ '.prettierrc.icons.cjs',
258
+ '.gitignore',
259
+ '.env.example',
260
+ 'svgr.config.mjs',
261
+ 'postcss.config.mjs',
262
+ 'prettier.config.mjs',
263
+ 'tailwind.config.ts',
264
+ 'next-env.d.ts',
265
+ 'lint-staged.config.js',
266
+ 'lefthook.yml',
267
+ 'knip.config.ts',
268
+ 'eslint.config.mjs',
269
+ 'commitlint.config.ts',
270
+ 'Dockerfile'
271
+ ];
272
+ for (const file of configFiles) {
273
+ const src = path.join(templateDir, file);
274
+ const dest = path.join(process.cwd(), file);
275
+ if (await fs.pathExists(src)) {
276
+ await fs.copy(src, dest, { overwrite: true });
277
+ }
278
+ }
279
+ // Copy .vscode folder from templates root (not configs)
280
+ const templatesRoot = path.join(__dirname, '../../templates');
281
+ const vscodeSrc = path.join(templatesRoot, '.vscode');
282
+ const vscodeDest = path.join(process.cwd(), '.vscode');
283
+ if (await fs.pathExists(vscodeSrc)) {
284
+ await fs.copy(vscodeSrc, vscodeDest, { overwrite: true });
285
+ }
286
+ // Generate next.config.ts based on project configuration
287
+ await generateNextConfig(config);
288
+ // Generate package.json based on project configuration
289
+ await generatePackageJson(config);
290
+ // Merge tsconfig.json with custom settings
291
+ await mergeTsConfig();
292
+ }
293
+ async function generateNextConfig(config) {
294
+ const hasI18n = config.features.includes('i18n');
295
+ const hasPwa = config.pwa;
296
+ const isSSR = config.ssr;
297
+ let content = `import type { NextConfig } from 'next';\n`;
298
+ if (hasI18n) {
299
+ content += `import createNextIntlPlugin from 'next-intl/plugin';\n`;
300
+ }
301
+ if (hasPwa) {
302
+ content += `import withPWA from 'next-pwa';\n`;
303
+ }
304
+ content += `
305
+ // Define the base Next.js configuration
306
+ const baseConfig: NextConfig = {
307
+ reactStrictMode: true,
308
+ poweredByHeader: false,
309
+ typescript: {
310
+ ignoreBuildErrors: false,
311
+ },
312
+ images: {
313
+ // Optimized quality: 75 provides excellent visual quality at ~50% smaller file size
314
+ // Next.js automatically generates WebP/AVIF when browser supports it
315
+ qualities: [75, 85, 100],
316
+ formats: ['image/avif', 'image/webp'],
317
+ deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
318
+ imageSizes: [16, 32, 48, 64, 96, 128, 256, 384, 512, 1024],
319
+ // Cache images for 7 days (604800 seconds) for better performance
320
+ minimumCacheTTL: 604800,
321
+ remotePatterns: [
322
+ {
323
+ protocol: 'https',
324
+ hostname: '**',
325
+ pathname: '/**',
326
+ },
327
+ ],
328
+ },
329
+ experimental: {
330
+ serverActions: {
331
+ bodySizeLimit: '10mb',
332
+ },
333
+ },
334
+ ${isSSR ? "output: 'standalone'," : "output: 'export',"}
335
+ webpack(config) {
336
+ // SVGR configuration for importing SVGs as React components
337
+ config.module.rules.push({
338
+ test: /\\.svg$/i,
339
+ issuer: /\\.[jt]sx?$/,
340
+ use: ['@svgr/webpack'],
341
+ });
342
+
343
+ return config;
344
+ },
345
+ };
346
+ `;
347
+ if (hasPwa) {
348
+ content += `
349
+ // Configure PWA
350
+ const pwaConfig = withPWA({
351
+ dest: 'public',
352
+ register: true,
353
+ skipWaiting: true,
354
+ disable: process.env.NODE_ENV === 'development',
355
+ })(baseConfig);
356
+ `;
357
+ }
358
+ if (hasI18n) {
359
+ content += `
360
+ // Initialize the Next-Intl plugin
361
+ const configWithPlugins = createNextIntlPlugin('./src/libs/I18n.ts')(${hasPwa ? 'pwaConfig' : 'baseConfig'});
362
+ `;
363
+ }
364
+ else {
365
+ content += `
366
+ const configWithPlugins = ${hasPwa ? 'pwaConfig' : 'baseConfig'};
367
+ `;
368
+ }
369
+ content += `
370
+ export default configWithPlugins;
371
+ `;
372
+ await fs.writeFile(path.join(process.cwd(), 'next.config.ts'), content);
373
+ }
374
+ async function generatePackageJson(config) {
375
+ const packageJson = {
376
+ name: config.projectName,
377
+ version: '0.1.0',
378
+ private: true,
379
+ scripts: {
380
+ dev: 'next dev --turbopack',
381
+ build: 'next build',
382
+ start: 'next start',
383
+ clean: 'rimraf .next out coverage',
384
+ lint: 'eslint .',
385
+ 'lint:fix': 'pnpm lint:spaces && eslint . --fix',
386
+ 'lint:spaces': 'find src -type f \\( -name \'*.tsx\' -o -name \'*.jsx\' \\) -exec perl -i -pe \'s/(className=(?:"|`)(?:[^"`])*) +/$1 /g\' {} +',
387
+ 'prettier:check': 'prettier --check .',
388
+ 'prettier:format': 'prettier --write .',
389
+ 'check:types': 'tsc --noEmit --pretty',
390
+ 'check:deps': 'knip',
391
+ 'check:i18n': 'i18n-check -l src/locales -s en -u src -f next-intl',
392
+ 'check:all': 'pnpm prettier:format && pnpm lint:fix && pnpm check:types && pnpm check:deps && pnpm check:i18n',
393
+ commit: 'commit',
394
+ svgr: 'rm -rf src/components/icons && svgr --config-file svgr.config.mjs --out-dir src/components/icons --no-dimensions --filename-case pascal src/assets/icons',
395
+ 'prettier:icons': 'prettier --write --config .prettierrc.icons.cjs \'src/components/icons/*.{tsx,ts}\'',
396
+ 'lint:icons': 'eslint --fix \'src/components/icons/*.{tsx,ts}\'',
397
+ 'generate-icons': 'pnpm run svgr && pnpm run prettier:icons && pnpm run lint:icons',
398
+ },
399
+ dependencies: {
400
+ '@heroui/react': '^2.8.5',
401
+ '@t3-oss/env-nextjs': '^0.13.8',
402
+ '@tanstack/react-query-devtools': '^5.90.2',
403
+ clsx: '^2.1.1',
404
+ next: '15.5.4',
405
+ 'next-pwa': '^5.6.0',
406
+ react: '19.2.0',
407
+ 'react-dom': '19.2.0',
408
+ sonner: '^2.0.7',
409
+ 'tailwind-variants': '1.0.0',
410
+ 'usehooks-ts': '^3.1.1',
411
+ zustand: '^5.0.8',
412
+ },
413
+ devDependencies: {
414
+ '@antfu/eslint-config': '^5.4.1',
415
+ '@commitlint/cli': '^20.1.0',
416
+ '@commitlint/config-conventional': '^20.0.0',
417
+ '@commitlint/prompt-cli': '^20.1.0',
418
+ '@commitlint/types': '^20.0.0',
419
+ '@eslint-react/eslint-plugin': '^1.42.1',
420
+ '@next/eslint-plugin-next': '^15.3.0',
421
+ '@svgr/babel-plugin-remove-jsx-attribute': '^8.0.0',
422
+ '@svgr/cli': '^8.1.0',
423
+ '@tailwindcss/postcss': '^4.1.17',
424
+ '@tanstack/react-query': '^5.90.7',
425
+ '@total-typescript/ts-reset': '^0.6.1',
426
+ '@types/node': '^24.10.0',
427
+ '@types/react': '^19.2.0',
428
+ autoprefixer: '10.4.22',
429
+ 'conventional-changelog-conventionalcommits': '^9.1.0',
430
+ 'dotenv-cli': '^10.0.0',
431
+ eslint: '^9.39.1',
432
+ 'eslint-plugin-format': '^1.0.2',
433
+ 'eslint-plugin-jsx-a11y': '^6.10.2',
434
+ 'eslint-plugin-playwright': '^2.2.2',
435
+ 'eslint-plugin-react-hooks': '^5.2.0',
436
+ 'eslint-plugin-react-refresh': '^0.4.19',
437
+ 'eslint-plugin-storybook': '^9.1.10',
438
+ 'eslint-plugin-tailwindcss': '^4.0.0-beta.0',
439
+ knip: '^5.68.0',
440
+ lefthook: '^1.13.6',
441
+ postcss: '^8.5.6',
442
+ prettier: '^3.6.2',
443
+ 'prettier-plugin-tailwindcss': '^0.7.1',
444
+ rimraf: '^6.1.0',
445
+ 'tailwind-variants': '3.1.1',
446
+ tailwindcss: '4.1.17',
447
+ typescript: '^5.9.3',
448
+ zod: '4.1.12',
449
+ },
450
+ config: {
451
+ commitizen: {
452
+ path: '@commitlint/cz-commitlint',
453
+ },
454
+ },
455
+ };
456
+ // i18n is always enabled
457
+ packageJson.dependencies['next-intl'] = '^4.5.0';
458
+ packageJson.devDependencies['@lingual/i18n-check'] = '^0.8.12';
459
+ await fs.writeJSON(path.join(process.cwd(), 'package.json'), packageJson, { spaces: 2 });
460
+ }
461
+ async function mergeTsConfig() {
462
+ const tsconfigPath = path.join(process.cwd(), 'tsconfig.json');
463
+ // Create the complete tsconfig with comments and proper structure
464
+ const tsconfigContent = `/* eslint-disable jsonc/sort-keys */
465
+ {
466
+ "compilerOptions": {
467
+ // ======================================================================
468
+ // Language & Environment
469
+ // Defines JavaScript version and runtime environment
470
+ // ======================================================================
471
+ "target": "ES2017",
472
+ "module": "esnext",
473
+ "lib": ["dom", "dom.iterable", "esnext"],
474
+ "moduleResolution": "bundler",
475
+ "isolatedModules": true,
476
+ // ======================================================================
477
+ // Type Safety - Foundation
478
+ // Core type checking settings for a robust codebase
479
+ // ======================================================================
480
+ "strict": true,
481
+ "alwaysStrict": true,
482
+ "strictNullChecks": true,
483
+ "noImplicitAny": true,
484
+ "noImplicitThis": true,
485
+ // ======================================================================
486
+ // Type Safety - Advanced
487
+ // Additional checks for higher code quality
488
+ // ======================================================================
489
+ "noUncheckedIndexedAccess": true,
490
+ "noImplicitReturns": true,
491
+ "noUnusedLocals": true,
492
+ "noUnusedParameters": true,
493
+ "noFallthroughCasesInSwitch": true,
494
+ "allowUnreachableCode": false,
495
+ "useUnknownInCatchVariables": true,
496
+ "noImplicitOverride": true,
497
+ // ======================================================================
498
+ // Interoperability
499
+ // Settings for working with different file types and modules
500
+ // ======================================================================
501
+ "allowJs": true,
502
+ "checkJs": false,
503
+ "esModuleInterop": true,
504
+ "resolveJsonModule": true,
505
+ // ======================================================================
506
+ // Build & Performance
507
+ // Settings that affect compilation output and build performance
508
+ // ======================================================================
509
+ "skipLibCheck": true,
510
+ "removeComments": true,
511
+ "preserveConstEnums": true,
512
+ "forceConsistentCasingInFileNames": true,
513
+ // ======================================================================
514
+ // Project Structure
515
+ // Configure import paths and module resolution
516
+ // ======================================================================
517
+ "baseUrl": ".",
518
+ "paths": {
519
+ "@/*": ["./src/*"],
520
+ "@/public/*": ["./public/*"]
521
+ },
522
+ // ======================================================================
523
+ // Next.js Project Configuration
524
+ // Controls settings specific to Next.js framework
525
+ // ======================================================================
526
+ "jsx": "preserve", // Preserve JSX for Next.js transformation
527
+ "incremental": true, // Enable faster incremental builds
528
+ "noEmit": true, // Skip emitting files (Next.js handles this)
529
+ "plugins": [
530
+ {
531
+ "name": "next"
532
+ }
533
+ ] // Enable Next.js TypeScript plugin
534
+ },
535
+ // Files to include/exclude from the project
536
+ "exclude": ["node_modules"],
537
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "**/*.mts"]
538
+ }
539
+ `;
540
+ // Write the complete tsconfig.json with comments
541
+ await fs.writeFile(tsconfigPath, tsconfigContent, 'utf-8');
542
+ }
543
+ async function createTypeDeclarations() {
544
+ // Create type declaration for next-pwa
545
+ const nextPwaTypesPath = path.join(process.cwd(), 'src', 'types', 'next-pwa.d.ts');
546
+ await fs.ensureDir(path.dirname(nextPwaTypesPath));
547
+ const nextPwaTypes = `declare module 'next-pwa' {
548
+ import { NextConfig } from 'next';
549
+
550
+ type PWAConfig = {
551
+ dest?: string;
552
+ register?: boolean;
553
+ skipWaiting?: boolean;
554
+ disable?: boolean;
555
+ sw?: string;
556
+ publicExcludes?: string[];
557
+ buildExcludes?: string[];
558
+ };
559
+
560
+ function withPWA(pwaConfig: PWAConfig): (nextConfig: NextConfig) => NextConfig;
561
+
562
+ export default withPWA;
563
+ }
564
+ `;
565
+ await fs.writeFile(nextPwaTypesPath, nextPwaTypes, 'utf-8');
566
+ }
567
+ async function generateDocumentation(config) {
568
+ const docsDir = path.join(process.cwd(), 'docs');
569
+ await fs.ensureDir(docsDir);
570
+ // Generate project-specific documentation
571
+ const docs = {
572
+ 'PROJECT_OVERVIEW.md': generateProjectOverview(config),
573
+ 'ARCHITECTURE_PATTERNS.md': await getTemplateDoc('ARCHITECTURE_PATTERNS.md'),
574
+ 'AI_QUICK_REFERENCE.md': await getTemplateDoc('AI_QUICK_REFERENCE.md'),
575
+ 'UI_COMPONENTS_GUIDE.md': await getTemplateDoc('UI_COMPONENTS_GUIDE.md'),
576
+ 'IMPLEMENTATION_GUIDE.md': await getTemplateDoc('IMPLEMENTATION_GUIDE.md'),
577
+ 'DOCUMENTATION_INDEX.md': generateDocumentationIndex(config)
578
+ };
579
+ for (const [filename, content] of Object.entries(docs)) {
580
+ await fs.writeFile(path.join(docsDir, filename), content);
581
+ }
582
+ }
583
+ function generateProjectOverview(config) {
584
+ return `# ${config.projectName} - Project Overview
585
+
586
+ **Generated:** ${new Date().toISOString().split('T')[0]}
587
+ **Project Type:** ${config.projectType}
588
+ **Features:** ${config.features.join(', ')}
589
+ **Languages:** ${config.languages.join(', ')}
590
+ **Rendering:** ${config.ssr ? 'Server-Side Rendering (SSR)' : 'Static Site Generation (SSG)'}
591
+ **PWA:** Enabled
592
+
593
+ ## Tech Stack
594
+
595
+ - **Framework:** Next.js 15 with App Router
596
+ - **Language:** TypeScript
597
+ - **Styling:** Tailwind CSS
598
+ - **UI Library:** HeroUI
599
+ - **State Management:** Zustand
600
+ - **Data Fetching:** React Query (TanStack Query)
601
+ - **Form Handling:** Zod
602
+ - **Icons:** Lucide React
603
+ - **Internationalization:** next-intl (${config.languages.join(', ')})
604
+ ${config.features.includes('auth') ? '- **Authentication:** Custom implementation\n' : ''}- **PWA:** next-pwa
605
+ - **Rendering Mode:** ${config.ssr ? 'SSR (Server-Side Rendering)' : 'SSG (Static Site Generation)'}
606
+
607
+ ## Project Structure
608
+
609
+ \`\`\`
610
+ ${config.projectName}/
611
+ ├── src/
612
+ │ ├── app/ # Next.js App Router pages
613
+ ${config.features.includes('auth') ? '│ │ ├── (auth)/ # Authentication routes\n│ │ │ ├── login/\n│ │ │ ├── register/\n│ │ │ └── layout.tsx\n' : ''}│ ├── components/
614
+ │ │ ├── ui/ # Shared UI components
615
+ │ │ └── features/ # Feature-specific components
616
+ │ ├── services/ # Service modules (data layer)
617
+ │ ├── hooks/ # Custom React hooks
618
+ │ ├── lib/ # Utilities
619
+ │ ├── types/ # TypeScript types
620
+ │ └── locales/ # Translation JSON files (${config.languages.map(l => `${l}.json`).join(', ')})
621
+ ├── public/ # Static assets
622
+ ├── docs/ # Documentation
623
+ └── scripts/ # Build/deployment scripts
624
+ \`\`\`
625
+
626
+ ## Getting Started
627
+
628
+ 1. Install dependencies:
629
+ \`\`\`bash
630
+ pnpm install
631
+ \`\`\`
632
+
633
+ 2. Run development server:
634
+ \`\`\`bash
635
+ pnpm dev
636
+ \`\`\`
637
+
638
+ 3. Open [http://localhost:3000](http://localhost:3000)
639
+
640
+ ## Documentation
641
+
642
+ - [Architecture Patterns](./ARCHITECTURE_PATTERNS.md) - Code structure and patterns
643
+ - [UI Components Guide](./UI_COMPONENTS_GUIDE.md) - HeroUI component usage
644
+ - [Implementation Guide](./IMPLEMENTATION_GUIDE.md) - Step-by-step workflows
645
+
646
+ ${config.figmaIntegration ? `## Figma Integration
647
+
648
+ Figma File: ${config.figmaUrl}
649
+
650
+ To generate components from Figma:
651
+ \`\`\`bash
652
+ adlas figma ${config.figmaUrl} --output src/components/features
653
+ \`\`\`
654
+ ` : ''}
655
+
656
+ ${config.swaggerIntegration ? `## API Integration
657
+
658
+ Swagger Spec: ${config.swaggerUrl}
659
+
660
+ To generate API layer:
661
+ \`\`\`bash
662
+ adlas swagger ${config.swaggerUrl} --output src/services
663
+ \`\`\`
664
+ ` : ''}
665
+
666
+ ## Next Steps
667
+
668
+ ${config.figmaIntegration ? '1. Run Figma integration to generate UI components\n' : ''}
669
+ ${config.swaggerIntegration ? '2. Run Swagger integration to generate API layer\n' : ''}
670
+ ${!config.figmaIntegration && !config.swaggerIntegration ? '1. Start building your features\n' : ''}
671
+ 2. Review the documentation in docs/ folder
672
+ 3. Follow the architecture patterns
673
+ 4. Build amazing things! 🚀
674
+ `;
675
+ }
676
+ function generateDocumentationIndex(config) {
677
+ return `# Documentation Index
678
+
679
+ Welcome to the ${config.projectName} documentation!
680
+
681
+ ## 📚 Core Documentation
682
+
683
+ 1. **[Project Overview](./PROJECT_OVERVIEW.md)**
684
+ - Tech stack
685
+ - Project structure
686
+ - Getting started
687
+
688
+ 2. **[Architecture Patterns](./ARCHITECTURE_PATTERNS.md)**
689
+ - Service module pattern
690
+ - Component structure
691
+ - State management
692
+ - Best practices
693
+
694
+ 3. **[UI Components Guide](./UI_COMPONENTS_GUIDE.md)**
695
+ - HeroUI components
696
+ - Form components
697
+ - Common patterns
698
+
699
+ 4. **[Implementation Guide](./IMPLEMENTATION_GUIDE.md)**
700
+ - Step-by-step workflows
701
+ - Feature implementation
702
+ - Testing strategies
703
+
704
+ 5. **[AI Quick Reference](./AI_QUICK_REFERENCE.md)**
705
+ - DO/DON'T rules
706
+ - Quick tips for working with AI
707
+
708
+ ## 🎨 Design & Development
709
+
710
+ ${config.figmaIntegration ? `### Figma Integration
711
+
712
+ Your Figma file: ${config.figmaUrl}
713
+
714
+ Generate components:
715
+ \`\`\`bash
716
+ adlas figma ${config.figmaUrl}
717
+ \`\`\`
718
+ ` : ''}
719
+
720
+ ${config.swaggerIntegration ? `### API Integration
721
+
722
+ Your Swagger spec: ${config.swaggerUrl}
723
+
724
+ Generate API layer:
725
+ \`\`\`bash
726
+ adlas swagger ${config.swaggerUrl}
727
+ \`\`\`
728
+ ` : ''}
729
+
730
+ ## 🔧 Configuration
731
+
732
+ All configuration files are set up and ready:
733
+ - \`.npmrc\` - Package manager settings
734
+ - \`prettier.config.mjs\` - Code formatting
735
+ - \`tailwind.config.ts\` - Tailwind + HeroUI setup
736
+ - \`tsconfig.json\` - TypeScript configuration
737
+
738
+ ## 📖 How to Use This Documentation
739
+
740
+ 1. **Starting a new feature?** → Read Implementation Guide
741
+ 2. **Need a component?** → Check UI Components Guide
742
+ 3. **Structuring code?** → Follow Architecture Patterns
743
+ 4. **Working with AI?** → Use AI Quick Reference
744
+
745
+ ---
746
+
747
+ **Last Updated:** ${new Date().toISOString().split('T')[0]}
748
+ `;
749
+ }
750
+ async function getTemplateDoc(filename) {
751
+ const templatePath = path.join(__dirname, '../../templates/docs', filename);
752
+ if (await fs.pathExists(templatePath)) {
753
+ return await fs.readFile(templatePath, 'utf-8');
754
+ }
755
+ return `# ${filename}\n\nTemplate documentation will be added here.`;
756
+ }
757
+ async function setupProjectStructure(config) {
758
+ const hasI18n = config.features.includes('i18n');
759
+ const hasAuth = config.features.includes('auth');
760
+ const dirs = [
761
+ 'src/services',
762
+ 'src/components/ui',
763
+ 'src/components/icons',
764
+ 'src/components/layout',
765
+ 'src/hooks',
766
+ 'src/libs',
767
+ 'src/types',
768
+ 'src/assets/icons',
769
+ 'public/icons',
770
+ 'public/images',
771
+ ];
772
+ // Create app folder structure based on i18n and project type
773
+ const appBase = hasI18n ? 'src/app/[locale]' : 'src/app';
774
+ if (hasI18n) {
775
+ dirs.push('src/app/[locale]');
776
+ dirs.push('src/locales');
777
+ }
778
+ else {
779
+ dirs.push('src/app');
780
+ }
781
+ // Create project-specific folder structure
782
+ if (config.projectType === 'ecommerce') {
783
+ // Website routes (public pages)
784
+ dirs.push(`${appBase}/(website)`);
785
+ dirs.push(`${appBase}/(website)/(home)`);
786
+ dirs.push(`${appBase}/(website)/products`);
787
+ dirs.push(`${appBase}/(website)/categories`);
788
+ dirs.push(`${appBase}/(website)/cart`);
789
+ dirs.push(`${appBase}/(website)/about`);
790
+ dirs.push(`${appBase}/(website)/contact`);
791
+ // Dashboard routes (user profile area)
792
+ dirs.push(`${appBase}/(dashboard)/profile`);
793
+ dirs.push(`${appBase}/(dashboard)/profile/orders`);
794
+ dirs.push(`${appBase}/(dashboard)/profile/wishlist`);
795
+ dirs.push(`${appBase}/(dashboard)/profile/settings`);
796
+ }
797
+ else if (config.projectType === 'dashboard') {
798
+ // Dashboard routes
799
+ dirs.push(`${appBase}/(dashboard)`);
800
+ dirs.push(`${appBase}/(dashboard)/profile`);
801
+ dirs.push(`${appBase}/(dashboard)/settings`);
802
+ }
803
+ else if (config.projectType === 'cms') {
804
+ // CMS routes
805
+ dirs.push(`${appBase}/(website)`);
806
+ dirs.push(`${appBase}/(website)/(home)`);
807
+ dirs.push(`${appBase}/(website)/news`);
808
+ dirs.push(`${appBase}/(website)/about`);
809
+ dirs.push(`${appBase}/(website)/contact`);
810
+ }
811
+ // Create auth folders based on project type
812
+ if (hasAuth) {
813
+ const authBase = `${appBase}/(auth)`;
814
+ // Common auth pages for dashboard and ecommerce
815
+ dirs.push(`${authBase}/login`);
816
+ dirs.push(`${authBase}/forget-password`);
817
+ dirs.push(`${authBase}/reset-password`);
818
+ // Register only for ecommerce
819
+ if (config.projectType === 'ecommerce') {
820
+ dirs.push(`${authBase}/register`);
821
+ }
822
+ }
823
+ for (const dir of dirs) {
824
+ await fs.ensureDir(path.join(process.cwd(), dir));
825
+ }
826
+ // Create locale JSON files for selected languages
827
+ if (hasI18n && config.languages) {
828
+ const localesDir = path.join(process.cwd(), 'src/locales');
829
+ const emptyLocale = {};
830
+ for (const lang of config.languages) {
831
+ const localeFile = path.join(localesDir, `${lang}.json`);
832
+ await fs.writeJSON(localeFile, emptyLocale, { spaces: 2 });
833
+ }
834
+ }
835
+ // Create app files based on i18n
836
+ await createAppFiles(config, hasI18n, hasAuth);
837
+ }
838
+ async function createAppFiles(config, hasI18n, hasAuth) {
839
+ if (hasI18n) {
840
+ // Root layout.tsx (minimal, just passes children)
841
+ const rootLayoutContent = `import '@/styles/globals.css';
842
+
843
+ import type { ReactNode } from 'react';
844
+
845
+ export default function RootLayout({
846
+ children,
847
+ }: Readonly<{
848
+ children: ReactNode;
849
+ }>) {
850
+ return children;
851
+ }
852
+ `;
853
+ await fs.writeFile(path.join(process.cwd(), 'src/app/layout.tsx'), rootLayoutContent);
854
+ // [locale]/layout.tsx
855
+ const localeLayoutContent = `import type { Metadata, Viewport } from 'next';
856
+ import type { ReactNode } from 'react';
857
+
858
+ import { hasLocale, NextIntlClientProvider } from 'next-intl';
859
+ import { setRequestLocale } from 'next-intl/server';
860
+ import { notFound } from 'next/navigation';
861
+
862
+ import { fontVariables } from '@/config/fonts';
863
+ import { siteMetadata, siteViewport } from '@/config/site';
864
+ import { routing } from '@/libs/I18nRouting';
865
+
866
+ import { Providers } from '../providers';
867
+
868
+ export const viewport: Viewport = siteViewport;
869
+ export const metadata: Metadata = siteMetadata;
870
+
871
+ export function generateStaticParams() {
872
+ return routing.locales.map((locale: string) => ({ locale }));
873
+ }
874
+
875
+ type LocaleLayoutProps = {
876
+ children: ReactNode;
877
+ params: Promise<{ locale: string }>;
878
+ };
879
+
880
+ export default async function LocaleLayout(props: LocaleLayoutProps) {
881
+ const { locale } = await props.params;
882
+
883
+ if (!hasLocale(routing.locales, locale)) {
884
+ notFound();
885
+ }
886
+
887
+ setRequestLocale(locale);
888
+
889
+ return (
890
+ <html suppressHydrationWarning lang={locale} dir="ltr">
891
+ <body suppressHydrationWarning className={\`\${fontVariables} antialiased\`}>
892
+ <NextIntlClientProvider locale={locale}>
893
+ <Providers>
894
+ {props.children}
895
+ </Providers>
896
+ </NextIntlClientProvider>
897
+ </body>
898
+ </html>
899
+ );
900
+ }
901
+ `;
902
+ await fs.writeFile(path.join(process.cwd(), 'src/app/[locale]/layout.tsx'), localeLayoutContent);
903
+ // [locale]/page.tsx - only for dashboard (ecommerce and cms use (website)/(home))
904
+ if (config.projectType === 'dashboard') {
905
+ const localePageContent = `export default function HomePage() {
906
+ return (
907
+ <main className="flex min-h-screen flex-col items-center justify-center p-24">
908
+ <h1 className="text-4xl font-bold">Welcome to ${config.projectName}</h1>
909
+ </main>
910
+ );
911
+ }
912
+ `;
913
+ await fs.writeFile(path.join(process.cwd(), 'src/app/[locale]/page.tsx'), localePageContent);
914
+ }
915
+ }
916
+ else {
917
+ // Simple layout without i18n
918
+ const layoutContent = `import '@/styles/globals.css';
919
+
920
+ import type { Metadata, Viewport } from 'next';
921
+ import type { ReactNode } from 'react';
922
+
923
+ import { fontVariables } from '@/config/fonts';
924
+ import { siteMetadata, siteViewport } from '@/config/site';
925
+
926
+ import { Providers } from './providers';
927
+
928
+ export const viewport: Viewport = siteViewport;
929
+ export const metadata: Metadata = siteMetadata;
930
+
931
+ export default function RootLayout({
932
+ children,
933
+ }: Readonly<{
934
+ children: ReactNode;
935
+ }>) {
936
+ return (
937
+ <html suppressHydrationWarning lang="en" dir="ltr">
938
+ <body suppressHydrationWarning className={\`\${fontVariables} antialiased\`}>
939
+ <Providers>
940
+ {children}
941
+ </Providers>
942
+ </body>
943
+ </html>
944
+ );
945
+ }
946
+ `;
947
+ await fs.writeFile(path.join(process.cwd(), 'src/app/layout.tsx'), layoutContent);
948
+ // Simple page.tsx - only for dashboard (ecommerce and cms use (website)/(home))
949
+ if (config.projectType === 'dashboard') {
950
+ const pageContent = `export default function HomePage() {
951
+ return (
952
+ <main className="flex min-h-screen flex-col items-center justify-center p-24">
953
+ <h1 className="text-4xl font-bold">Welcome to ${config.projectName}</h1>
954
+ </main>
955
+ );
956
+ }
957
+ `;
958
+ await fs.writeFile(path.join(process.cwd(), 'src/app/page.tsx'), pageContent);
959
+ }
960
+ }
961
+ // Create providers.tsx
962
+ const providersContent = `'use client';
963
+
964
+ import type { ReactNode } from 'react';
965
+
966
+ import { HeroUIProvider } from '@heroui/react';
967
+ import { QueryClientProvider } from '@tanstack/react-query';
968
+ import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
969
+ import { ThemeProvider as NextThemesProvider } from 'next-themes';
970
+ import { useRouter } from 'next/navigation';
971
+
972
+ import { queryClient } from '@/libs/react-query';
973
+
974
+ declare module '@react-types/shared' {
975
+ // eslint-disable-next-line ts/consistent-type-definitions -- Required for module augmentation
976
+ interface RouterConfig {
977
+ routerOptions: NonNullable<Parameters<ReturnType<typeof useRouter>['push']>[1]>;
978
+ }
979
+ }
980
+
981
+ type ProvidersProps = {
982
+ children: ReactNode;
983
+ };
984
+
985
+ export function Providers({ children }: ProvidersProps) {
986
+ const router = useRouter();
987
+
988
+ return (
989
+ <QueryClientProvider client={queryClient}>
990
+ <HeroUIProvider navigate={router.push}>
991
+ <NextThemesProvider
992
+ attribute="class"
993
+ defaultTheme="light"
994
+ enableSystem
995
+ disableTransitionOnChange={false}
996
+ >
997
+ {children}
998
+ </NextThemesProvider>
999
+ </HeroUIProvider>
1000
+ <ReactQueryDevtools initialIsOpen={false} />
1001
+ </QueryClientProvider>
1002
+ );
1003
+ }
1004
+ `;
1005
+ const providersPath = path.join(process.cwd(), 'src/app/providers.tsx');
1006
+ await fs.writeFile(providersPath, providersContent);
1007
+ // Create auth pages if auth is enabled
1008
+ if (hasAuth) {
1009
+ await createAuthPages(config, hasI18n);
1010
+ }
1011
+ // Create project-specific layouts and pages
1012
+ await createProjectTypeFiles(config, hasI18n);
1013
+ }
1014
+ async function createProjectTypeFiles(config, hasI18n) {
1015
+ const appBase = hasI18n
1016
+ ? path.join(process.cwd(), 'src/app/[locale]')
1017
+ : path.join(process.cwd(), 'src/app');
1018
+ if (config.projectType === 'ecommerce') {
1019
+ // Website layout
1020
+ const websiteLayoutContent = `import type { ReactNode } from 'react';
1021
+
1022
+ import { Footer, Navbar } from '@/components/layout';
1023
+
1024
+ export default function WebsiteLayout({
1025
+ children,
1026
+ }: {
1027
+ children: ReactNode;
1028
+ }) {
1029
+ return (
1030
+ <div className="flex min-h-screen flex-col">
1031
+ <Navbar />
1032
+ <main className="flex-1">{children}</main>
1033
+ <Footer />
1034
+ </div>
1035
+ );
1036
+ }
1037
+ `;
1038
+ await fs.writeFile(path.join(appBase, '(website)/layout.tsx'), websiteLayoutContent);
1039
+ // Home page
1040
+ const homePageContent = `export default function HomePage() {
1041
+ return (
1042
+ <main className="flex min-h-screen flex-col items-center justify-center p-24">
1043
+ <h1 className="text-4xl font-bold">Welcome to ${config.projectName}</h1>
1044
+ </main>
1045
+ );
1046
+ }
1047
+ `;
1048
+ await fs.writeFile(path.join(appBase, '(website)/(home)/page.tsx'), homePageContent);
1049
+ // Dashboard layout
1050
+ const dashboardLayoutContent = `import type { ReactNode } from 'react';
1051
+
1052
+ export default function DashboardLayout({
1053
+ children,
1054
+ }: {
1055
+ children: ReactNode;
1056
+ }) {
1057
+ return (
1058
+ <div className="flex min-h-screen">
1059
+ {/* Add Sidebar component here */}
1060
+ <aside className="w-64 bg-default-100 p-4">
1061
+ <nav>
1062
+ {/* Add navigation links here */}
1063
+ </nav>
1064
+ </aside>
1065
+ <main className="flex-1 p-6">{children}</main>
1066
+ </div>
1067
+ );
1068
+ }
1069
+ `;
1070
+ await fs.writeFile(path.join(appBase, '(dashboard)/layout.tsx'), dashboardLayoutContent);
1071
+ // Profile page
1072
+ const profilePageContent = `export default function ProfilePage() {
1073
+ return (
1074
+ <div>
1075
+ <h1 className="text-2xl font-bold">Profile</h1>
1076
+ {/* Add profile content here */}
1077
+ </div>
1078
+ );
1079
+ }
1080
+ `;
1081
+ await fs.writeFile(path.join(appBase, '(dashboard)/profile/page.tsx'), profilePageContent);
1082
+ }
1083
+ else if (config.projectType === 'dashboard') {
1084
+ // Dashboard layout
1085
+ const dashboardLayoutContent = `import type { ReactNode } from 'react';
1086
+
1087
+ export default function DashboardLayout({
1088
+ children,
1089
+ }: {
1090
+ children: ReactNode;
1091
+ }) {
1092
+ return (
1093
+ <div className="flex min-h-screen">
1094
+ {/* Add Sidebar component here */}
1095
+ <aside className="w-64 bg-default-100 p-4">
1096
+ <nav>
1097
+ {/* Add navigation links here */}
1098
+ </nav>
1099
+ </aside>
1100
+ <main className="flex-1 p-6">{children}</main>
1101
+ </div>
1102
+ );
1103
+ }
1104
+ `;
1105
+ await fs.writeFile(path.join(appBase, '(dashboard)/layout.tsx'), dashboardLayoutContent);
1106
+ // Dashboard home page
1107
+ const dashboardHomeContent = `export default function DashboardPage() {
1108
+ return (
1109
+ <div>
1110
+ <h1 className="text-2xl font-bold">Dashboard</h1>
1111
+ {/* Add dashboard content here */}
1112
+ </div>
1113
+ );
1114
+ }
1115
+ `;
1116
+ await fs.writeFile(path.join(appBase, '(dashboard)/page.tsx'), dashboardHomeContent);
1117
+ }
1118
+ else if (config.projectType === 'cms') {
1119
+ // Website layout
1120
+ const websiteLayoutContent = `import type { ReactNode } from 'react';
1121
+
1122
+ import { Footer, Navbar } from '@/components/layout';
1123
+
1124
+ export default function WebsiteLayout({
1125
+ children,
1126
+ }: {
1127
+ children: ReactNode;
1128
+ }) {
1129
+ return (
1130
+ <div className="flex min-h-screen flex-col">
1131
+ <Navbar />
1132
+ <main className="flex-1">{children}</main>
1133
+ <Footer />
1134
+ </div>
1135
+ );
1136
+ }
1137
+ `;
1138
+ await fs.writeFile(path.join(appBase, '(website)/layout.tsx'), websiteLayoutContent);
1139
+ // Home page
1140
+ const homePageContent = `export default function HomePage() {
1141
+ return (
1142
+ <main className="flex min-h-screen flex-col items-center justify-center p-24">
1143
+ <h1 className="text-4xl font-bold">Welcome to ${config.projectName}</h1>
1144
+ </main>
1145
+ );
1146
+ }
1147
+ `;
1148
+ await fs.writeFile(path.join(appBase, '(website)/(home)/page.tsx'), homePageContent);
1149
+ }
1150
+ // Create layout components (Navbar, Footer)
1151
+ await createLayoutComponents(config);
1152
+ }
1153
+ async function createLayoutComponents(config) {
1154
+ const layoutDir = path.join(process.cwd(), 'src/components/layout');
1155
+ // Navbar component
1156
+ const navbarContent = `'use client';
1157
+
1158
+ import { Link } from '@heroui/react';
1159
+
1160
+ export function Navbar() {
1161
+ return (
1162
+ <header className="sticky top-0 z-50 w-full border-b border-default-200 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
1163
+ <div className="container flex h-16 items-center justify-between">
1164
+ <Link href="/" className="font-bold text-xl">
1165
+ ${config.projectName}
1166
+ </Link>
1167
+ <nav className="flex items-center gap-6">
1168
+ {/* Add navigation links here */}
1169
+ </nav>
1170
+ </div>
1171
+ </header>
1172
+ );
1173
+ }
1174
+ `;
1175
+ await fs.writeFile(path.join(layoutDir, 'Navbar.tsx'), navbarContent);
1176
+ // Footer component
1177
+ const footerContent = `export function Footer() {
1178
+ return (
1179
+ <footer className="border-t border-default-200 py-8">
1180
+ <div className="container">
1181
+ <p className="text-center text-sm text-default-500">
1182
+ &copy; {new Date().getFullYear()} ${config.projectName}. All rights reserved.
1183
+ </p>
1184
+ </div>
1185
+ </footer>
1186
+ );
1187
+ }
1188
+ `;
1189
+ await fs.writeFile(path.join(layoutDir, 'Footer.tsx'), footerContent);
1190
+ // Index file for exports
1191
+ const indexContent = `export { Navbar } from './Navbar';
1192
+ export { Footer } from './Footer';
1193
+ `;
1194
+ await fs.writeFile(path.join(layoutDir, 'index.ts'), indexContent);
1195
+ }
1196
+ async function createAuthPages(config, hasI18n) {
1197
+ const authBase = hasI18n
1198
+ ? path.join(process.cwd(), 'src/app/[locale]/(auth)')
1199
+ : path.join(process.cwd(), 'src/app/(auth)');
1200
+ // Auth layout
1201
+ const authLayoutContent = `export default function AuthLayout({
1202
+ children,
1203
+ }: {
1204
+ children: React.ReactNode;
1205
+ }) {
1206
+ return (
1207
+ <div className="min-h-screen flex items-center justify-center bg-default-50">
1208
+ {children}
1209
+ </div>
1210
+ );
1211
+ }
1212
+ `;
1213
+ await fs.writeFile(path.join(authBase, 'layout.tsx'), authLayoutContent);
1214
+ // Login page
1215
+ const loginPageContent = `'use client';
1216
+
1217
+ export default function LoginPage() {
1218
+ return (
1219
+ <div className="w-full max-w-md p-8 space-y-6 bg-white rounded-lg shadow-md">
1220
+ <h1 className="text-2xl font-bold text-center">Login</h1>
1221
+ {/* Add your login form here */}
1222
+ </div>
1223
+ );
1224
+ }
1225
+ `;
1226
+ await fs.writeFile(path.join(authBase, 'login/page.tsx'), loginPageContent);
1227
+ // Forget password page
1228
+ const forgetPasswordContent = `'use client';
1229
+
1230
+ export default function ForgetPasswordPage() {
1231
+ return (
1232
+ <div className="w-full max-w-md p-8 space-y-6 bg-white rounded-lg shadow-md">
1233
+ <h1 className="text-2xl font-bold text-center">Forget Password</h1>
1234
+ {/* Add your forget password form here */}
1235
+ </div>
1236
+ );
1237
+ }
1238
+ `;
1239
+ await fs.writeFile(path.join(authBase, 'forget-password/page.tsx'), forgetPasswordContent);
1240
+ // Reset password page
1241
+ const resetPasswordContent = `'use client';
1242
+
1243
+ export default function ResetPasswordPage() {
1244
+ return (
1245
+ <div className="w-full max-w-md p-8 space-y-6 bg-white rounded-lg shadow-md">
1246
+ <h1 className="text-2xl font-bold text-center">Reset Password</h1>
1247
+ {/* Add your reset password form here */}
1248
+ </div>
1249
+ );
1250
+ }
1251
+ `;
1252
+ await fs.writeFile(path.join(authBase, 'reset-password/page.tsx'), resetPasswordContent);
1253
+ // Register page (only for ecommerce)
1254
+ if (config.projectType === 'ecommerce') {
1255
+ const registerPageContent = `'use client';
1256
+
1257
+ export default function RegisterPage() {
1258
+ return (
1259
+ <div className="w-full max-w-md p-8 space-y-6 bg-white rounded-lg shadow-md">
1260
+ <h1 className="text-2xl font-bold text-center">Register</h1>
1261
+ {/* Add your registration form here */}
1262
+ </div>
1263
+ );
1264
+ }
1265
+ `;
1266
+ await fs.writeFile(path.join(authBase, 'register/page.tsx'), registerPageContent);
1267
+ }
1268
+ }
1269
+ async function generateNavigationUrls(config, configDest) {
1270
+ const hasAuth = config.features.includes('auth');
1271
+ let content = `export const NAVIGATION_URLS = {
1272
+ // Public routes
1273
+ ROOT: '/',
1274
+ `;
1275
+ // Add auth routes based on project type
1276
+ if (hasAuth) {
1277
+ if (config.projectType === 'dashboard') {
1278
+ content += ` // Auth routes
1279
+ AUTH: {
1280
+ INDEX: '/auth',
1281
+ LOGIN: '/auth/login',
1282
+ FORGET_PASSWORD: '/auth/forget-password',
1283
+ RESET_PASSWORD: '/auth/reset-password',
1284
+ },
1285
+ `;
1286
+ }
1287
+ else if (config.projectType === 'ecommerce') {
1288
+ content += ` // Auth routes
1289
+ AUTH: {
1290
+ INDEX: '/auth',
1291
+ LOGIN: '/auth/login',
1292
+ REGISTER: '/auth/register',
1293
+ FORGET_PASSWORD: '/auth/forget-password',
1294
+ RESET_PASSWORD: '/auth/reset-password',
1295
+ },
1296
+ `;
1297
+ }
1298
+ }
1299
+ // Add project-specific routes
1300
+ if (config.projectType === 'cms') {
1301
+ content += ` // Content routes
1302
+ NEWS: {
1303
+ INDEX: '/news',
1304
+ DETAIL: (slug: string) => \`/news/\${slug}\`,
1305
+ },
1306
+ // Static pages
1307
+ ABOUT: '/about',
1308
+ CONTACT: '/contact',
1309
+ `;
1310
+ }
1311
+ else if (config.projectType === 'dashboard') {
1312
+ content += ` // Dashboard routes
1313
+ DASHBOARD: {
1314
+ INDEX: '/dashboard',
1315
+ PROFILE: '/dashboard/profile',
1316
+ SETTINGS: '/dashboard/settings',
1317
+ },
1318
+ `;
1319
+ }
1320
+ else if (config.projectType === 'ecommerce') {
1321
+ content += ` // User profile routes
1322
+ PROFILE: {
1323
+ INDEX: '/profile',
1324
+ ORDERS: '/profile/orders',
1325
+ WISHLIST: '/profile/wishlist',
1326
+ SETTINGS: '/profile/settings',
1327
+ },
1328
+ // Product routes
1329
+ PRODUCTS: {
1330
+ INDEX: '/products',
1331
+ DETAIL: (id: string) => \`/products/\${id}\`,
1332
+ },
1333
+ CATEGORIES: {
1334
+ INDEX: '/categories',
1335
+ DETAIL: (slug: string) => \`/categories/\${slug}\`,
1336
+ },
1337
+ // Cart & Checkout
1338
+ CART: '/cart',
1339
+ CHECKOUT: '/checkout',
1340
+ // Static pages
1341
+ ABOUT: '/about',
1342
+ CONTACT: '/contact',
1343
+ `;
1344
+ }
1345
+ content += `};
1346
+ `;
1347
+ await fs.writeFile(path.join(configDest, 'navigationUrls.ts'), content);
1348
+ }
1349
+ async function copyBoilerplate(config) {
1350
+ const templateDir = path.join(__dirname, '../../templates/boilerplate');
1351
+ // Copy utility files
1352
+ const utilsSrc = path.join(templateDir, 'utils');
1353
+ const utilsDest = path.join(process.cwd(), 'src/utils');
1354
+ if (await fs.pathExists(utilsSrc)) {
1355
+ await fs.copy(utilsSrc, utilsDest);
1356
+ }
1357
+ // Copy providers
1358
+ const providersSrc = path.join(templateDir, 'providers.tsx');
1359
+ const providersDest = path.join(process.cwd(), 'src/providers.tsx');
1360
+ if (await fs.pathExists(providersSrc)) {
1361
+ await fs.copy(providersSrc, providersDest);
1362
+ }
1363
+ // Copy reset.d.ts
1364
+ const resetSrc = path.join(templateDir, 'reset.d.ts');
1365
+ const resetDest = path.join(process.cwd(), 'src/reset.d.ts');
1366
+ if (await fs.pathExists(resetSrc)) {
1367
+ await fs.copy(resetSrc, resetDest);
1368
+ }
1369
+ // Copy project type specific files
1370
+ const projectTypeDir = path.join(templateDir, 'project-types', config.projectType);
1371
+ if (await fs.pathExists(projectTypeDir)) {
1372
+ await fs.copy(projectTypeDir, path.join(process.cwd(), 'src'));
1373
+ }
1374
+ // Copy public/images folder
1375
+ const publicImagesSrc = path.join(templateDir, 'public/images');
1376
+ const publicImagesDest = path.join(process.cwd(), 'public/images');
1377
+ if (await fs.pathExists(publicImagesSrc)) {
1378
+ await fs.copy(publicImagesSrc, publicImagesDest);
1379
+ }
1380
+ // Copy validations folder (only specific files)
1381
+ const validationsSrc = path.join(templateDir, 'validations');
1382
+ const validationsDest = path.join(process.cwd(), 'src/validations');
1383
+ if (await fs.pathExists(validationsSrc)) {
1384
+ await fs.ensureDir(validationsDest);
1385
+ // Always copy these files
1386
+ const validationFiles = ['commonValidations.ts', 'zodErrorMap.ts'];
1387
+ // Add auth.validation.ts if auth is enabled
1388
+ if (config.features.includes('auth')) {
1389
+ validationFiles.push('auth.validation.ts');
1390
+ }
1391
+ for (const file of validationFiles) {
1392
+ const src = path.join(validationsSrc, file);
1393
+ const dest = path.join(validationsDest, file);
1394
+ if (await fs.pathExists(src)) {
1395
+ await fs.copy(src, dest);
1396
+ }
1397
+ }
1398
+ }
1399
+ // Copy styles folder
1400
+ const stylesSrc = path.join(templateDir, 'styles');
1401
+ const stylesDest = path.join(process.cwd(), 'src/styles');
1402
+ if (await fs.pathExists(stylesSrc)) {
1403
+ await fs.copy(stylesSrc, stylesDest);
1404
+ }
1405
+ // Copy config folder
1406
+ const configSrc = path.join(templateDir, 'config');
1407
+ const configDest = path.join(process.cwd(), 'src/config');
1408
+ if (await fs.pathExists(configSrc)) {
1409
+ await fs.copy(configSrc, configDest);
1410
+ }
1411
+ // Generate navigationUrls.ts based on project type
1412
+ await generateNavigationUrls(config, configDest);
1413
+ // Copy libs folder (always copy react-query and env.ts)
1414
+ const libsSrc = path.join(templateDir, 'libs');
1415
+ const libsDest = path.join(process.cwd(), 'src/libs');
1416
+ if (await fs.pathExists(libsSrc)) {
1417
+ // Copy react-query folder
1418
+ const reactQuerySrc = path.join(libsSrc, 'react-query');
1419
+ if (await fs.pathExists(reactQuerySrc)) {
1420
+ await fs.ensureDir(libsDest);
1421
+ await fs.copy(reactQuerySrc, path.join(libsDest, 'react-query'));
1422
+ }
1423
+ // Copy env.ts
1424
+ const envSrc = path.join(libsSrc, 'env.ts');
1425
+ if (await fs.pathExists(envSrc)) {
1426
+ await fs.ensureDir(libsDest);
1427
+ await fs.copy(envSrc, path.join(libsDest, 'env.ts'));
1428
+ }
1429
+ // i18n is always enabled, copy i18n files
1430
+ const i18nFiles = ['I18n.ts', 'I18nNavigation.ts', 'I18nRouting.ts'];
1431
+ for (const file of i18nFiles) {
1432
+ const i18nSrc = path.join(libsSrc, file);
1433
+ if (await fs.pathExists(i18nSrc)) {
1434
+ await fs.copy(i18nSrc, path.join(libsDest, file));
1435
+ }
1436
+ }
1437
+ }
1438
+ // Copy types folder (i18n is always enabled)
1439
+ const typesSrc = path.join(templateDir, 'types');
1440
+ const typesDest = path.join(process.cwd(), 'src/types');
1441
+ if (await fs.pathExists(typesSrc)) {
1442
+ await fs.copy(typesSrc, typesDest);
1443
+ }
1444
+ }
1445
+ async function initGit(config) {
1446
+ await execa('git', ['add', '.'], { stdio: 'pipe' });
1447
+ await execa('git', ['commit', '-m', `chore: Initialize ${config.projectName} with Adlas CLI
1448
+
1449
+ Project Type: ${config.projectType}
1450
+ Features: ${config.features.join(', ')}
1451
+
1452
+ Generated with @adlas/create-app`], { stdio: 'pipe' });
1453
+ }
1454
+ function printNextSteps(config) {
1455
+ console.log(chalk.blue('Next steps:\n'));
1456
+ console.log(chalk.gray(' 1. Navigate to project:'));
1457
+ console.log(chalk.white(` cd ${config.projectName}\n`));
1458
+ console.log(chalk.gray(' 2. Start development server:'));
1459
+ console.log(chalk.white(' pnpm dev\n'));
1460
+ if (config.figmaIntegration) {
1461
+ console.log(chalk.gray(' 3. Generate UI from Figma:'));
1462
+ console.log(chalk.white(` adlas figma ${config.figmaUrl}\n`));
1463
+ }
1464
+ if (config.swaggerIntegration) {
1465
+ console.log(chalk.gray(` ${config.figmaIntegration ? '4' : '3'}. Generate API layer from Swagger:`));
1466
+ console.log(chalk.white(` adlas swagger ${config.swaggerUrl}\n`));
1467
+ }
1468
+ console.log(chalk.gray(' Documentation:'));
1469
+ console.log(chalk.white(' docs/DOCUMENTATION_INDEX.md\n'));
1470
+ }
1471
+ //# sourceMappingURL=init.js.map