#!/usr/bin/env node import { execSync } from 'child_process'; import fs from 'fs-extra'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const c = { reset: '\x1b[0m', dim: '\x1b[2m', green: '\x1b[32m', cyan: '\x1b[36m', red: '\x1b[31m', bold: '\x1b[1m', }; const step = (msg) => process.stdout.write(` ${c.dim}◆${c.reset} ${msg}...\n`); const done = (msg) => process.stdout.write(` ${c.green}✓${c.reset} ${msg}\n`); const fail = (msg) => process.stdout.write(` ${c.red}✗${c.reset} ${msg}\n`); // Templates zen (inline pour éviter des dépendances externes) const zenTemplates = { instrumentation: `export async function register() { if (process.env.NEXT_RUNTIME === 'nodejs') { const { initializeZen } = await import('@zen/core'); await initializeZen(); } } `, authRedirect: `import { redirect } from 'next/navigation'; export default function Redirect() { redirect('/auth/login/'); } `, authCatchAll: `export { default } from '@zen/core/features/auth/page'; `, adminRedirect: `import { redirect } from 'next/navigation'; export default function Redirect() { redirect('/admin/dashboard'); } `, adminCatchAll: `export { default } from '@zen/core/features/admin/page'; `, zenApiRoute: `export { GET, POST, PUT, DELETE, PATCH } from '@zen/core/zen/api'; `, zenPageRoute: `export { default, generateMetadata } from '@zen/core/modules/page'; `, }; // Fichiers zen à créer dans le projet cible const zenFiles = [ { path: 'instrumentation.js', template: 'instrumentation' }, { path: 'app/(auth)/auth/page.js', template: 'authRedirect' }, { path: 'app/(auth)/auth/[...auth]/page.js', template: 'authCatchAll' }, { path: 'app/(admin)/admin/page.js', template: 'adminRedirect' }, { path: 'app/(admin)/admin/[...admin]/page.js', template: 'adminCatchAll' }, { path: 'app/zen/api/[...path]/route.js', template: 'zenApiRoute' }, { path: 'app/zen/[...zen]/page.js', template: 'zenPageRoute' }, ]; async function createZenFile(filePath, content) { const fullPath = path.resolve(process.cwd(), filePath); await fs.ensureDir(path.dirname(fullPath)); await fs.writeFile(fullPath, content, 'utf-8'); } async function initProject() { try { console.log(`\n ${c.bold}${c.cyan}zen-start${c.reset}\n`); step('Creating Next.js project'); execSync('npx create-next-app@latest ./ --javascript --tailwind --app --empty', { stdio: 'inherit' }); done('Next.js project created'); if (fs.existsSync('./app')) { fs.removeSync('./app'); } done('Cleaned default /app directory'); fs.writeJsonSync('./jsconfig.json', { compilerOptions: { paths: { '@styles/*': ['./styles/*'], '@components/*': ['./components/*'] } }, }, { spaces: 2 }); done('Configured path aliases'); fs.writeFileSync('./next.config.mjs', `/** @type {import('next').NextConfig} */ const nextConfig = { \tdevIndicators: false, }; export default nextConfig; `); done('Configured next.config.mjs'); const packageJsonPath = './package.json'; if (fs.existsSync(packageJsonPath)) { const packageJson = fs.readJsonSync(packageJsonPath); const projectName = packageJson.name || 'project'; fs.writeFileSync('README.md', `# ${projectName}\n\nBuilt with Next.js, Tailwind CSS and @zen/core.`); packageJson.scripts ??= {}; packageJson.scripts['make-favicon'] = 'node dev/icons/make-favicon.js'; fs.writeJsonSync(packageJsonPath, packageJson, { spaces: 2 }); } done('Updated README and package.json'); const templatesPath = path.join(__dirname, '..', 'templates'); if (!fs.existsSync(templatesPath)) { fail('Templates directory not found in zen-start package'); process.exit(1); } fs.copySync(templatesPath, './', { overwrite: true }); done('Copied template files'); for (const file of zenFiles) { await createZenFile(file.path, zenTemplates[file.template]); } done('Created @zen/core route files'); fs.writeFileSync('./.npmrc', '@zen:registry=https://git.hyko.cx/api/packages/zen/npm/\n'); done('Configured npm registry'); step('Installing @zen/core'); execSync('npm install @zen/core', { stdio: 'inherit' }); done('@zen/core installed'); const envExample = path.resolve(process.cwd(), 'node_modules/@zen/core/.env.example'); const envDest = path.resolve(process.cwd(), '.env'); if (fs.existsSync(envExample) && !fs.existsSync(envDest)) { fs.copyFileSync(envExample, envDest); done('Created .env from @zen/core template'); } console.log(`\n ${c.green}${c.bold}Project ready.${c.reset} Next steps:\n`); console.log(` ${c.dim}1.${c.reset} Fill in the environment variables in ${c.cyan}.env${c.reset}`); console.log(` ${c.dim}2.${c.reset} Initialize the database`); console.log(` ${c.dim}npx zen-db init${c.reset}\n`); } catch (error) { fail(error.message); process.exit(1); } } initProject();