UNPKG

create-next-pro-cli

Version:

Advanced Next.js project scaffolder with i18n, Tailwind, App Router and more.

1,049 lines (1,029 loc) 33.3 kB
#!/usr/bin/env node // src/index.ts import fs from "fs"; import os from "os"; import path from "path"; import prompts8 from "prompts"; import { fileURLToPath as fileURLToPath2 } from "url"; import { dirname, resolve } from "path"; // src/lib/addComponent.ts import { join as join2 } from "path"; import { mkdir, readFile as readFile2, writeFile, readdir } from "fs/promises"; import prompts from "prompts"; // src/lib/utils.ts import { readFile } from "fs/promises"; import { existsSync } from "fs"; import { join } from "path"; function capitalize(str) { return str.charAt(0).toUpperCase() + str.slice(1); } async function loadConfig() { const configPath = join(process.cwd(), "cnp.config.json"); if (!existsSync(configPath)) return null; try { const raw = await readFile(configPath, "utf-8"); return JSON.parse(raw); } catch { return null; } } function toFileName(key) { switch (key) { case "layout": return "layout.tsx"; case "page": return "page.tsx"; case "loading": return "loading.tsx"; case "not-found": return "not-found.tsx"; case "error": return "error.tsx"; case "global-error": return "global-error.tsx"; case "route": return "route.ts"; case "template": return "template.tsx"; case "default": return "default.tsx"; default: return `${key}.tsx`; } } // src/lib/addComponent.ts import { existsSync as existsSync2, statSync } from "fs"; async function addComponent(args) { let componentName = args[1]; let pageScope = null; let pageIndex = args.findIndex((arg) => arg === "-P" || arg === "--page"); if (pageIndex !== -1 && args[pageIndex + 1]) { pageScope = args[pageIndex + 1]; } let nestedPath = null; if (pageScope && pageScope.includes(".")) { nestedPath = join2(...pageScope.split(".")); } if (!componentName || componentName.startsWith("-")) { const response = await prompts.prompt({ type: "text", name: "componentName", message: "\u{1F9E9} Component name to add:", validate: (name) => name ? true : "Component name is required" }); componentName = response.componentName; } const config = await loadConfig(); if (!config) { console.error( "\u274C Configuration file cnp.config.json not found. Run this command from the project root." ); return; } const useI18n = !!config.useI18n; const componentNameUpper = capitalize(componentName); const templatePath = join2( new URL("..", import.meta.url).pathname, "templates", "Component" ); let messagesPath = null; if (useI18n) { messagesPath = join2(process.cwd(), "messages"); if (!existsSync2(messagesPath)) { console.error( "\u274C Messages directory missing. Ensure i18n was configured." ); return; } } let componentTargetPath; let translationKey; if (pageScope) { if (nestedPath) { componentTargetPath = join2(process.cwd(), "src", "ui", nestedPath); translationKey = pageScope; } else { componentTargetPath = join2(process.cwd(), "src", "ui", pageScope); translationKey = pageScope; } } else { componentTargetPath = join2(process.cwd(), "src", "ui", "_global"); translationKey = "_global_ui"; } if (!existsSync2(componentTargetPath)) { await mkdir(componentTargetPath, { recursive: true }); } const componentFile = join2(componentTargetPath, `${componentNameUpper}.tsx`); const templateComponentPath = join2(templatePath, "Component.tsx"); if (existsSync2(templateComponentPath)) { let content = await readFile2(templateComponentPath, "utf-8"); content = content.replace(/Component/g, componentNameUpper).replace(/componentPage/g, translationKey); await writeFile(componentFile, content); console.log(`\u{1F4C4} File created: ${componentFile}`); } else { console.error( "\u274C Template Component.tsx introuvable :", templateComponentPath ); } if (useI18n && messagesPath) { const entries = await readdir(messagesPath, { withFileTypes: true }); const langDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name); const jsonTemplate = join2(templatePath, "component.json"); if (!existsSync2(jsonTemplate)) { console.error("\u274C Template component.json not found:", jsonTemplate); return; } const jsonContent = await readFile2(jsonTemplate, "utf-8"); const parsed = JSON.parse(jsonContent); for (const locale of langDirs) { const localeDir = join2(messagesPath, locale); if (!existsSync2(localeDir) || !statSync(localeDir).isDirectory()) continue; let jsonTarget; if (pageScope) { jsonTarget = join2(messagesPath, locale, `${pageScope}.json`); } else { jsonTarget = join2(messagesPath, locale, `_global_ui.json`); } let current = {}; if (existsSync2(jsonTarget)) { const jsonFile = await readFile2(jsonTarget, "utf-8"); current = JSON.parse(jsonFile); } current[componentNameUpper] = parsed; await writeFile(jsonTarget, JSON.stringify(current, null, 2)); console.log(`\u{1F4C4} File updated: ${jsonTarget}`); } } else { console.log("\u2139\uFE0F Skipping translation entries; next-intl not enabled."); } console.log( `\u2705 Component "${componentNameUpper}" added ${pageScope ? `to page ${pageScope}` : "globally"}${useI18n ? " with localized messages" : ""}.` ); } // src/lib/addPage.ts import { join as join3 } from "path"; import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2, readdir as readdir2 } from "fs/promises"; import prompts2 from "prompts"; import { existsSync as existsSync3, statSync as statSync2 } from "fs"; async function addPage(args) { let pageName = args[1]; if (!pageName || pageName.startsWith("-")) { const response = await prompts2.prompt({ type: "text", name: "pageName", message: "\u{1F4DD} Page name to add:", validate: (name) => name ? true : "Page name is required" }); pageName = response.pageName; } let parentName = null; let childName = null; if (pageName.includes(".")) { [parentName, childName] = pageName.split("."); } let shortFlags = args.find((arg) => /^-[A-Za-z]+$/.test(arg)); let longFlags = new Set(args.filter((a) => a.startsWith("--"))); const flags = /* @__PURE__ */ new Set(); if (!shortFlags && Array.from(longFlags).length === 0) { shortFlags = "-LPl"; } if (shortFlags) { for (const char of shortFlags.slice(1)) { switch (char) { case "L": flags.add("layout"); break; case "P": flags.add("page"); break; case "l": flags.add("loading"); break; case "n": flags.add("not-found"); break; case "e": flags.add("error"); break; case "g": flags.add("global-error"); break; case "r": flags.add("route"); break; case "t": flags.add("template"); break; case "d": flags.add("default"); break; } } } for (const flag of [ "layout", "page", "loading", "not-found", "error", "global-error", "route", "template", "default" ]) { if (longFlags.has("--" + flag)) flags.add(flag); } const config = await loadConfig(); if (!config) { console.error( "\u274C Configuration file cnp.config.json not found. Run this command from the project root." ); return; } const useI18n = !!config.useI18n; const srcSegments = ["src", "app"]; if (useI18n) srcSegments.push("[locale]"); const srcPath = join3(process.cwd(), ...srcSegments); if (!existsSync3(srcPath)) { console.error(`\u274C Expected directory not found: ${srcPath}`); return; } let messagesPath = null; let locales = []; if (useI18n) { messagesPath = join3(process.cwd(), "messages"); if (!existsSync3(messagesPath)) { console.error( "\u274C Messages directory missing. Ensure i18n was configured." ); return; } const entries = await readdir2(messagesPath, { withFileTypes: true }); locales = entries.filter((e) => e.isDirectory()).map((e) => e.name); } const templatePath = join3( new URL("..", import.meta.url).pathname, "templates", "Page" ); let uiPageDir, localePagePath, jsonFileName; if (parentName && childName) { uiPageDir = join3(process.cwd(), "src", "ui", parentName, childName); localePagePath = join3(srcPath, parentName, childName); jsonFileName = parentName; } else { uiPageDir = join3(process.cwd(), "src", "ui", pageName); localePagePath = join3(srcPath, pageName); jsonFileName = pageName; } if (!existsSync3(uiPageDir)) { await mkdir2(uiPageDir, { recursive: true }); } const uiPageFile = join3(uiPageDir, "page-ui.tsx"); const uiPageTemplate = join3(templatePath, "page-ui.tsx"); if (existsSync3(uiPageTemplate)) { let uiContent = await readFile3(uiPageTemplate, "utf-8"); uiContent = uiContent.replace(/template/g, childName || pageName).replace(/Template/g, capitalize(childName || pageName)); await writeFile2(uiPageFile, uiContent); console.log(`\u{1F4C4} File created: ${uiPageFile}`); } else { console.warn( "\u26A0\uFE0F Missing template file: page-ui.tsx at path:", uiPageTemplate ); } if (!existsSync3(localePagePath)) { await mkdir2(localePagePath, { recursive: true }); } for (const flag of flags) { const filename = toFileName(flag); const src = join3(templatePath, filename); const dst = join3(localePagePath, filename); if (!existsSync3(src)) { console.warn(`\u26A0\uFE0F Missing template file: ${filename} at path: ${src}`); continue; } const content = await readFile3(src, "utf-8"); const replaced = content.replace(/template/g, childName || pageName).replace(/Template/g, capitalize(childName || pageName)); await writeFile2(dst, replaced); console.log(`\u{1F4C4} File created: ${dst}`); } if (useI18n && messagesPath) { const jsonTemplate = join3(templatePath, "page.json"); if (!existsSync3(jsonTemplate)) { console.warn("\u26A0\uFE0F Missing template: page.json at path:", jsonTemplate); } else { const content = await readFile3(jsonTemplate, "utf-8"); const replaced = content.replace(/template/g, childName || pageName).replace(/Template/g, capitalize(childName || pageName)); for (const locale of locales) { const localeDir = join3(messagesPath, locale); if (!existsSync3(localeDir) || !statSync2(localeDir).isDirectory()) continue; const jsonTarget = join3(messagesPath, locale, `${jsonFileName}.json`); let current = {}; if (existsSync3(jsonTarget)) { const jsonFile = await readFile3(jsonTarget, "utf-8"); try { current = JSON.parse(jsonFile); } catch { current = {}; } } if (parentName && childName) { current[childName] = JSON.parse(replaced); } else { current = JSON.parse(replaced); } await writeFile2(jsonTarget, JSON.stringify(current, null, 2)); console.log(`\u{1F4C4} File created: ${jsonTarget}`); } } } else { console.log("\u2139\uFE0F Skipping translation templates; next-intl not enabled."); } console.log( `\u2705 Page "${pageName}" with templates added${useI18n ? " for each locale" : ""}.` ); } // src/lib/rmPage.ts import { join as join4 } from "path"; import { writeFile as writeFile3, readdir as readdir3 } from "fs/promises"; import prompts3 from "prompts"; import { existsSync as existsSync4 } from "fs"; async function rmPage(args) { let pageName = args[1]; if (!pageName || pageName.startsWith("-")) { const response = await prompts3.prompt({ type: "text", name: "pageName", message: "\u{1F5D1}\uFE0F Page name to remove:", validate: (name) => name ? true : "Page name is required" }); pageName = response.pageName; } const messagesPath = join4(process.cwd(), "messages"); const entries = await readdir3(messagesPath, { withFileTypes: true }); const langDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name); for (const locale of langDirs) { const jsonTarget = join4(messagesPath, locale, `${pageName}.json`); if (existsSync4(jsonTarget)) { await writeFile3(jsonTarget, ""); await import("child_process").then( (cp3) => cp3.execSync(`rm -f '${jsonTarget}'`) ); console.log(`\u{1F5D1}\uFE0F Deleted: ${jsonTarget}`); } } const uiPageDir = join4(process.cwd(), "src", "ui", pageName); if (existsSync4(uiPageDir)) { await import("child_process").then( (cp3) => cp3.execSync(`rm -rf '${uiPageDir}'`) ); console.log(`\u{1F5D1}\uFE0F Deleted: ${uiPageDir}`); } const appLocaleDir = join4(process.cwd(), "src", "app", "[locale]", pageName); if (existsSync4(appLocaleDir)) { await import("child_process").then( (cp3) => cp3.execSync(`rm -rf '${appLocaleDir}'`) ); console.log(`\u{1F5D1}\uFE0F Deleted: ${appLocaleDir}`); } console.log(`\u2705 Page "${pageName}" deleted.`); } // src/lib/addLib.ts import { join as join5 } from "path"; import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile4 } from "fs/promises"; import prompts4 from "prompts"; import { existsSync as existsSync5 } from "fs"; async function addLib(args) { let libArg = args[1]; if (!libArg || libArg.startsWith("-")) { const response = await prompts4.prompt({ type: "text", name: "libArg", message: "\u{1F4E6} Lib name to add:", validate: (name) => name ? true : "Lib name is required" }); libArg = response.libArg; } let libName = libArg; let fileName = null; if (libArg.includes(".")) { [libName, fileName] = libArg.split("."); } const config = await loadConfig(); if (!config) { console.error( "\u274C Configuration file cnp.config.json not found. Run this command from the project root." ); return; } const libDir = join5(process.cwd(), "src", "lib", libName); if (!existsSync5(libDir)) { await mkdir3(libDir, { recursive: true }); } const templateDir = join5( new URL("..", import.meta.url).pathname, "templates", "Lib" ); const indexTemplate = join5(templateDir, "index.ts"); const fileTemplate = join5(templateDir, "item.ts"); const indexPath = join5(libDir, "index.ts"); if (!existsSync5(indexPath)) { if (existsSync5(indexTemplate)) { const content = await readFile4(indexTemplate, "utf-8"); await writeFile4(indexPath, content); } else { await writeFile4(indexPath, "export {}\n"); } console.log(`\u{1F4C4} File created: ${indexPath}`); } if (fileName) { const filePath = join5(libDir, `${fileName}.ts`); if (!existsSync5(filePath)) { if (existsSync5(fileTemplate)) { let content = await readFile4(fileTemplate, "utf-8"); content = content.replace(/template/g, fileName).replace(/Template/g, capitalize(fileName)); await writeFile4(filePath, content); } else { await writeFile4( filePath, `export function ${fileName}() { // TODO: implement } ` ); } console.log(`\u{1F4C4} File created: ${filePath}`); } let indexContent = await readFile4(indexPath, "utf-8"); const importLine = `import { ${fileName} } from "./${fileName}";`; const importRegex = new RegExp( `import\\s*{\\s*${fileName}\\s*}\\s*from\\s*"\\./${fileName}";` ); const exportRegex = /export\s*{([^}]*)}/m; const imports = []; const exportsSet = []; for (const line of indexContent.split("\n")) { if (line.startsWith("import")) { imports.push(line); } else if (line.startsWith("export")) { const match = line.match(exportRegex); if (match && match[1]) { exportsSet.push( ...match[1].split(",").map((s) => s.trim()).filter(Boolean) ); } } } if (!imports.some((l) => importRegex.test(l))) { imports.push(importLine); } if (!exportsSet.includes(fileName)) { exportsSet.push(fileName); } indexContent = imports.join("\n") + "\n\nexport { " + exportsSet.join(", ") + " };\n"; await writeFile4(indexPath, indexContent); console.log(`\u270F\uFE0F Updated index: ${indexPath}`); } console.log( `\u2705 Lib "${libName}"${fileName ? ` with module ${fileName}` : ""} added.` ); } // src/lib/addApi.ts import { join as join6 } from "path"; import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile5 } from "fs/promises"; import prompts5 from "prompts"; import { existsSync as existsSync6 } from "fs"; async function addApi(args) { let apiName = args[1]; if (!apiName || apiName.startsWith("-")) { const response = await prompts5.prompt({ type: "text", name: "apiName", message: "\u{1F50C} API route name to add:", validate: (name) => name ? true : "API route name is required" }); apiName = response.apiName; } const config = await loadConfig(); if (!config) { console.error( "\u274C Configuration file cnp.config.json not found. Run this command from the project root." ); return; } const apiDir = join6(process.cwd(), "src", "app", "api", apiName); if (!existsSync6(apiDir)) { await mkdir4(apiDir, { recursive: true }); } const templateDir = join6( new URL("..", import.meta.url).pathname, "templates", "Api" ); const routeTemplate = join6(templateDir, "route.ts"); const routePath = join6(apiDir, "route.ts"); if (!existsSync6(routePath)) { if (existsSync6(routeTemplate)) { let content = await readFile5(routeTemplate, "utf-8"); content = content.replace(/template/g, apiName); await writeFile5(routePath, content); } else { await writeFile5( routePath, `import { NextResponse } from "next/server"; export async function GET() { return NextResponse.json({ message: "Hello from ${apiName}" }); } ` ); } console.log(`\u{1F4C4} File created: ${routePath}`); } else { console.log(`\u2139\uFE0F File already exists: ${routePath}`); } console.log(`\u2705 API route "${apiName}" added.`); } // src/scaffold.ts import { cp, mkdir as mkdir5, rm, writeFile as writeFile6, readFile as readFile6 } from "fs/promises"; import { join as join7 } from "path"; import { existsSync as existsSync7 } from "fs"; import { fileURLToPath } from "url"; // src/lib/helper/consoleColor.ts var RED = "\x1B[31m"; var GREEN = "\x1B[32m"; var CYAN = "\x1B[36m"; var RESET = "\x1B[0m"; function red(text) { return RED + text + RESET; } function green(text) { return GREEN + text + RESET; } function cyan(text) { return CYAN + text + RESET; } // src/scaffold.ts async function scaffoldProject(options) { const targetPath = join7(process.cwd(), options.projectName); const __dirname2 = new URL(".", import.meta.url); const templatePath = join7( fileURLToPath(__dirname2), "..", "templates", "Projects", "default" ); if (existsSync7(targetPath)) { if (options.force) { console.warn("\u26A0\uFE0F Target directory already exists, removing..."); await rm(targetPath, { recursive: true, force: true }); } else { console.error( red("[X] Target directory already exists. Use --force to overwrite.") ); process.exit(1); } } try { console.log("Creating project directory..."); await mkdir5(targetPath, { recursive: true }); console.log("Copying files from template..."); await cp(templatePath, targetPath, { recursive: true }); const pkgPath = join7(targetPath, "package.json"); if (existsSync7(pkgPath)) { const pkg = JSON.parse(await readFile6(pkgPath, "utf-8")); pkg.dependencies = pkg.dependencies || {}; if (options.useI18n) { pkg.dependencies["next-intl"] = pkg.dependencies["next-intl"] || "^4.3.5"; } await writeFile6(pkgPath, JSON.stringify(pkg, null, 2)); } await writeFile6( join7(targetPath, "cnp.config.json"), JSON.stringify(options, null, 2) ); console.log("Project setup complete!"); console.log(""); console.log("To get started:"); console.log(" " + green(`cd ${options.projectName}`)); console.log(""); console.log( "Then install dependencies and launch the dev server with your preferred tool:" ); console.log(" " + green(`bun install && bun dev`)); console.log(" " + green(`npm install && npm run dev`)); console.log(" " + green(`pnpm install && pnpm run dev`)); console.log(""); console.log("Documentation and examples can be found at:"); console.log( " " + cyan("https://github.com/Rising-Corporation/create-next-pro-cli") ); console.log( "_-`'-_-'`_`'-_-'`_`'-_-'`_`'-_-'`_`'-_-'`_`'-_-'`_`'-_-'`_`'-_-'`-_" ); } catch (err) { console.error(red("[X] Error during project creation:"), err); process.exit(1); } } // src/lib/createProject.ts async function createProject(nameArg, force) { const response = { projectName: nameArg, useTypescript: true, useEslint: true, useTailwind: true, useSrcDir: true, useTurbopack: true, useI18n: true, customAlias: true, importAlias: "@/*", force }; console.log(`Creating project "${response.projectName}"...`); await scaffoldProject(response); } // src/lib/createProjectWithPrompt.ts import prompts6 from "prompts"; async function createProjectWithPrompt() { const response = await prompts6.prompt([ { type: "text", name: "projectName", message: "Project name:", initial: "my-next-app" }, { type: "toggle", name: "useTypescript", message: "\u2714 Use TypeScript?", initial: true, active: "Yes", inactive: "No" }, { type: "toggle", name: "useEslint", message: "\u2714 Use ESLint?", initial: true, active: "Yes", inactive: "No" }, { type: "toggle", name: "useTailwind", message: "\u2714 Use Tailwind CSS?", initial: true, active: "Yes", inactive: "No" }, { type: "toggle", name: "useSrcDir", message: "\u2714 Use `src/` directory?", initial: true, active: "Yes", inactive: "No" }, { type: "toggle", name: "useTurbopack", message: "\u2714 Use Turbopack for `next dev`?", initial: true, active: "Yes", inactive: "No" }, { type: "toggle", name: "useI18n", message: "\u2714 Use i18n with next-intl for translations?", initial: true, active: "Yes", inactive: "No" }, { type: "toggle", name: "customAlias", message: "\u2714 Customize import alias (`@/*` by default)?", initial: true, active: "Yes", inactive: "No" }, { type: (prev) => prev ? "text" : null, name: "importAlias", message: "\u2714 What import alias would you like?", initial: "@core/*" } ]); console.log("\nYour choices:"); console.log(response); await scaffoldProject(response); } // src/lib/addLanguage.ts import { join as join8 } from "path"; import { existsSync as existsSync8 } from "fs"; import { cp as cp2, readFile as readFile7, writeFile as writeFile7 } from "fs/promises"; import prompts7 from "prompts"; function generateLocales() { const dn = new Intl.DisplayNames(["en"], { type: "language" }); const locales = []; for (let i = 0; i < 26; i++) { for (let j = 0; j < 26; j++) { const code = String.fromCharCode(97 + i) + String.fromCharCode(97 + j); try { const name = dn.of(code); if (name && name.toLowerCase() !== code) { locales.push(code); } } catch { } } } return locales.sort(); } async function addLanguage(args) { const config = await loadConfig(); if (!config?.useI18n) { console.error("\u274C i18n is not enabled in this project."); return; } const messagesPath = join8(process.cwd(), "messages"); if (!existsSync8(messagesPath)) { console.error("\u274C Messages directory missing. Ensure i18n was configured."); return; } const available = generateLocales(); let locale = args[1]; if (!locale || !available.includes(locale)) { const response = await prompts7({ type: "autocomplete", name: "locale", message: "\u{1F310} Locale to add:", choices: available.map((l) => ({ title: l, value: l })) }); locale = response.locale; } if (!locale) return; if (existsSync8(join8(messagesPath, locale))) { console.error(`\u274C Locale ${locale} already exists.`); return; } const routingFile = join8(process.cwd(), "src", "lib", "i18n", "routing.ts"); if (!existsSync8(routingFile)) { console.error("\u274C routing.ts not found. Are you in project root?"); return; } const routingContent = await readFile7(routingFile, "utf-8"); const defaultMatch = routingContent.match(/defaultLocale:\s*"([^"]+)"/); const defaultLocale = defaultMatch ? defaultMatch[1] : null; if (!defaultLocale || !existsSync8(join8(messagesPath, defaultLocale))) { console.error("\u274C Default locale not found."); return; } await cp2(join8(messagesPath, defaultLocale), join8(messagesPath, locale), { recursive: true }); console.log(`\u{1F4C4} Directory created: ${join8(messagesPath, locale)}`); const localesMatch = routingContent.match(/locales:\s*\[([^\]]*)\]/); if (localesMatch) { const localesArr = localesMatch[1].split(",").map((s) => s.trim().replace(/["']/g, "")).filter(Boolean); if (!localesArr.includes(locale)) { localesArr.push(locale); const newLocales = `locales: [${localesArr.map((l) => `"${l}"`).join(", ")}]`; const newContent = routingContent.replace(/locales:\s*\[[^\]]*\]/, newLocales); await writeFile7(routingFile, newContent); console.log(`\u{1F4C4} File updated: ${routingFile}`); } } console.log( `\u2705 Locale "${locale}" added and copied from default locale "${defaultLocale}".` ); } // src/lib/addText.ts import { join as join9 } from "path"; import { existsSync as existsSync9 } from "fs"; import { readFile as readFile8, writeFile as writeFile8, readdir as readdir4 } from "fs/promises"; function defaultText(key) { return key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()); } async function addText(args) { const pathArg = args[1]; if (!pathArg) { console.error("\u274C Dot path parameter is required."); return; } const providedText = args.slice(2).join(" "); const config = await loadConfig(); if (!config?.useI18n) { console.error("\u274C i18n is not enabled in this project."); return; } const messagesPath = join9(process.cwd(), "messages"); if (!existsSync9(messagesPath)) { console.error("\u274C Messages directory missing. Ensure i18n was configured."); return; } const entries = await readdir4(messagesPath, { withFileTypes: true }); const locales = entries.filter((e) => e.isDirectory()).map((e) => e.name); const [fileName, ...segments] = pathArg.split("."); if (!fileName || segments.length === 0) { console.error("\u274C Invalid dot path provided."); return; } const finalKey = segments[segments.length - 1]; const text = providedText || defaultText(finalKey); for (const locale of locales) { const filePath = join9(messagesPath, locale, `${fileName}.json`); let data = {}; if (existsSync9(filePath)) { const raw = await readFile8(filePath, "utf-8"); try { data = JSON.parse(raw); } catch { } } let cursor = data; for (let i = 0; i < segments.length - 1; i++) { const seg = segments[i]; if (!cursor[seg]) cursor[seg] = {}; cursor = cursor[seg]; } cursor[finalKey] = text; await writeFile8(filePath, JSON.stringify(data, null, 2)); console.log(`\u{1F4C4} File updated: ${filePath}`); } console.log(`\u2705 Text added at path "${pathArg}" with value "${text}".`); } // src/index.ts var CONFIG_DIR = process.env.XDG_CONFIG_HOME ? path.join(process.env.XDG_CONFIG_HOME, "create-next-pro") : path.join(os.homedir(), ".config", "create-next-pro"); var CONFIG_FILE = path.join(CONFIG_DIR, "config.json"); function readCfg() { try { return JSON.parse(fs.readFileSync(CONFIG_FILE, "utf8")); } catch { return null; } } function writeCfg(cfg) { fs.mkdirSync(CONFIG_DIR, { recursive: true }); fs.writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2)); } function rcFile(shell) { return path.join(os.homedir(), shell === "zsh" ? ".zshrc" : ".bashrc"); } function ensureLineInRc(file, line) { try { const cur = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : ""; if (!cur.includes(line)) fs.appendFileSync(file, ` ${line} `); } catch { } } async function installCompletion(shell) { const __dirname2 = path.dirname(fileURLToPath2(import.meta.url)); const completionSrc = path.resolve( __dirname2, "../create-next-pro-completion.sh" ); const completionDst = path.join(CONFIG_DIR, "completion.sh"); fs.mkdirSync(CONFIG_DIR, { recursive: true }); fs.copyFileSync(completionSrc, completionDst); ensureLineInRc(rcFile(shell), `source "${completionDst}"`); } var __filename = fileURLToPath2(import.meta.url); var __dirname = dirname(__filename); var packageJsonPath = resolve(__dirname, "../package.json"); var packageJson = fs.readFileSync(packageJsonPath, "utf8"); async function onboarding() { const pkg = JSON.parse(packageJson); console.log(`\u{1F680} Welcome to create-next-pro v${pkg.version} `); const res = await prompts8( [ { type: "select", name: "shell", message: "Which shell do you use?", choices: [ { title: "zsh", value: "zsh" }, { title: "bash", value: "bash" } ], initial: (os.userInfo().shell || "").includes("zsh") ? 0 : 1 }, { type: "toggle", name: "completion", message: "Install autocompletion?", initial: true, active: "Yes", inactive: "No" } ], { onCancel: () => process.exit(1) } ); const cfg = { version: 1, shell: res.shell, completionInstalled: !!res.completion, createdAt: (/* @__PURE__ */ new Date()).toISOString(), updatedAt: (/* @__PURE__ */ new Date()).toISOString() }; if (cfg.completionInstalled) await installCompletion(cfg.shell); writeCfg(cfg); console.log("\n\u2705 Configuration saved."); console.log("you can now use the CLI ! ex : "); console.log(" Without prompt (will change in future) :"); console.log(" create-next-pro my-next-project"); console.log(" With prompt :"); console.log(" create-next-pro"); console.log( "For more information, visit: https://github.com/Rising-Corporation/create-next-pro-cli" ); console.log("Happy coding! \u{1F389}"); return cfg; } function showHelp() { console.log(`create-next-pro Usage: create-next-pro <project-name> [--force] create-next-pro addpage [options] create-next-pro addcomponent [options] create-next-pro addlib [name] create-next-pro addapi [name] create-next-pro addlanguage [locale] create-next-pro addtext <path> [text] create-next-pro rmpage [options] Options: --help Show this help message --reconfigure Run the configuration assistant again `); } function showVersion() { const pkg = JSON.parse(packageJson); console.log(`v${pkg.version}`); } async function main() { let args; if (typeof Bun !== "undefined") { args = Bun.argv.slice(2); } else if (typeof process !== "undefined" && process.argv) { args = process.argv.slice(2); } else { args = []; } if (args.includes("--help")) return showHelp(); if (args.includes("--version") || args.includes("-v")) return showVersion(); if (args.includes("--reconfigure") || !readCfg()) { await onboarding(); return; } const force = args.includes("--force"); if (args[0] === "addpage" && args.length === 1) { args.push("-LPl"); } if (args[0] === "addcomponent") { addComponent(args); return; } if (args[0] === "addpage") { addPage(args); return; } if (args[0] === "addlib") { addLib(args); return; } if (args[0] === "addapi") { addApi(args); return; } if (args[0] === "addlanguage") { await addLanguage(args); return; } if (args[0] === "addtext") { await addText(args); return; } if (args[0] === "rmpage") { rmPage(args); return; } const nameArg = args.find((arg) => !arg.startsWith("--")); if (nameArg) { createProject(nameArg, force); return; } await createProjectWithPrompt(); } // bin.node.ts main(); //# sourceMappingURL=bin.node.js.map