UNPKG

@entro314labs/at3-stack-kit

Version:

Upgrade existing projects to AT3 Stack with intelligent migration

1,162 lines (1,104 loc) 39.5 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { addAI: () => addAI, addI18n: () => addI18n, addPWA: () => addPWA, addSupabase: () => addSupabase, addTesting: () => addTesting, analyzeProject: () => analyzeProject, detectProjectType: () => detectProjectType, getMissingFeatures: () => getMissingFeatures, getRecommendations: () => getRecommendations, isCompatible: () => isCompatible, program: () => import_commander.program }); module.exports = __toCommonJS(index_exports); // src/cli.ts var import_prompts = require("@clack/prompts"); var import_chalk2 = __toESM(require("chalk"), 1); var import_commander = require("commander"); var import_detect_package_manager2 = require("detect-package-manager"); var import_fs_extra6 = require("fs-extra"); var import_path4 = require("path"); // src/detect.ts var import_detect_package_manager = require("detect-package-manager"); var import_fs_extra = require("fs-extra"); var import_path = require("path"); async function detectProjectType(projectPath) { const packageJsonPath = (0, import_path.join)(projectPath, "package.json"); if (!(0, import_fs_extra.existsSync)(packageJsonPath)) { return "unknown"; } try { const packageJson = JSON.parse((0, import_fs_extra.readFileSync)(packageJsonPath, "utf-8")); const deps = { ...packageJson.dependencies, ...packageJson.devDependencies }; const hasAI = deps.ai || deps["@ai-sdk/openai"] || deps["@ai-sdk/anthropic"] || deps["@ai-sdk/google"]; const hasSupabase = deps["@supabase/supabase-js"] || deps["@supabase/ssr"]; const hasNextJS = deps.next; const hasTailwind = deps.tailwindcss; const hasTypeScript = deps.typescript || (0, import_fs_extra.existsSync)((0, import_path.join)(projectPath, "tsconfig.json")); if (hasAI && hasSupabase && hasNextJS && (hasTailwind || hasTypeScript)) { return "ait3e"; } if (deps["@t3-oss/create-t3-app"] || deps.next && deps["@trpc/server"] && deps.prisma) { return "t3"; } if (deps.next) return "nextjs"; if (deps.nuxt || deps["@nuxt/core"]) return "nuxt"; if (deps.vue) return "vue"; if (deps.react) return "react"; if (deps.vite) return "vite"; if (deps.webpack) return "webpack"; return "node"; } catch (error) { return "unknown"; } } async function analyzeProject(projectPath) { if (!(0, import_fs_extra.existsSync)(projectPath)) { throw new Error(`Project path does not exist: ${projectPath}`); } const packageJsonPath = (0, import_path.join)(projectPath, "package.json"); if (!(0, import_fs_extra.existsSync)(packageJsonPath)) { throw new Error("No package.json found. This does not appear to be a Node.js project."); } const packageJson = JSON.parse((0, import_fs_extra.readFileSync)(packageJsonPath, "utf-8")); let packageManager = "npm"; try { packageManager = await (0, import_detect_package_manager.detect)({ cwd: projectPath }); } catch { if ((0, import_fs_extra.existsSync)((0, import_path.join)(projectPath, "pnpm-lock.yaml"))) packageManager = "pnpm"; else if ((0, import_fs_extra.existsSync)((0, import_path.join)(projectPath, "yarn.lock"))) packageManager = "yarn"; else if ((0, import_fs_extra.existsSync)((0, import_path.join)(projectPath, "bun.lockb"))) packageManager = "bun"; } const dependencies = analyzeDependencies(packageJson); const configFiles = findConfigFiles(projectPath); const hasTypeScript = hasTypeScriptSupport(projectPath, dependencies); const hasNextjs = hasDependency(dependencies, "next"); const hasReact = hasDependency(dependencies, "react"); const hasVue = hasDependency(dependencies, "vue"); const hasTailwind = hasDependency(dependencies, "tailwindcss"); const hasTRPC = dependencies.some((dep) => dep.name.includes("@trpc/")); const hasEslint = hasDependency(dependencies, "eslint"); const hasPrettier = hasDependency(dependencies, "prettier"); const hasBiome = hasDependency(dependencies, "@biomejs/biome"); const hasAI = hasDependency(dependencies, "ai") || hasDependency(dependencies, "@ai-sdk/openai") || hasDependency(dependencies, "@ai-sdk/anthropic") || hasDependency(dependencies, "@ai-sdk/google"); const hasSupabase = hasDependency(dependencies, "@supabase/supabase-js") || hasDependency(dependencies, "@supabase/ssr"); const hasEdgeRuntime = (0, import_fs_extra.existsSync)((0, import_path.join)(projectPath, "middleware.ts")) || (0, import_fs_extra.existsSync)((0, import_path.join)(projectPath, "src/middleware.ts")); const hasVectorDB = hasSupabaseVectorConfig(projectPath); const hasPWA = dependencies.some( (dep) => dep.name.includes("workbox") || dep.name.includes("pwa") ); const hasI18n = dependencies.some( (dep) => dep.name.includes("next-intl") || dep.name.includes("i18n") ); const hasVitest = hasDependency(dependencies, "vitest"); const hasPlaywright = hasDependency(dependencies, "playwright") || hasDependency(dependencies, "@playwright/test"); return { path: projectPath, type: await detectProjectType(projectPath), hasNextjs, hasReact, hasVue, hasTypeScript, hasTailwind, hasTRPC, hasSupabase, hasAI, hasPWA, hasI18n, hasVitest, hasPlaywright, hasEslint, hasPrettier, hasBiome, hasEdgeRuntime, hasVectorDB, packageManager, dependencies, configFiles }; } function analyzeDependencies(packageJson) { const deps = []; if (packageJson.dependencies) { Object.entries(packageJson.dependencies).forEach(([name, version]) => { deps.push({ name, version, type: "dependency" }); }); } if (packageJson.devDependencies) { Object.entries(packageJson.devDependencies).forEach(([name, version]) => { deps.push({ name, version, type: "devDependency" }); }); } if (packageJson.peerDependencies) { Object.entries(packageJson.peerDependencies).forEach(([name, version]) => { deps.push({ name, version, type: "peerDependency" }); }); } return deps; } function findConfigFiles(projectPath) { const configFiles = []; const commonConfigFiles = [ // TypeScript "tsconfig.json", "tsconfig.build.json", // Next.js "next.config.js", "next.config.ts", "next.config.mjs", // Tailwind "tailwind.config.js", "tailwind.config.ts", "tailwind.config.mjs", "postcss.config.js", "postcss.config.mjs", // Linting ".eslintrc.js", ".eslintrc.json", ".eslintrc.yml", ".eslintrc.yaml", "eslint.config.js", "eslint.config.mjs", ".prettierrc", ".prettierrc.js", ".prettierrc.json", "biome.json", // Testing "vitest.config.ts", "vitest.config.js", "jest.config.js", "jest.config.ts", "playwright.config.ts", // Build tools "vite.config.ts", "vite.config.js", "webpack.config.js", "rollup.config.js", // Other ".gitignore", ".env.example", ".env.local", "README.md" ]; commonConfigFiles.forEach((file) => { if ((0, import_fs_extra.existsSync)((0, import_path.join)(projectPath, file))) { configFiles.push(file); } }); return configFiles; } function hasTypeScriptSupport(projectPath, dependencies) { return (0, import_fs_extra.existsSync)((0, import_path.join)(projectPath, "tsconfig.json")) || hasDependency(dependencies, "typescript"); } function hasDependency(dependencies, name) { return dependencies.some((dep) => dep.name === name); } function hasSupabaseVectorConfig(projectPath) { const supabaseMigrationDir = (0, import_path.join)(projectPath, "supabase", "migrations"); if ((0, import_fs_extra.existsSync)(supabaseMigrationDir)) { try { const { readdirSync } = require("fs"); const migrationFiles = readdirSync(supabaseMigrationDir); return migrationFiles.some((file) => { if (file.endsWith(".sql")) { const content = (0, import_fs_extra.readFileSync)((0, import_path.join)(supabaseMigrationDir, file), "utf8"); return content.includes("vector") || content.includes("embedding"); } return false; }); } catch (error) { return false; } } return false; } function getMissingFeatures(info) { const missing = []; if (!info.hasSupabase) missing.push("supabase"); if (!info.hasAI) missing.push("ai"); if (!info.hasPWA) missing.push("pwa"); if (!info.hasI18n) missing.push("i18n"); if (!info.hasVitest) missing.push("testing"); if (!info.hasTailwind) missing.push("tailwind"); if (!info.hasTypeScript) missing.push("typescript"); return missing; } function isCompatible(info) { if (!info.hasNextjs && !info.hasReact) return false; if (!["npm", "pnpm", "yarn", "bun"].includes(info.packageManager)) return false; return true; } function getRecommendations(info) { const recommendations = []; if (!info.hasTypeScript) { recommendations.push({ priority: "high", feature: "typescript", reason: "TypeScript provides better development experience and type safety" }); } if (!info.hasTailwind) { recommendations.push({ priority: "high", feature: "tailwind", reason: "Tailwind CSS is essential for AT3 stack styling" }); } if (!info.hasSupabase) { recommendations.push({ priority: "medium", feature: "supabase", reason: "Supabase provides database, auth, and edge functions" }); } if (!info.hasAI) { recommendations.push({ priority: "medium", feature: "ai", reason: "AI integration is a core feature of AT3 stack" }); } if (!info.hasVitest && !info.hasPlaywright) { recommendations.push({ priority: "low", feature: "testing", reason: "Comprehensive testing improves code quality" }); } return recommendations; } // src/features/add-ai.ts var import_node_path = require("path"); var import_fs_extra2 = require("fs-extra"); async function addAI(type, projectPath) { const srcPath = (0, import_node_path.join)(projectPath, "src"); await (0, import_fs_extra2.ensureDir)(srcPath); const aiPath = (0, import_node_path.join)(srcPath, "lib", "ai"); await (0, import_fs_extra2.ensureDir)(aiPath); if (type === "custom") { await addCustomAI(aiPath); } if (type === "vercel") { await addVercelAI(aiPath); } const apiPath = (0, import_node_path.join)(srcPath, "app", "api"); await (0, import_fs_extra2.ensureDir)(apiPath); await addAPIRoutes(apiPath, type); await updatePackageJson(projectPath, type); } async function addCustomAI(aiPath) { const clientConfig = `/** * Custom AI client configuration */ export interface AIProvider { name: string; baseURL: string; apiKey: string; } export const AI_PROVIDERS = { openai: { name: 'OpenAI', baseURL: 'https://api.openai.com/v1', apiKey: process.env.OPENAI_API_KEY!, }, anthropic: { name: 'Anthropic', baseURL: 'https://api.anthropic.com', apiKey: process.env.ANTHROPIC_API_KEY!, }, google: { name: 'Google AI', baseURL: 'https://generativelanguage.googleapis.com', apiKey: process.env.GOOGLE_AI_API_KEY!, }, } as const; export type AIProviderKey = keyof typeof AI_PROVIDERS; `; await (0, import_fs_extra2.writeFile)((0, import_node_path.join)(aiPath, "config.ts"), clientConfig); const client = `/** * Custom AI client implementation */ import { AI_PROVIDERS, type AIProviderKey } from './config.js'; export interface ChatMessage { role: 'system' | 'user' | 'assistant'; content: string; } export interface CompletionOptions { model: string; temperature?: number; maxTokens?: number; } export class AIClient { constructor( private provider: AIProviderKey, private options: CompletionOptions = {} ) {} async completion( messages: ChatMessage[], options: Partial<CompletionOptions> = {} ): Promise<string> { const config = AI_PROVIDERS[this.provider]; const finalOptions = { ...this.options, ...options }; // Implementation would depend on the provider // This is a basic structure const response = await fetch(\`\${config.baseURL}/chat/completions\`, { method: 'POST', headers: { 'Authorization': \`Bearer \${config.apiKey}\`, 'Content-Type': 'application/json', }, body: JSON.stringify({ model: finalOptions.model || 'gpt-3.5-turbo', messages, temperature: finalOptions.temperature || 0.7, max_tokens: finalOptions.maxTokens || 1000, }), }); const data = await response.json(); return data.choices[0]?.message?.content || ''; } } `; await (0, import_fs_extra2.writeFile)((0, import_node_path.join)(aiPath, "client.ts"), client); } async function addVercelAI(aiPath) { const vercelClient = `/** * Vercel AI SDK integration */ import { anthropic } from '@ai-sdk/anthropic'; import { google } from '@ai-sdk/google'; import { openai } from '@ai-sdk/openai'; import { generateText, streamText } from 'ai'; export const AI_MODELS = { 'gpt-4-turbo': openai('gpt-4-turbo'), 'gpt-3.5-turbo': openai('gpt-3.5-turbo'), 'claude-3-haiku': anthropic('claude-3-haiku-20240307'), 'claude-3-sonnet': anthropic('claude-3-sonnet-20240229'), 'gemini-pro': google('models/gemini-pro'), } as const; export type AIModelKey = keyof typeof AI_MODELS; export async function generateCompletion( model: AIModelKey, prompt: string, options?: { temperature?: number; maxTokens?: number; } ) { const { text } = await generateText({ model: AI_MODELS[model], prompt, temperature: options?.temperature || 0.7, maxTokens: options?.maxTokens || 1000, }); return text; } export async function streamCompletion( model: AIModelKey, prompt: string, options?: { temperature?: number; maxTokens?: number; } ) { const { textStream } = await streamText({ model: AI_MODELS[model], prompt, temperature: options?.temperature || 0.7, maxTokens: options?.maxTokens || 1000, }); return textStream; } `; await (0, import_fs_extra2.writeFile)((0, import_node_path.join)(aiPath, "vercel-client.ts"), vercelClient); const hooks = `/** * React hooks for Vercel AI SDK */ 'use client'; import { useChat, useCompletion } from 'ai/react'; import { type AIModelKey } from './vercel-client.js'; export function useAIChat(model: AIModelKey = 'gpt-3.5-turbo') { return useChat({ api: '/api/chat', body: { model, }, }); } export function useAICompletion(model: AIModelKey = 'gpt-3.5-turbo') { return useCompletion({ api: '/api/completion', body: { model, }, }); } `; await (0, import_fs_extra2.writeFile)((0, import_node_path.join)(aiPath, "vercel-hooks.ts"), hooks); } async function addAPIRoutes(apiPath, type) { if (type === "vercel") { const chatRoute = `import { AI_MODELS, type AIModelKey } from '@/lib/ai/vercel-client'; import { streamText } from 'ai'; export async function POST(req: Request) { const { messages, model = 'gpt-3.5-turbo' } = await req.json(); const result = await streamText({ model: AI_MODELS[model as AIModelKey], messages, }); return result.toDataStreamResponse(); } `; await (0, import_fs_extra2.ensureDir)((0, import_node_path.join)(apiPath, "chat")); await (0, import_fs_extra2.writeFile)((0, import_node_path.join)(apiPath, "chat", "route.ts"), chatRoute); const completionRoute = `import { AI_MODELS, type AIModelKey } from '@/lib/ai/vercel-client'; import { generateText } from 'ai'; export async function POST(req: Request) { const { prompt, model = 'gpt-3.5-turbo' } = await req.json(); const { text } = await generateText({ model: AI_MODELS[model as AIModelKey], prompt, }); return Response.json({ text }); } `; await (0, import_fs_extra2.ensureDir)((0, import_node_path.join)(apiPath, "completion")); await (0, import_fs_extra2.writeFile)((0, import_node_path.join)(apiPath, "completion", "route.ts"), completionRoute); } } async function updatePackageJson(projectPath, type) { const packageJsonPath = (0, import_node_path.join)(projectPath, "package.json"); if (!await (0, import_fs_extra2.pathExists)(packageJsonPath)) return; const packageJson = JSON.parse(await (0, import_fs_extra2.readFile)(packageJsonPath, "utf-8")); if (!packageJson.dependencies) packageJson.dependencies = {}; if (type === "vercel") { packageJson.dependencies.ai = "^5.0.8"; packageJson.dependencies["@ai-sdk/openai"] = "^2.0.7"; packageJson.dependencies["@ai-sdk/anthropic"] = "^2.0.1"; packageJson.dependencies["@ai-sdk/google"] = "^2.0.3"; } await (0, import_fs_extra2.writeFile)(packageJsonPath, JSON.stringify(packageJson, null, 2)); } // src/features/add-i18n.ts function addI18n(_projectPath) { console.log("\u2713 i18n support would be added here"); } // src/features/add-pwa.ts var import_node_path2 = require("path"); var import_fs_extra3 = require("fs-extra"); async function addPWA(projectPath) { await addManifest(projectPath); await addServiceWorker(projectPath); await updatePackageJson2(projectPath); console.log("\u2713 PWA support added"); } async function addManifest(projectPath) { const publicPath = (0, import_node_path2.join)(projectPath, "public"); await (0, import_fs_extra3.ensureDir)(publicPath); const manifest = { name: "AT3 App", short_name: "AT3", description: "AT3 Stack Application", start_url: "/", display: "standalone", background_color: "#ffffff", theme_color: "#000000", icons: [ { src: "/icons/icon-192x192.png", sizes: "192x192", type: "image/png" }, { src: "/icons/icon-512x512.png", sizes: "512x512", type: "image/png" } ] }; await (0, import_fs_extra3.writeFile)((0, import_node_path2.join)(publicPath, "manifest.json"), JSON.stringify(manifest, null, 2)); } async function addServiceWorker(projectPath) { const publicPath = (0, import_node_path2.join)(projectPath, "public"); const sw = `// Basic service worker for PWA self.addEventListener('install', (event) => { console.log('Service worker installing...'); self.skipWaiting(); }); self.addEventListener('activate', (event) => { console.log('Service worker activating...'); event.waitUntil(self.clients.claim()); }); self.addEventListener('fetch', (event) => { // Add caching logic here }); `; await (0, import_fs_extra3.writeFile)((0, import_node_path2.join)(publicPath, "sw.js"), sw); } function updatePackageJson2(_projectPath) { console.log("PWA package.json updates would go here"); } // src/features/add-supabase.ts var import_fs_extra4 = require("fs-extra"); var import_path2 = require("path"); async function addSupabase(projectPath) { const srcPath = (0, import_path2.join)(projectPath, "src"); await (0, import_fs_extra4.ensureDir)(srcPath); const supabasePath = (0, import_path2.join)(srcPath, "lib", "supabase"); await (0, import_fs_extra4.ensureDir)(supabasePath); await addSupabaseClients(supabasePath); await addAuthHelpers((0, import_path2.join)(srcPath, "lib", "auth")); await updatePackageJson3(projectPath); await addEnvExample(projectPath); await initSupabaseConfig(projectPath); } async function addSupabaseClients(supabasePath) { const clientCode = `import { createBrowserClient } from '@supabase/ssr'; export function createClient() { return createBrowserClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! ); } `; await (0, import_fs_extra4.writeFile)((0, import_path2.join)(supabasePath, "client.ts"), clientCode); const serverCode = `import { createServerClient } from '@supabase/ssr'; import { cookies } from 'next/headers'; export async function createServerClient() { const cookieStore = await cookies(); return createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { cookies: { getAll() { return cookieStore.getAll(); }, setAll(cookiesToSet) { try { cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options) ); } catch { // The 'setAll' method was called from a Server Component. // This can be ignored if you have middleware refreshing // user sessions. } }, }, } ); } `; await (0, import_fs_extra4.writeFile)((0, import_path2.join)(supabasePath, "server.ts"), serverCode); const middlewareCode = `import { createServerClient } from '@supabase/ssr'; import { NextResponse, type NextRequest } from 'next/server'; export async function updateSession(request: NextRequest) { let supabaseResponse = NextResponse.next({ request, }); const supabase = createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { cookies: { getAll() { return request.cookies.getAll(); }, setAll(cookiesToSet) { cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value)); supabaseResponse = NextResponse.next({ request, }); cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options) ); }, }, } ); // This will refresh session if expired - required for Server Components await supabase.auth.getUser(); return supabaseResponse; } `; await (0, import_fs_extra4.writeFile)((0, import_path2.join)(supabasePath, "middleware.ts"), middlewareCode); } async function addAuthHelpers(authPath) { await (0, import_fs_extra4.ensureDir)(authPath); const authHelpersCode = `import { createServerClient } from '@/lib/supabase/server'; import { redirect } from 'next/navigation'; export async function requireAuth() { const supabase = await createServerClient(); const { data: { user }, error } = await supabase.auth.getUser(); if (error || !user) { redirect('/auth/sign-in'); } return user; } export async function getUser() { const supabase = await createServerClient(); const { data: { user } } = await supabase.auth.getUser(); return user; } export async function signOut() { const supabase = await createServerClient(); await supabase.auth.signOut(); redirect('/'); } `; await (0, import_fs_extra4.writeFile)((0, import_path2.join)(authPath, "auth-helpers.ts"), authHelpersCode); } async function updatePackageJson3(projectPath) { const packageJsonPath = (0, import_path2.join)(projectPath, "package.json"); if (!await (0, import_fs_extra4.pathExists)(packageJsonPath)) return; const packageJson = JSON.parse(await (0, import_fs_extra4.readFile)(packageJsonPath, "utf-8")); if (!packageJson.dependencies) packageJson.dependencies = {}; if (!packageJson.devDependencies) packageJson.devDependencies = {}; packageJson.dependencies["@supabase/supabase-js"] = "^2.54.0"; packageJson.dependencies["@supabase/ssr"] = "^0.7.0-rc.2"; packageJson.devDependencies["supabase"] = "^2.33.9"; await (0, import_fs_extra4.writeFile)(packageJsonPath, JSON.stringify(packageJson, null, 2)); } async function addEnvExample(projectPath) { const envExamplePath = (0, import_path2.join)(projectPath, ".env.example"); let envContent = ""; if (await (0, import_fs_extra4.pathExists)(envExamplePath)) { envContent = await (0, import_fs_extra4.readFile)(envExamplePath, "utf-8"); } if (!envContent.includes("NEXT_PUBLIC_SUPABASE_URL")) { const supabaseVars = ` # Supabase NEXT_PUBLIC_SUPABASE_URL=your_supabase_url NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key SUPABASE_SERVICE_ROLE_KEY=your_service_role_key `; envContent = envContent + supabaseVars; await (0, import_fs_extra4.writeFile)(envExamplePath, envContent); } } async function initSupabaseConfig(projectPath) { const supabaseConfigPath = (0, import_path2.join)(projectPath, "supabase", "config.toml"); if (await (0, import_fs_extra4.pathExists)(supabaseConfigPath)) return; await (0, import_fs_extra4.ensureDir)((0, import_path2.join)(projectPath, "supabase")); const config = `# A string used to distinguish different Supabase projects on the same host. project_id = "your-project-id" [api] enabled = true port = 54321 schemas = ["public", "graphql_public"] extra_search_path = ["public", "extensions"] max_rows = 1000 [auth] enabled = true port = 9999 site_url = "http://localhost:3000" additional_redirect_urls = ["https://localhost:3000"] jwt_expiry = 3600 refresh_token_rotation_enabled = true security_update_password_require_reauthentication = true [auth.email] enable_signup = true double_confirm_changes = true enable_confirmations = false `; await (0, import_fs_extra4.writeFile)(supabaseConfigPath, config); } // src/features/add-testing.ts function addTesting(_projectPath) { console.log("\u2713 Testing suite would be added here"); } // src/utils/cli-styling.ts var import_boxen = __toESM(require("boxen"), 1); var import_chalk = __toESM(require("chalk"), 1); var import_figures = __toESM(require("figures"), 1); var import_gradient_string = __toESM(require("gradient-string"), 1); var colors = { primary: import_chalk.default.hex("#2563eb"), // Blue secondary: import_chalk.default.hex("#7c3aed"), // Purple success: import_chalk.default.green, error: import_chalk.default.red, warning: import_chalk.default.yellow, info: import_chalk.default.blue, muted: import_chalk.default.gray, accent: import_chalk.default.magenta, ai: import_chalk.default.hex("#10b981") // Emerald for AI features }; var gradients = { at3: (0, import_gradient_string.default)("#2563eb", "#10b981"), // Blue to Emerald ai: (0, import_gradient_string.default)("#10b981", "#059669"), // Emerald gradient success: (0, import_gradient_string.default)("#22c55e", "#16a34a"), // Green gradient warning: (0, import_gradient_string.default)("#f59e0b", "#d97706"), // Amber gradient error: (0, import_gradient_string.default)("#ef4444", "#dc2626") // Red gradient }; var symbols = { success: colors.success(import_figures.default.tick), error: colors.error(import_figures.default.cross), warning: colors.warning(import_figures.default.warning), info: colors.info(import_figures.default.info), arrow: colors.muted(import_figures.default.arrowRight), bullet: colors.muted(import_figures.default.bullet), line: colors.muted(import_figures.default.line), ai: colors.ai("\u{1F916}"), stack: colors.primary("\u{1F4DA}"), edge: colors.secondary("\u26A1"), database: colors.info("\u{1F5C4}\uFE0F"), config: colors.muted("\u2699\uFE0F") }; var style = { title: (text) => gradients.at3(text), subtitle: (text) => colors.secondary(text), heading: (text) => colors.primary.bold(text), label: (text) => colors.muted(text), value: (text) => colors.primary(text), success: (text) => colors.success(text), error: (text) => colors.error(text), warning: (text) => colors.warning(text), info: (text) => colors.info(text), muted: (text) => colors.muted(text), accent: (text) => colors.accent(text), ai: (text) => colors.ai(text), code: (text) => import_chalk.default.cyan.italic(text), path: (text) => import_chalk.default.dim.underline(text), command: (text) => import_chalk.default.bgBlack.white.bold(` ${text} `) }; var header = { main: (title, subtitle) => { const titleText = gradients.at3.multiline(title); const subtitleText = subtitle ? ` ${colors.muted(subtitle)}` : ""; return `${titleText}${subtitleText}`; }, section: (title) => ` ${colors.primary.bold(title)} ${colors.muted("\u2500".repeat(title.length))}` }; // src/utils/integration.ts var import_fs_extra5 = require("fs-extra"); var import_path3 = require("path"); function updateAT3Config(projectPath, addedFeatures) { const configPath = (0, import_path3.join)(projectPath, ".at3-config.json"); let config; if ((0, import_fs_extra5.existsSync)(configPath)) { try { config = JSON.parse(require("fs").readFileSync(configPath, "utf8")); } catch { config = createDefaultConfig(); } } else { config = createDefaultConfig(); } config.features = [.../* @__PURE__ */ new Set([...config.features, ...addedFeatures])]; config.toolsUsed = [.../* @__PURE__ */ new Set([...config.toolsUsed, "at3-stack-kit"])]; config.lastMigration = (/* @__PURE__ */ new Date()).toISOString(); try { require("fs").writeFileSync(configPath, JSON.stringify(config, null, 2)); } catch { } return config; } function createDefaultConfig() { return { version: "0.1.0", created: (/* @__PURE__ */ new Date()).toISOString(), features: [], toolsUsed: ["at3-stack-kit"] }; } function suggestAT3Tools(projectInfo, addedFeatures) { const suggestions = []; if (!projectInfo.hasEslint || !projectInfo.hasPrettier || !projectInfo.hasVitest) { suggestions.push( `${colors.info("\u{1F4A1} Tip:")} Use ${style.command("@entro314-labs/at3t")} for advanced linting, testing, and development workflow optimization` ); } if (addedFeatures.length > 0) { suggestions.push( `${colors.info("\u{1F4A1} Tip:")} For new projects, use ${style.command("create-at3-app")} to start with the AT3 stack from the beginning` ); } return suggestions; } function getPostMigrationWorkflow(projectInfo, addedFeatures) { const workflows = []; if (addedFeatures.includes("ai")) { workflows.push("Configure your AI provider API keys in .env.local"); workflows.push("Explore the AI integration examples in your project"); } if (addedFeatures.includes("supabase")) { workflows.push("Set up your Supabase project and update connection strings"); workflows.push("Run database migrations if any were added"); } if (addedFeatures.includes("testing")) { workflows.push( `Run ${style.command(projectInfo.packageManager + " test")} to verify your test setup` ); } if (projectInfo.type === "ait3e") { workflows.push("Your project now includes the complete AT3 stack!"); workflows.push("Consider using at3t for advanced development workflow optimization"); } return workflows; } // src/cli.ts var features = [ { id: "ai-custom", name: "AI Integration (Custom)", description: "Add custom AI integration with multiple providers", value: "ai-custom" }, { id: "ai-vercel", name: "AI Integration (Vercel SDK)", description: "Add Vercel AI SDK for streaming responses", value: "ai-vercel" }, { id: "supabase", name: "Supabase", description: "Add Supabase for database, auth, and edge functions", value: "supabase" }, { id: "pwa", name: "PWA Support", description: "Add Progressive Web App features", value: "pwa" }, { id: "i18n", name: "Internationalization", description: "Add next-intl for multi-language support", value: "i18n" }, { id: "testing", name: "Testing Suite", description: "Add Vitest and Playwright for comprehensive testing", value: "testing" } ]; async function main() { console.clear(); (0, import_prompts.intro)(style.title("AT3 Stack Kit")); const s = (0, import_prompts.spinner)(); s.start("Analyzing your project..."); const projectType = await detectProjectType(process.cwd()); const packageManager = await (0, import_detect_package_manager2.detect)().catch(() => "npm"); s.stop( `${symbols.success} Detected: ${style.value(getProjectTypeLabel(projectType))} project using ${style.accent(packageManager)}` ); if (projectType === "unknown") { (0, import_prompts.cancel)( "This doesn't appear to be a supported project type. AT3 Kit supports Next.js, T3, and React projects." ); process.exit(1); } (0, import_prompts.note)(getProjectInfo(projectType), "Current Project"); const selectedFeatures = await (0, import_prompts.multiselect)({ message: "Which features would you like to add?", options: features.map((feature) => ({ value: feature.value, label: feature.name, hint: feature.description })), required: true }); if ((0, import_prompts.isCancel)(selectedFeatures) || selectedFeatures.length === 0) { (0, import_prompts.cancel)("No features selected."); process.exit(0); } const shouldInstall = await (0, import_prompts.confirm)({ message: `Install dependencies with ${packageManager}?`, initialValue: true }); if ((0, import_prompts.isCancel)(shouldInstall)) { (0, import_prompts.cancel)("Operation cancelled."); process.exit(0); } const addSpinner = (0, import_prompts.spinner)(); try { for (const featureId of selectedFeatures) { addSpinner.start(`Adding ${getFeatureName(featureId)}...`); switch (featureId) { case "ai-custom": await addAI("custom", process.cwd()); break; case "ai-vercel": await addAI("vercel", process.cwd()); break; case "supabase": await addSupabase(process.cwd()); break; case "pwa": await addPWA(process.cwd()); break; case "i18n": await addI18n(process.cwd()); break; case "testing": await addTesting(process.cwd()); break; } addSpinner.stop(`\u2713 Added ${getFeatureName(featureId)}`); } if (shouldInstall) { addSpinner.start(`Installing dependencies with ${packageManager}...`); addSpinner.stop("\u2713 Dependencies installed"); } const projectInfo = { type: projectType, packageManager }; const addedFeatures = selectedFeatures; updateAT3Config(process.cwd(), addedFeatures); const toolSuggestions = suggestAT3Tools(projectInfo, addedFeatures); const workflows = getPostMigrationWorkflow(projectInfo, addedFeatures); (0, import_prompts.outro)(style.success("\u{1F389} Your project has been upgraded to AT3 Stack!")); const nextSteps = [ "Review the added files and configurations", "Update your .env.local with required API keys", `Run ${style.command(packageManager + " dev")} to test your upgraded project`, ...workflows ]; (0, import_prompts.note)( ` ${header.section("Next steps:")} ${nextSteps.map((step, i) => ` ${colors.muted(`${i + 1}.`)} ${step}`).join("\n")} ${colors.primary("Documentation:")} ${colors.muted("\u2022")} AT3 Stack Guide: https://at3-stack.dev/docs ${colors.muted("\u2022")} Feature Guides: https://at3-stack.dev/docs/features ${toolSuggestions.length > 0 ? `${colors.primary("AT3 Ecosystem:")} ${toolSuggestions.map((s2) => `${colors.muted("\u2022")} ${s2}`).join("\n ")} ` : ""}${colors.primary("Need help?")} ${colors.muted("\u2022")} GitHub Issues: https://github.com/entro314-labs/at3-stack-kit/issues ${colors.muted("\u2022")} Discord: https://discord.gg/at3-stack `, "Welcome to AT3!" ); } catch (error) { addSpinner.stop("Error occurred"); console.error(import_chalk2.default.red("Error upgrading project:"), error); process.exit(1); } } function getProjectTypeLabel(type) { switch (type) { case "nextjs": return "Next.js"; case "t3": return "T3 Stack"; case "react": return "React"; case "unknown": return "Unknown"; default: return "Unknown"; } } function getProjectInfo(type) { const packageJsonPath = (0, import_path4.join)(process.cwd(), "package.json"); if (!(0, import_fs_extra6.existsSync)(packageJsonPath)) { return "No package.json found"; } const packageJson = JSON.parse((0, import_fs_extra6.readFileSync)(packageJsonPath, "utf-8")); const deps = Object.keys({ ...packageJson.dependencies, ...packageJson.devDependencies }); let info = `Project: ${packageJson.name || "unnamed"} `; info += `Type: ${getProjectTypeLabel(type)} `; const keyDeps = ["next", "react", "typescript", "tailwindcss"]; const foundDeps = keyDeps.filter((dep) => deps.some((d) => d.includes(dep))); if (foundDeps.length > 0) { info += `Dependencies: ${foundDeps.join(", ")}`; } return info; } function getFeatureName(id) { return features.find((f) => f.value === id)?.name || id; } import_commander.program.name("at3-kit").description("Upgrade existing projects to AT3 Stack").version("0.1.0").option("-d, --dry-run", "Show what would be changed without making changes").option("--no-install", "Skip installing dependencies").action(async (options) => { await main(); }); import_commander.program.command("add <feature>").description("Add a specific feature to your project").option("--no-install", "Skip installing dependencies").action(async (feature, options) => { const validFeatures = features.map((f) => f.value); if (!validFeatures.includes(feature)) { console.error(import_chalk2.default.red(`Unknown feature: ${feature}`)); console.log(import_chalk2.default.dim(`Valid features: ${validFeatures.join(", ")}`)); process.exit(1); } console.log(import_chalk2.default.blue(`Adding ${getFeatureName(feature)}...`)); }); import_commander.program.command("detect").description("Detect current project type and show available upgrades").action(async () => { const projectType = await detectProjectType(process.cwd()); const packageManager = await (0, import_detect_package_manager2.detect)().catch(() => "npm"); console.log(import_chalk2.default.green("Project Analysis:")); console.log(` Type: ${getProjectTypeLabel(projectType)}`); console.log(` Package Manager: ${packageManager}`); console.log("\n" + getProjectInfo(projectType)); }); import_commander.program.parse(); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { addAI, addI18n, addPWA, addSupabase, addTesting, analyzeProject, detectProjectType, getMissingFeatures, getRecommendations, isCompatible, program }); //# sourceMappingURL=index.cjs.map