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
JavaScript
// 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