UNPKG

quallaa-cli

Version:

Sets up core infrastructure services for AI-assisted development

549 lines (506 loc) 16.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.generateProject = generateProject; async function generateProject(config) { const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); await fs.mkdir(config.name, { recursive: true }); process.chdir(config.name); await generatePackageJson(config); await generateNextConfig(); await generateTsConfig(); await generateTailwindConfig(); await generateProjectStructure(); await generateBaseComponents(config); await generateEnvExample(config); await generateGitignore(); await generateReadme(config); } async function generatePackageJson(config) { const packageJson = { name: config.name, version: '0.1.0', private: true, scripts: { dev: 'next dev --turbo', build: 'next build', start: 'next start', lint: 'next lint', 'type-check': 'tsc --noEmit', }, dependencies: { next: '^15.0.0', react: '^18.0.0', 'react-dom': '^18.0.0', ...(config.services.includes('supabase') && { '@supabase/ssr': '^0.5.0', '@supabase/supabase-js': '^2.45.0', }), ...(config.services.includes('resend') && { resend: '^4.0.0', }), ...(config.services.includes('typesense') && { typesense: '^1.8.0', }), }, devDependencies: { '@types/node': '^20.0.0', '@types/react': '^18.0.0', '@types/react-dom': '^18.0.0', eslint: '^8.0.0', 'eslint-config-next': '^15.0.0', tailwindcss: '^3.4.0', typescript: '^5.0.0', autoprefixer: '^10.0.0', postcss: '^8.0.0', }, engines: { node: '>=18.0.0', }, }; const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); await fs.writeFile('package.json', JSON.stringify(packageJson, null, 2)); } async function generateNextConfig() { const nextConfig = `/** @type {import('next').NextConfig} */ const nextConfig = { experimental: { turbo: { resolveExtensions: [ '.mdx', '.tsx', '.ts', '.jsx', '.js', '.mjs', '.json', ], }, }, eslint: { dirs: ['app', 'lib', 'components'], }, typescript: { ignoreBuildErrors: false, }, } module.exports = nextConfig `; const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); await fs.writeFile('next.config.js', nextConfig); } async function generateTsConfig() { const tsConfig = { compilerOptions: { lib: ['dom', 'dom.iterable', 'es6'], allowJs: true, skipLibCheck: true, strict: true, noEmit: true, esModuleInterop: true, module: 'esnext', moduleResolution: 'bundler', resolveJsonModule: true, isolatedModules: true, jsx: 'preserve', incremental: true, plugins: [ { name: 'next', }, ], baseUrl: '.', paths: { '@/*': ['./*'], }, }, include: ['next-env.d.ts', '**/*.ts', '**/*.tsx', '.next/types/**/*.ts'], exclude: ['node_modules'], }; const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); await fs.writeFile('tsconfig.json', JSON.stringify(tsConfig, null, 2)); } async function generateTailwindConfig() { const tailwindConfig = `/** @type {import('tailwindcss').Config} */ module.exports = { content: [ './pages/**/*.{js,ts,jsx,tsx,mdx}', './components/**/*.{js,ts,jsx,tsx,mdx}', './app/**/*.{js,ts,jsx,tsx,mdx}', ], theme: { extend: { colors: { background: 'var(--background)', foreground: 'var(--foreground)', }, }, }, plugins: [], } `; const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); await fs.writeFile('tailwind.config.js', tailwindConfig); } async function generateProjectStructure() { const directories = [ 'app', 'app/api', 'components', 'components/ui', 'lib', 'public', ]; const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); for (const dir of directories) { await fs.mkdir(dir, { recursive: true }); } } async function generateBaseComponents(config) { const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); const layout = `import type { Metadata } from 'next' import { Inter } from 'next/font/google' import './globals.css' const inter = Inter({ subsets: ['latin'] }) export const metadata: Metadata = { title: '${config.name}', description: 'Built with Quallaa CLI - AI-native development for domain experts', } export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( <html lang="en"> <body className={inter.className}>{children}</body> </html> ) } `; await fs.writeFile('app/layout.tsx', layout); const page = `export default function Home() { return ( <main className="flex min-h-screen flex-col items-center justify-between p-24"> <div className="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm"> <h1 className="text-4xl font-bold text-center"> Welcome to ${config.name} </h1> <p className="text-center mt-4 text-lg text-gray-600"> Built with Quallaa CLI - AI-native development for domain experts </p> <div className="mt-8 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> ${generateServiceCards(config.services)} </div> </div> </main> ) } `; await fs.writeFile('app/page.tsx', page); const globalsCss = `@tailwind base; @tailwind components; @tailwind utilities; :root { --foreground-rgb: 0, 0, 0; --background-start-rgb: 214, 219, 220; --background-end-rgb: 255, 255, 255; } @media (prefers-color-scheme: dark) { :root { --foreground-rgb: 255, 255, 255; --background-start-rgb: 0, 0, 0; --background-end-rgb: 0, 0, 0; } } body { color: rgb(var(--foreground-rgb)); background: linear-gradient( to bottom, transparent, rgb(var(--background-end-rgb)) ) rgb(var(--background-start-rgb)); } `; await fs.writeFile('app/globals.css', globalsCss); const types = `export interface User { id: string email: string name?: string } export interface ApiResponse<T = any> { data?: T error?: string success: boolean } `; await fs.writeFile('lib/types.ts', types); if (config.services.includes('supabase')) { await generateSupabaseConfig(); } if (config.services.includes('resend')) { await generateResendConfig(); } } function generateServiceCards(services) { const serviceCards = { vercel: ` <div className="p-6 border border-gray-200 rounded-lg"> <h3 className="text-lg font-semibold mb-2">Vercel</h3> <p className="text-gray-600">Hosting and deployment configured</p> </div>`, supabase: ` <div className="p-6 border border-gray-200 rounded-lg"> <h3 className="text-lg font-semibold mb-2">Supabase</h3> <p className="text-gray-600">Database and authentication ready</p> </div>`, github: ` <div className="p-6 border border-gray-200 rounded-lg"> <h3 className="text-lg font-semibold mb-2">GitHub</h3> <p className="text-gray-600">Version control configured</p> </div>`, resend: ` <div className="p-6 border border-gray-200 rounded-lg"> <h3 className="text-lg font-semibold mb-2">Resend</h3> <p className="text-gray-600">Email service configured</p> </div>`, typesense: ` <div className="p-6 border border-gray-200 rounded-lg"> <h3 className="text-lg font-semibold mb-2">Typesense</h3> <p className="text-gray-600">Search engine configured</p> </div>`, }; return services.map(service => serviceCards[service] || '').join(''); } async function generateSupabaseConfig() { const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); const supabaseConfig = `import { createClient } from '@supabase/supabase-js' const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL! const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! export const supabase = createClient(supabaseUrl, supabaseAnonKey) `; await fs.writeFile('lib/supabase.ts', supabaseConfig); const middleware = `import { createServerClient } from '@supabase/ssr' import { NextResponse, type NextRequest } from 'next/server' export async function middleware(request: NextRequest) { let response = NextResponse.next({ request: { headers: request.headers, }, }) const supabase = createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { cookies: { get(name: string) { return request.cookies.get(name)?.value }, set(name: string, value: string, options: any) { request.cookies.set({ name, value, ...options, }) response = NextResponse.next({ request: { headers: request.headers, }, }) response.cookies.set({ name, value, ...options, }) }, remove(name: string, options: any) { request.cookies.set({ name, value: '', ...options, }) response = NextResponse.next({ request: { headers: request.headers, }, }) response.cookies.set({ name, value: '', ...options, }) }, }, } ) await supabase.auth.getUser() return response } export const config = { matcher: [ '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)', ], } `; await fs.writeFile('middleware.ts', middleware); } async function generateResendConfig() { const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); const resendConfig = `import { Resend } from 'resend' export const resend = new Resend(process.env.RESEND_API_KEY) `; await fs.writeFile('lib/resend.ts', resendConfig); } async function generateEnvExample(config) { const envVars = ['# Environment Variables']; if (config.services.includes('supabase')) { envVars.push('', '# Supabase'); envVars.push('NEXT_PUBLIC_SUPABASE_URL=your_supabase_url'); envVars.push('NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key'); envVars.push('SUPABASE_SERVICE_ROLE_KEY=your_service_role_key'); } if (config.services.includes('resend')) { envVars.push('', '# Resend'); envVars.push('RESEND_API_KEY=your_resend_api_key'); } if (config.services.includes('typesense')) { envVars.push('', '# Typesense (Optional)'); envVars.push('TYPESENSE_HOST=your_typesense_host'); envVars.push('TYPESENSE_PORT=443'); envVars.push('TYPESENSE_PROTOCOL=https'); envVars.push('TYPESENSE_API_KEY=your_typesense_api_key'); } const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); await fs.writeFile('.env.example', envVars.join('\n')); } async function generateGitignore() { const gitignore = `# Dependencies node_modules/ /.pnp .pnp.js # Testing /coverage # Next.js /.next/ /out/ # Production /build # Environment variables .env .env.local .env.development.local .env.test.local .env.production.local # Vercel .vercel # TypeScript *.tsbuildinfo next-env.d.ts # Supabase /supabase/.temp /supabase/.branches /supabase/.projects # IDE .vscode/ .idea/ # OS .DS_Store Thumbs.db # Logs npm-debug.log* yarn-debug.log* yarn-error.log* `; const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); await fs.writeFile('.gitignore', gitignore); } async function generateReadme(config) { const readme = `# ${config.name} Built with [Quallaa CLI](https://quallaa.com) - AI-native development for domain experts. ## Services Configured ${config.services.map(service => `- **${service}**: ${getServiceDescription(service)}`).join('\n')} ## Getting Started 1. **Install dependencies:** \`\`\`bash npm install \`\`\` 2. **Set up environment variables:** \`\`\`bash cp .env.example .env.local # Fill in your service credentials \`\`\` 3. **${config.services.includes('supabase') ? 'Start Supabase:' : 'Configure your database:'}** ${config.services.includes('supabase') ? '```bash\n supabase start\n supabase db push\n ```' : 'Configure your chosen database solution.'} 4. **Start the development server:** \`\`\`bash npm run dev \`\`\` 5. **Open [http://localhost:3000](http://localhost:3000)** to see your application. ## Project Structure - \`app/\` - Next.js 15 App Router pages and layouts - \`components/\` - Reusable React components - \`lib/\` - Utility functions and configurations - \`public/\` - Static assets ## Available Scripts - \`npm run dev\` - Start development server with Turbopack - \`npm run build\` - Build the application for production - \`npm run start\` - Start the production server - \`npm run lint\` - Run ESLint - \`npm run type-check\` - Run TypeScript type checking ## Learn More - [Next.js Documentation](https://nextjs.org/docs) ${config.services.includes('supabase') ? '- [Supabase Documentation](https://supabase.com/docs)' : ''} ${config.services.includes('vercel') ? '- [Vercel Documentation](https://vercel.com/docs)' : ''} - [Quallaa Documentation](https://docs.quallaa.com) ## Support For support with this application, visit [Quallaa Support](https://quallaa.com/support) or check the generated \`CLAUDE.md\` file for detailed context. `; const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); await fs.writeFile('README.md', readme); } function getServiceDescription(service) { const descriptions = { vercel: 'Hosting and deployment', supabase: 'Database and authentication', github: 'Version control', resend: 'Email service', typesense: 'Search engine', }; return descriptions[service] || 'Service configured'; } //# sourceMappingURL=project.js.map