From 9d2bd46e72c7b72eca3a449e09f376ab5be468b2 Mon Sep 17 00:00:00 2001 From: vaporvee Date: Wed, 3 Sep 2025 21:02:43 +0200 Subject: [PATCH] fixed it lol --- index.js | 526 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 334 insertions(+), 192 deletions(-) diff --git a/index.js b/index.js index ff5cb8d..45d7281 100755 --- a/index.js +++ b/index.js @@ -1,49 +1,60 @@ #!/usr/bin/env node -import * as p from '@clack/prompts'; -import color from 'picocolors'; -import fs from 'fs-extra'; -import path from 'path'; -import { spawn, spawnSync } from 'node:child_process'; +import * as p from "@clack/prompts"; +import color from "picocolors"; +import fs from "fs-extra"; +import path from "path"; +import { spawn, spawnSync } from "node:child_process"; +import { fileURLToPath } from "node:url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); async function runCommand(cmd, args, cwd) { return new Promise((resolve, reject) => { - const proc = spawn(cmd, args, { cwd, stdio: 'ignore' }); - proc.on('exit', (code) => (code === 0 ? resolve() : reject(new Error(`Command failed: ${cmd} ${args.join(' ')}`)))); + const proc = spawn(cmd, args, { cwd, stdio: "ignore" }); + proc.on("exit", (code) => + code === 0 + ? resolve() + : reject(new Error(`Command failed: ${cmd} ${args.join(" ")}`)), + ); }); } async function main() { - p.intro(`${color.bgRedBright(color.black(' ✨ Welcome to Lumify Sanity Template! ✨ '))}`); + p.intro( + `${color.bgRedBright(color.black(" ✨ Welcome to Lumify Sanity Template! ✨ "))}`, + ); const project = await p.group( { projectName: () => p.text({ - message: `${color.cyan('🔖 Enter your project name (for display):')}`, - placeholder: 'My Sanity Lumify page', - validate: (v) => (v.length < 2 ? `${color.red('❌ Too short!')}` : undefined), + message: `${color.cyan("🔖 Enter your project name (for display):")}`, + placeholder: "My Sanity Lumify page", + validate: (v) => + v.length < 2 ? `${color.red("❌ Too short!")}` : undefined, }), dirName: ({ results }) => { const defaultKebab = String(results.projectName) .trim() .toLowerCase() - .replace(/[^a-z0-9]+/g, '-') - .replace(/^-+|-+$/g, ''); + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-+|-+$/g, ""); return p.text({ - message: `${color.cyan('📂 Enter a directory name for your project:')}`, + message: `${color.cyan("📂 Enter a directory name for your project:")}`, placeholder: `./${defaultKebab}`, initialValue: defaultKebab, validate: (v) => { - if (v.length < 2) return `${color.red('❌ Too short!')}`; + if (v.length < 2) return `${color.red("❌ Too short!")}`; const kebab = String(v) .trim() .toLowerCase() - .replace(/[^a-z0-9]+/g, '-') - .replace(/^-+|-+$/g, ''); + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-+|-+$/g, ""); const root = path.resolve(process.cwd(), kebab); if (fs.existsSync(root)) { - return `${color.red('⚠️ Directory already exists:')} ${color.yellow(root)}`; + return `${color.red("⚠️ Directory already exists:")} ${color.yellow(root)}`; } return undefined; }, @@ -51,71 +62,73 @@ async function main() { }, shouldGit: () => p.confirm({ - message: `${color.green('🌱 Do you want to initialize a git repository?')}`, + message: `${color.green("🌱 Do you want to initialize a git repository?")}`, initialValue: true, }), packageManager: () => p.select({ - message: `${color.magenta('📦 Which package manager do you want to use?')}`, + message: `${color.magenta("📦 Which package manager do you want to use?")}`, options: [ - { value: 'bun', label: `${color.yellow('bun 🥯')}` }, - { value: 'npm', label: `${color.green('npm 📦')}` }, + { value: "bun", label: `${color.yellow("bun 🥯")}` }, + { value: "npm", label: `${color.green("npm 📦")}` }, ], - initialValue: 'bun', + initialValue: "bun", }), lowerCaseName: ({ results }) => { const defaultLowercase = String(results.dirName || results.projectName) .trim() .toLowerCase() - .replace(/[^a-z0-9]+/g, '-') - .replace(/^-+|-+$/g, ''); + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-+|-+$/g, ""); return p.text({ - message: `${color.cyan('📓 Enter a lowercase name for your project (e.g., used as')} ${color.yellow('.sanity.studio')}${color.cyan('):')}`, + message: `${color.cyan("📓 Enter a lowercase name for your project (e.g., used as")} ${color.yellow(".sanity.studio")}${color.cyan("):")}`, placeholder: `${defaultLowercase}`, initialValue: defaultLowercase, validate: (v) => { - if (v.length < 2) return `${color.red('❌ Too short!')}`; - if (!/^[a-z0-9-]+$/.test(v)) return `${color.red('❌ Only lowercase letters, numbers, and hyphens allowed!')}`; - if (v.startsWith('-') || v.endsWith('-')) return `${color.red('❌ Cannot start or end with hyphen!')}`; - if (v.includes('.sanity.studio')) return `${color.red('❌ Do not include .sanity.studio - it will be added automatically!')}`; + if (v.length < 2) return `${color.red("❌ Too short!")}`; + if (!/^[a-z0-9-]+$/.test(v)) + return `${color.red("❌ Only lowercase letters, numbers, and hyphens allowed!")}`; + if (v.startsWith("-") || v.endsWith("-")) + return `${color.red("❌ Cannot start or end with hyphen!")}`; + if (v.includes(".sanity.studio")) + return `${color.red("❌ Do not include .sanity.studio - it will be added automatically!")}`; }, }); }, faviconPath: () => p.text({ - message: `${color.cyan('🖼️ Path to favicon SVG (leave empty to skip):')}`, - placeholder: './myicon.svg', + message: `${color.cyan("🖼️ Path to favicon SVG (leave empty to skip):")}`, + placeholder: "./myicon.svg", }), extraDomain: () => p.text({ - message: `${color.cyan('🌍 Enter a domain this will run on later (leave empty to skip):')}`, - placeholder: 'youronlinedomain.com', + message: `${color.cyan("🌍 Enter a domain this will run on later (leave empty to skip):")}`, + placeholder: "youronlinedomain.com", validate: () => undefined, }), }, { onCancel: () => { - p.cancel('Operation cancelled.'); + p.cancel("Operation cancelled."); process.exit(0); }, - } + }, ); const kebabName = String(project.dirName) .trim() .toLowerCase() - .replace(/[^a-z0-9]+/g, '-') - .replace(/^-+|-+$/g, ''); + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-+|-+$/g, ""); const rootDir = path.join(process.cwd(), kebabName); - const pmx = project.packageManager === 'bun' ? 'bun x' : 'npx'; - const pm = project.packageManager === 'bun' ? 'bun' : 'npm'; - const studioDir = path.join(rootDir, 'apps', 'studio'); + const pm = project.packageManager === "bun" ? "bun" : "npm"; + const studioDir = path.join(rootDir, "apps", "studio"); await p.tasks([ { - title: `${color.yellow('📁 Copying template contents to root')}`, + title: `${color.yellow("📁 Copying template contents to root")}`, task: async () => { - const templateDir = path.join(process.cwd(), 'template'); + const templateDir = path.join(__dirname, "template"); // Ensure root directory exists with proper permissions await fs.ensureDir(rootDir); @@ -124,7 +137,7 @@ async function main() { await fs.copy(templateDir, rootDir, { overwrite: true, errorOnExist: false, - preserveTimestamps: false + preserveTimestamps: false, }); } catch (error) { throw new Error(`Failed to copy template files: ${error.message}`); @@ -140,160 +153,265 @@ async function main() { if (stat.isDirectory()) { await renameGitignoreFiles(itemPath); - } else if (item === 'gitignore') { - const gitignorePath = path.join(dir, '.gitignore'); + } else if (item === "gitignore") { + const gitignorePath = path.join(dir, ".gitignore"); await fs.move(itemPath, gitignorePath); } } } catch (error) { - throw new Error(`Failed to rename gitignore files: ${error.message}`); + throw new Error( + `Failed to rename gitignore files: ${error.message}`, + ); } } await renameGitignoreFiles(rootDir); - return 'Template copied!'; + return "Template copied!"; }, }, { title: `${color.green(`📦 Installing dependencies with ${project.packageManager}`)}`, task: async () => { - await runCommand(project.packageManager === 'bun' ? 'bun' : 'npm', ['install'], rootDir); - return 'Dependencies installed successfully'; + await runCommand( + project.packageManager === "bun" ? "bun" : "npm", + ["install"], + rootDir, + ); + return "Dependencies installed successfully"; }, }, - { - title: `${color.cyan('🔑 Checking Sanity login status')}`, - task: async () => { - await fs.ensureDir(studioDir); - let loggedIn = false; - let hasProjects = false; - try { - const result = await new Promise((res, rej) => { - let out = ''; - const c = spawn(pmx, ['sanity', 'projects', 'list'], { cwd: studioDir, stdio: ['ignore', 'pipe', 'pipe'] }); - c.stdout.on('data', (d) => (out += d)); - c.on('exit', (code) => (code === 0 ? res(out) : rej(new Error('fail')))); - }); - if (result.includes('id ')) { - loggedIn = true; - hasProjects = result.split('\n').slice(1).some((l) => l.trim()); - } - } catch { } - if (loggedIn) { - return hasProjects ? 'Already logged in to Sanity!' : 'Logged in to Sanity (no projects yet).'; - } else { - await runCommand(pmx, ['sanity', 'login'], studioDir); - return 'Sanity login complete!'; - } - }, - }, - { - title: `${color.magenta('🛠️ Creating Sanity project')}`, - task: async () => { - spawnSync(pmx, [ - "sanity", - "projects", - "create", - project.projectName - ], { - cwd: studioDir, - stdio: "inherit", - encoding: "utf-8" + ]); + + // Now check Sanity login status and prompt for provider if needed + console.log(`${color.cyan("🔑 Checking Sanity login status...")}`); + let loggedIn = false; + let hasProjects = false; + + try { + const result = await new Promise((res, rej) => { + let out = ""; + const c = spawn(pm, ["x", "sanity", "projects", "list"], { + cwd: studioDir, + stdio: ["ignore", "pipe", "pipe"], + }); + c.stdout.on("data", (d) => (out += d)); + c.on("exit", (code) => + code === 0 ? res(out) : rej(new Error("Not logged in")), + ); + }); + if (result.includes("id ")) { + loggedIn = true; + hasProjects = result + .split("\n") + .slice(1) + .some((l) => l.trim()); + } + } catch {} + + if (!loggedIn) { + // Exit clack UI completely for interactive commands + p.outro(`${color.yellow("⚠️ Authentication required...")}`); + + // Clear terminal and use clean console session + console.clear(); + console.log( + `\n${color.bgRedBright(color.black(" ✨ Lumify Sanity Template - Authentication ✨ "))}\n`, + ); + + if (!loggedIn) { + p.log.warn(`${color.yellow("⚠️ You need to login to Sanity first.")}`); + const provider = await p.select({ + message: `${color.cyan("🔐 Choose a provider to login to Sanity:")}`, + options: [ + { value: "sanity", label: "Sanity (email/password)" }, + { value: "google", label: "Google" }, + { value: "github", label: "GitHub" }, + ], + initialValue: "sanity", + }); + + p.log.info(`${color.cyan(`🚀 Logging in with provider: ${provider}`)}`); + + try { + await runCommand( + pm, + ["x", "sanity", "login", "--provider", provider], + studioDir, + ); + p.log.success(`${color.green("✅ Sanity login complete!")}`); + } catch (error) { + p.log.error(`${color.red("❌ Sanity login failed:")}`); + p.log.error(error.message); + process.exit(1); + } + } else { + p.log.success( + hasProjects + ? `${color.green("✅ Already logged in to Sanity!")}` + : `${color.green("✅ Logged in to Sanity (no projects yet).")}`, + ); + } + + // Clear terminal for clean project creation + // Create Sanity project (manual step to avoid interaction issues) + p.log.info(`${color.magenta("🛠️ Creating Sanity project...")}`); + p.log.warn( + `${color.yellow("⚠️ Due to terminal interaction issues, you need to run this command manually:")}`, + ); + p.log.message(`${color.cyan("cd " + studioDir)}`); + p.log.message( + `${color.cyan(`${pm} x sanity projects create "${project.projectName}"`)}`, + ); + + await p.confirm({ + message: `${color.green("Press Enter when the project is created successfully...")}`, + initialValue: true, + }); + + p.log.success(`${color.green("✅ Continuing with project configuration...")}`); + + const listProjects = spawnSync(pm, ["x", "sanity", "projects", "list"], { + cwd: studioDir, + stdio: ["ignore", "pipe", "pipe"], + encoding: "utf-8", + }); + + let sanityJson; + if (listProjects.stdout) { + try { + const lines = listProjects.stdout + .split(/\r?\n/) + .filter((l) => l.trim() && !l.startsWith("id") && !l.startsWith("─")); + const projectIds = lines + .map((line) => { + const match = line.match(/^([a-z0-9]{8})\b/); + return match ? match[1] : null; }) + .filter((id) => !!id); - const listProjects = spawnSync(pmx, [ - "sanity", - "projects", - "list" - ], { - cwd: studioDir, - stdio: ["ignore", "pipe", "pipe"], - encoding: "utf-8" - }) + if (projectIds.length > 0) { + sanityJson = { projectId: projectIds[0] }; + } + } catch (e) { + p.log.error( + `${color.red("❌ Could not parse Sanity project list output.")}`, + ); + process.exit(1); + } + } + if (!sanityJson || !sanityJson.projectId) { + console.error(`${color.red("❌ Sanity project creation failed.")}`); + process.exit(1); + } - let sanityJson; - if (listProjects.stdout) { - try { - const lines = listProjects.stdout.split(/\r?\n/).filter(l => l.trim() && !l.startsWith('id') && !l.startsWith('─')); - const projectIds = lines.map(line => { - const match = line.match(/^([a-z0-9]{8})\b/); - return match ? match[1] : null; - }).filter(id => !!id); + // Update connection file with project ID + const connPath = path.join(rootDir, "packages/sanity-connection/index.ts"); + if (!(await fs.pathExists(connPath))) { + console.error( + `${color.red("❌ Connection file not found at " + connPath)}`, + ); + process.exit(1); + } + let connFile = await fs.readFile(connPath, "utf8"); + if (!/projectId: "[^"]+"/.test(connFile)) { + console.error( + `${color.red("❌ Could not find projectId in connection file")}`, + ); + process.exit(1); + } + connFile = connFile.replace( + /projectId: "[^"]+"/, + `projectId: "${sanityJson.projectId}"`, + ); + await fs.writeFile(connPath, connFile); - if (projectIds.length > 0) { - sanityJson = { projectId: projectIds[0] }; - } - } catch (e) { - throw new Error("Could not parse Sanity project list output."); - } - } - if (!sanityJson || !sanityJson.projectId) { - throw new Error("Sanity project creation failed."); - } + // Update wrangler.jsonc with project name + const wranglerPath = path.join(rootDir, "apps/client/wrangler.jsonc"); + if (await fs.pathExists(wranglerPath)) { + let wranglerFile = await fs.readFile(wranglerPath, "utf8"); + wranglerFile = wranglerFile.replace( + /"name": "[^"]+"/, + `"name": "${project.lowerCaseName}"`, + ); + await fs.writeFile(wranglerPath, wranglerFile); + } - // Update connection file with project ID - const connPath = path.join(rootDir, "packages/sanity-connection/index.ts"); - if (!(await fs.pathExists(connPath))) { - throw new Error(`Connection file not found at ${connPath}`); - } - let connFile = await fs.readFile(connPath, "utf8"); - if (!/projectId: "[^"]+"/.test(connFile)) { - throw new Error(`Could not find projectId in ${connPath}. Please ensure the file contains a line like projectId: "${sanityJson.projectId}"`); - } - connFile = connFile.replace(/projectId: "[^"]+"/, `projectId: "${sanityJson.projectId}"`); - await fs.writeFile(connPath, connFile); + p.log.success( + `${color.green("✅ Sanity project created with ID: " + sanityJson.projectId)}`, + ); - // Update wrangler.jsonc with project name - const wranglerPath = path.join(rootDir, "apps/client/wrangler.jsonc"); - if (await fs.pathExists(wranglerPath)) { - let wranglerFile = await fs.readFile(wranglerPath, "utf8"); - wranglerFile = wranglerFile.replace(/"name": "[^"]+"/, `"name": "${project.lowerCaseName}"`); - await fs.writeFile(wranglerPath, wranglerFile); - } - - return `Sanity project created with ID: ${sanityJson.projectId}`; - }, - }, + await p.tasks([ { - title: `${color.blue('🗄️ Setting up production dataset for Sanity CMS')}`, + title: `${color.blue("🗄️ Setting up production dataset for Sanity CMS")}`, task: async () => { - if (!(await fs.pathExists(studioDir))) throw new Error(`Studio directory not found at ${studioDir}`); + if (!(await fs.pathExists(studioDir))) + throw new Error(`Studio directory not found at ${studioDir}`); try { - await runCommand(pmx, ['sanity', 'dataset', 'create', 'production'], studioDir); - return 'Production dataset created successfully'; + await runCommand( + pm, + ["x", "sanity", "dataset", "create", "production"], + studioDir, + ); + return "Production dataset created successfully"; } catch (error) { // Check if dataset already exists - if (error.message && error.message.includes('already exists')) { - return 'Production dataset already exists'; + if (error.message && error.message.includes("already exists")) { + return "Production dataset already exists"; } - throw new Error(`Failed to create production dataset: ${error.message}`); + throw new Error( + `Failed to create production dataset: ${error.message}`, + ); } }, }, { - title: `${color.green('🔐 Creating Sanity viewer token')}`, + title: `${color.green("🔐 Creating Sanity viewer token")}`, task: async () => { - if (!(await fs.pathExists(studioDir))) throw new Error(`Studio directory not found at ${studioDir}`); + if (!(await fs.pathExists(studioDir))) + throw new Error(`Studio directory not found at ${studioDir}`); const addOut = await new Promise((res, rej) => { - let buf = ''; + let buf = ""; const c = spawn( - pmx, - ['sanity', 'tokens', 'add', 'Main Viewer API Token', '--role=viewer', '-y', '--json'], - { cwd: studioDir, stdio: ['ignore', 'pipe', 'pipe'] } + pm, + [ + "x", + "sanity", + "tokens", + "add", + "Main Viewer API Token", + "--role=viewer", + "-y", + "--json", + ], + { cwd: studioDir, stdio: ["ignore", "pipe", "pipe"] }, + ); + c.stdout.on("data", (d) => (buf += d)); + c.on("exit", (code) => + code === 0 ? res(buf) : rej(new Error("token failed")), ); - c.stdout.on('data', (d) => (buf += d)); - c.on('exit', (code) => (code === 0 ? res(buf) : rej(new Error('token failed')))); }); const token = JSON.parse(addOut); - const connPath = path.join(rootDir, 'packages/sanity-connection/index.ts'); - let content = await fs.readFile(connPath, 'utf8'); + const connPath = path.join( + rootDir, + "packages/sanity-connection/index.ts", + ); + let content = await fs.readFile(connPath, "utf8"); content = content - .replace(/publicViewerToken: "[^"]+"/, `publicViewerToken: "${token.key}"`) - .replace(/studioHost: "[^"]+"/, `studioHost: "${project.lowerCaseName}"`) - .replace(/studioUrl: "[^"]+"/, `studioUrl: "https://${project.lowerCaseName}.sanity.studio"`); + .replace( + /publicViewerToken: "[^"]+"/, + `publicViewerToken: "${token.key}"`, + ) + .replace( + /studioHost: "[^"]+"/, + `studioHost: "${project.lowerCaseName}"`, + ) + .replace( + /studioUrl: "[^"]+"/, + `studioUrl: "https://${project.lowerCaseName}.sanity.studio"`, + ); await fs.writeFile(connPath, content); - return 'Viewer token created and configured!'; + return "Viewer token created and configured!"; }, }, ]); @@ -301,21 +419,33 @@ async function main() { if (project.faviconPath && project.faviconPath.trim()) { await p.tasks([ { - title: `${color.yellow('🌟 Generating favicon')}`, + title: `${color.yellow("🌟 Generating favicon")}`, task: async () => { - await runCommand(pm, ['create', "favicon", project.faviconPath, 'packages/ui/favicon'], rootDir); - await fs.copy(path.join(rootDir, 'packages/ui/favicon/'), path.join(rootDir, 'apps/client/public/'), { overwrite: true }); - await fs.copy(path.join(rootDir, 'packages/ui/favicon/'), path.join(rootDir, 'apps/studio/static/'), { overwrite: true }); - return 'Favicon generated and copied!'; + await runCommand( + pm, + ["create", "favicon", project.faviconPath, "packages/ui/favicon"], + rootDir, + ); + await fs.copy( + path.join(rootDir, "packages/ui/favicon/"), + path.join(rootDir, "apps/client/public/"), + { overwrite: true }, + ); + await fs.copy( + path.join(rootDir, "packages/ui/favicon/"), + path.join(rootDir, "apps/studio/static/"), + { overwrite: true }, + ); + return "Favicon generated and copied!"; }, }, ]); } const corsOrigins = [ - 'http://localhost:5173', - 'https://*.api.sanity.io', - 'wss://*.api.sanity.io', + "http://localhost:5173", + "https://*.api.sanity.io", + "wss://*.api.sanity.io", `https://${project.lowerCaseName}.sanity.studio`, ]; if (project.extraDomain && project.extraDomain.trim()) { @@ -323,27 +453,39 @@ async function main() { } await p.tasks( corsOrigins.map((origin) => ({ - title: `${color.cyan('🌐 Adding Sanity CORS origin:')} ${color.yellow(origin)}`, + title: `${color.cyan("🌐 Adding Sanity CORS origin:")} ${color.yellow(origin)}`, task: async () => { - const args = ['sanity', 'cors', 'add', origin, '--yes']; - if (origin === `https://${project.lowerCaseName}.sanity.studio`) args.push('--credentials'); - await runCommand(pmx, args, studioDir); - return `CORS added: ${origin}` + (args.includes('--credentials') ? ' (credentials allowed)' : ''); + const args = ["x", "sanity", "cors", "add", origin, "--yes"]; + if (origin === `https://${project.lowerCaseName}.sanity.studio`) + args.push("--credentials"); + await runCommand(pm, args, studioDir); + return ( + `CORS added: ${origin}` + + (args.includes("--credentials") ? " (credentials allowed)" : "") + ); }, - })) + })), ); if (project.shouldGit) { await p.tasks([ { - title: `${color.green('🌱 Setting up Git repository')}`, + title: `${color.green("🌱 Setting up Git repository")}`, task: async () => { - await runCommand('git', ['config', '--global', 'init.defaultBranch', 'main'], process.cwd()); - await runCommand('git', ['init'], rootDir); - await runCommand('git', ['add', '.'], rootDir); - await runCommand('git', ['commit', '-m', 'Initiated from Lumify Sanity template :rocket:'], rootDir); - await runCommand('git', ['branch', '-M', 'main'], rootDir); - return 'Git repository initialized with main branch'; + await runCommand( + "git", + ["config", "--global", "init.defaultBranch", "main"], + process.cwd(), + ); + await runCommand("git", ["init"], rootDir); + await runCommand("git", ["add", "."], rootDir); + await runCommand( + "git", + ["commit", "-m", "Initiated from Lumify Sanity template :rocket:"], + rootDir, + ); + await runCommand("git", ["branch", "-M", "main"], rootDir); + return "Git repository initialized with main branch"; }, }, ]); @@ -353,27 +495,27 @@ async function main() { const pmDeploy = `${project.packageManager} run deploy`; p.text({ - message: - `${color.bgGreen(color.black('✨ Setup complete! ✨'))}\n${color.dim('Need help?')} ${color.underline(color.cyan('https://duckduckgo.com/'))} ${color.yellow('💡')}`, + message: `${color.bgGreen(color.black("✨ Setup complete! ✨"))}\n${color.dim("Need help?")} ${color.underline(color.cyan("https://duckduckgo.com/"))} ${color.yellow("💡")}`, }); p.outro( - color.bold(color.cyan('🎉 All done! Your project is ready! 🎉')) + '\n\n' + - color.bold('Next steps:\n') + - ` ${color.bold(color.green('cd'))} ${color.cyan(kebabName)}\n` + - ` ${color.bold(color.green(pmRun))} ${color.dim('# Start your local dev server')} ${color.yellow('🖥️')}\n` + - ` ${color.bold(color.green(pmDeploy))} ${color.dim('# Deploy your Sanity Studio')} ${color.yellow('🚀')}\n\n` + - color.dim('Local development:\n') + - ` • App: ${color.cyan('http://localhost:5173')} ${color.yellow('🌐')}\n` + - ` • Studio: ${color.cyan('http://localhost:3333')} ${color.yellow('🛠️')}\n\n` + - color.dim('After deploying:\n') + - ` • Studio: ${color.cyan(`https://${project.lowerCaseName}.sanity.studio`)} ${color.yellow('✨')}` + color.bold(color.cyan("🎉 All done! Your project is ready! 🎉")) + + "\n\n" + + color.bold("Next steps:\n") + + ` ${color.bold(color.green("cd"))} ${color.cyan(kebabName)}\n` + + ` ${color.bold(color.green(pmRun))} ${color.dim("# Start your local dev server")} ${color.yellow("🖥️")}\n` + + ` ${color.bold(color.green(pmDeploy))} ${color.dim("# Deploy your Sanity Studio")} ${color.yellow("🚀")}\n\n` + + color.dim("Local development:\n") + + ` • App: ${color.cyan("http://localhost:5173")} ${color.yellow("🌐")}\n` + + ` • Studio: ${color.cyan("http://localhost:3333")} ${color.yellow("🛠️")}\n\n` + + color.dim("After deploying:\n") + + ` • Studio: ${color.cyan(`https://${project.lowerCaseName}.sanity.studio`)} ${color.yellow("✨")}`, ); process.exit(0); } main().catch((err) => { - p.log.error('An error occurred during setup:'); + p.log.error("An error occurred during setup:"); p.log.error(err.message || err); process.exit(1); });