@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.
- package/README.md +476 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +39 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/figma.d.ts +16 -0
- package/dist/commands/figma.d.ts.map +1 -0
- package/dist/commands/figma.js +172 -0
- package/dist/commands/figma.js.map +1 -0
- package/dist/commands/index.d.ts +5 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +5 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +1471 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/swagger.d.ts +16 -0
- package/dist/commands/swagger.d.ts.map +1 -0
- package/dist/commands/swagger.js +404 -0
- package/dist/commands/swagger.js.map +1 -0
- package/dist/commands/update.d.ts +15 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +93 -0
- package/dist/commands/update.js.map +1 -0
- package/package.json +63 -0
- package/templates/.vscode/extensions.json +9 -0
- package/templates/.vscode/launch.json +26 -0
- package/templates/.vscode/settings.json +67 -0
- package/templates/.vscode/tasks.json +21 -0
- package/templates/boilerplate/config/fonts.ts +10 -0
- package/templates/boilerplate/config/navigationUrls.ts +47 -0
- package/templates/boilerplate/config/site.ts +96 -0
- package/templates/boilerplate/libs/I18n.ts +15 -0
- package/templates/boilerplate/libs/I18nNavigation.ts +5 -0
- package/templates/boilerplate/libs/I18nRouting.ts +9 -0
- package/templates/boilerplate/libs/env.ts +21 -0
- package/templates/boilerplate/libs/react-query/ReactQueryProvider.tsx +21 -0
- package/templates/boilerplate/libs/react-query/index.ts +2 -0
- package/templates/boilerplate/libs/react-query/queryClient.ts +62 -0
- package/templates/boilerplate/libs/react-query/queryKeys.ts +5 -0
- package/templates/boilerplate/public/images/index.ts +1 -0
- package/templates/boilerplate/reset.d.ts +2 -0
- package/templates/boilerplate/styles/globals.css +308 -0
- package/templates/boilerplate/types/i18n.ts +10 -0
- package/templates/boilerplate/types/locale.ts +8 -0
- package/templates/boilerplate/utils/file/fileConfig.ts +123 -0
- package/templates/boilerplate/utils/file/fileValidation.ts +78 -0
- package/templates/boilerplate/utils/file/imageCompression.ts +182 -0
- package/templates/boilerplate/utils/file/index.ts +3 -0
- package/templates/boilerplate/utils/helpers.ts +55 -0
- package/templates/boilerplate/validations/auth.validation.ts +92 -0
- package/templates/boilerplate/validations/commonValidations.ts +258 -0
- package/templates/boilerplate/validations/zodErrorMap.ts +101 -0
- package/templates/configs/.env.example +8 -0
- package/templates/configs/.prettierignore +23 -0
- package/templates/configs/.prettierrc.cjs +26 -0
- package/templates/configs/.prettierrc.icons.cjs +11 -0
- package/templates/configs/Dockerfile +6 -0
- package/templates/configs/commitlint.config.ts +8 -0
- package/templates/configs/eslint.config.mjs +119 -0
- package/templates/configs/knip.config.ts +32 -0
- package/templates/configs/lefthook.yml +42 -0
- package/templates/configs/lint-staged.config.js +8 -0
- package/templates/configs/next.config.template.ts +77 -0
- package/templates/configs/next.config.ts +43 -0
- package/templates/configs/package.json +75 -0
- package/templates/configs/postcss.config.mjs +15 -0
- package/templates/configs/svgr.config.mjs +129 -0
- package/templates/configs/tsconfig.json +75 -0
- package/templates/docs/AI_QUICK_REFERENCE.md +379 -0
- package/templates/docs/ARCHITECTURE_PATTERNS.md +927 -0
- package/templates/docs/DOCUMENTATION_INDEX.md +411 -0
- package/templates/docs/FIGMA_TO_CODE_GUIDE.md +768 -0
- package/templates/docs/IMPLEMENTATION_GUIDE.md +892 -0
- package/templates/docs/PROJECT_OVERVIEW.md +302 -0
- package/templates/docs/REFACTOR_PROGRESS.md +1113 -0
- package/templates/docs/SHADCN_TO_HEROUI_MIGRATION.md +1375 -0
- 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
|
+
© {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
|