initia-devaxis
Version:
CLI to create full-stack React projects with Next.js (REST) or RedwoodJS (GraphQL)
381 lines (366 loc) • 14.1 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createNextProject = createNextProject;
const chalk_1 = __importDefault(require("chalk"));
const child_process_1 = require("child_process");
const fs_1 = require("fs");
const promises_1 = require("fs/promises");
const os_1 = require("os");
const path_1 = require("path");
/**
* Crée un nouveau projet Next.js avec la configuration spécifiée
*/
/**
* Vérifie que le gestionnaire de paquets est disponible
*/
async function checkPackageManagerAvailability(packageManager) {
try {
// Exécuter la vérification dans le répertoire home pour éviter les conflits
await executeCommandSilent(packageManager, ["--version"], {
cwd: (0, os_1.homedir)(),
});
}
catch (error) {
throw new Error(`
${packageManager} is not installed or not available in PATH.
Please install ${packageManager} before creating a project:
- npm: comes with Node.js
- yarn: npm install -g yarn
- pnpm: npm install -g pnpm
`);
}
}
async function createNextProject(config) {
const packageManager = config.packageManager || "npm";
// Vérifier que le gestionnaire de paquets est disponible
await checkPackageManagerAvailability(packageManager);
const args = [
"create-next-app@latest",
config.name,
"--src-dir",
"--app",
"--import-alias",
"@/*",
];
if (config.typescript) {
args.push("--typescript");
}
else {
args.push("--javascript");
}
if (config.eslint) {
args.push("--eslint");
}
else {
args.push("--no-eslint");
}
if (config.tailwind) {
args.push("--tailwind");
}
else {
args.push("--no-tailwind");
}
if (packageManager === "yarn") {
args.push("--use-yarn");
}
else if (packageManager === "pnpm") {
args.push("--use-pnpm");
}
else {
args.push("--use-npm");
}
args.push("--skip-install");
try {
// Utiliser le bon exécuteur selon le gestionnaire de paquets
const executor = packageManager === "pnpm"
? "pnpm"
: packageManager === "yarn"
? "yarn"
: "npx";
const execArgs = packageManager === "pnpm"
? ["dlx", ...args]
: packageManager === "yarn"
? ["create", ...args.slice(1)] // remove "create-next-app@latest", keep the rest
: args;
await executeCommandSilent(executor, execArgs);
// Si pnpm est choisi, supprimer les fichiers yarn qui pourraient causer des conflits
if (packageManager === "pnpm") {
try {
const projectPath = (0, path_1.join)(process.cwd(), config.name);
// Supprimer les fichiers yarn qui pourraient interférer
const yarnFiles = [".yarnrc.yml", ".yarnrc", "yarn.lock"];
for (const file of yarnFiles) {
const filePath = (0, path_1.join)(projectPath, file);
if ((0, fs_1.existsSync)(filePath)) {
await (0, promises_1.unlink)(filePath);
}
}
}
catch (cleanupError) {
// Ignorer les erreurs de nettoyage
}
}
const installCommand = packageManager === "npm"
? "install"
: packageManager === "yarn"
? "install"
: "install";
await executeCommandSilent(packageManager, [installCommand], {
cwd: config.name,
});
// Configuration post-création
if (config.prettier) {
await setupPrettier(config.name, config.typescript, packageManager);
}
if (config.eslint) {
await enhanceESLintConfig(config.name, config.typescript, config.prettier);
}
await createReadme(config, packageManager);
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
// Afficher l'erreur dans un joli cadre rouge
const maxLength = 65;
const lines = errorMessage.match(/.{1,60}/g) || [errorMessage];
const errorBox = `
╭─── ❌ NEXT.JS PROJECT CREATION FAILED ───────────────────────────╮
│ │
${lines.map((line) => `│ ${line.padEnd(maxLength - 2)} │`).join("\n")}
│ │
╰───────────────────────────────────────────────────────────────────╯
`;
console.error(chalk_1.default.red(errorBox));
throw new Error(`Failed to create Next.js project: ${errorMessage}`);
}
}
/**
* Exécute une commande silencieusement mais capture les erreurs pour debugging
*/
function executeCommandSilent(command, args, options) {
return new Promise((resolve, reject) => {
const fullCommand = `${command} ${args.join(" ")}`;
const child = (0, child_process_1.spawn)(command, args, {
stdio: ["inherit", "pipe", "pipe"], // Capture stdout et stderr pour debugging
shell: true,
cwd: options?.cwd,
env: {
...process.env,
NPM_CONFIG_LOGLEVEL: "silent",
NPM_CONFIG_PROGRESS: "false",
NPM_CONFIG_AUDIT: "false",
NPM_CONFIG_FUND: "false",
NPM_CONFIG_UPDATE_NOTIFIER: "false",
// Variables pour forcer pnpm malgré la configuration yarn
PNPM_IGNORE_PNPMFILE: "true",
PNPM_SHAMEFULLY_HOIST: "true",
// Supprimer les variables yarn qui pourraient interférer
YARN_IGNORE_PATH: "1",
YARN_IGNORE_CWD: "1",
CI: "true",
DISABLE_OPENCOLLECTIVE: "true",
ADBLOCK: "true",
},
});
let stdout = "";
let stderr = "";
// Capturer stdout et stderr sans les afficher
child.stdout?.on("data", (data) => {
stdout += data.toString();
});
child.stderr?.on("data", (data) => {
stderr += data.toString();
});
const timeout = setTimeout(() => {
child.kill("SIGTERM");
reject(new Error("Command timed out"));
}, 300000); // 5 minutes
child.on("close", (code) => {
clearTimeout(timeout);
if (code === 0) {
resolve();
}
else {
// Créer un message d'erreur détaillé et bien visible
const errorBox = `
╭─── ❌ COMMAND FAILED ─────────────────────────────────────────────╮
│ │
│ Command: ${fullCommand}
│ Exit Code: ${code}
│ │
${stderr
? `│ Error Output: │
│ ${stderr
.split("\n")
.slice(0, 5)
.map((line) => ` ${line}`.padEnd(65))
.join("\n│")}\n│ │`
: ""}
${stdout && stdout.trim()
? `│ Standard Output: │
│ ${stdout
.split("\n")
.slice(0, 3)
.map((line) => ` ${line}`.padEnd(65))
.join("\n│")}\n│ │`
: ""}
╰───────────────────────────────────────────────────────────────────╯
`;
console.error(chalk_1.default.red(errorBox));
// Inclure l'erreur réelle dans le message d'exception
const realError = stderr || stdout || `Exit code ${code}`;
reject(new Error(realError.split("\n")[0])); // Première ligne de l'erreur réelle
}
});
child.on("error", (error) => {
clearTimeout(timeout);
console.error(`Spawn error for command: ${fullCommand}`, error);
reject(new Error(`Execution error: ${error.message}`));
});
});
}
/**
* Configure Prettier pour le projet Next.js
*/
async function setupPrettier(projectName, typescript, packageManager = "npm") {
const projectPath = (0, path_1.join)(process.cwd(), projectName);
// Configuration Prettier
const prettierConfig = {
semi: false,
singleQuote: true,
trailingComma: "es5",
tabWidth: 2,
printWidth: 80,
endOfLine: "lf",
};
// Fichier .prettierrc.json
await (0, promises_1.writeFile)((0, path_1.join)(projectPath, ".prettierrc.json"), JSON.stringify(prettierConfig, null, 2));
// Fichier .prettierignore
const prettierIgnore = `
node_modules
.next
out
dist
*.log
.DS_Store
.env*
public
*.lock
package-lock.json
yarn.lock
pnpm-lock.yaml
`.trim();
await (0, promises_1.writeFile)((0, path_1.join)(projectPath, ".prettierignore"), prettierIgnore);
// Installation des dépendances Prettier en mode silencieux
const devDeps = [
"prettier",
"eslint-config-prettier",
"eslint-plugin-prettier",
];
const devInstallArgs = packageManager === "npm"
? ["install", "--save-dev", ...devDeps]
: packageManager === "yarn"
? ["add", "--dev", ...devDeps]
: ["add", "--save-dev", ...devDeps];
await executeCommandSilent(packageManager, devInstallArgs, {
cwd: projectPath,
});
// Mise à jour du package.json avec les scripts
const packageJsonPath = (0, path_1.join)(projectPath, "package.json");
const packageJson = JSON.parse(await (0, promises_1.readFile)(packageJsonPath, "utf-8"));
packageJson.scripts = {
...packageJson.scripts,
format: "prettier --write .",
"format:check": "prettier --check .",
"lint:fix": "next lint --fix",
};
await (0, promises_1.writeFile)(packageJsonPath, JSON.stringify(packageJson, null, 2));
}
/**
* Améliore la configuration ESLint
*/
async function enhanceESLintConfig(projectName, typescript, prettier) {
const projectPath = (0, path_1.join)(process.cwd(), projectName);
const eslintConfigPath = (0, path_1.join)(projectPath, ".eslintrc.json");
let eslintConfig = {
extends: ["next/core-web-vitals", "eslint:recommended"],
rules: {
"react/no-unescaped-entities": "off",
"@next/next/no-page-custom-font": "off",
},
};
if (typescript) {
eslintConfig.extends.push("@typescript-eslint/recommended");
}
if (prettier) {
eslintConfig.extends.push("prettier");
eslintConfig.rules = {
...eslintConfig.rules,
"prettier/prettier": "error",
};
}
await (0, promises_1.writeFile)(eslintConfigPath, JSON.stringify(eslintConfig, null, 2));
}
/**
* Crée un README personnalisé pour le projet
*/
async function createReadme(config, packageManager = "npm") {
const projectPath = (0, path_1.join)(process.cwd(), config.name);
const readme = `# ${config.name}
This project was created with [INITIA CLI](https://github.com/your-username/initia) using **${config.framework}**.
## 🚀 Quick Start
Install dependencies:
\`\`\`bash
${packageManager} ${packageManager === "npm"
? "install"
: packageManager === "yarn"
? "install"
: "install"}
\`\`\`
Run the development server:
\`\`\`bash
${packageManager} run dev
\`\`\`
Open [http://localhost:3000](http://localhost:3000) in your browser.
## 📁 Project Structure
\`\`\`
${config.name}/
├── src/
│ ├── app/ # App Router (Next.js 13+)
│ ├── components/ # Reusable components
│ └── styles/ # CSS files
├── public/ # Static assets
└── ...
\`\`\`
## 🛠️ Available Scripts
- \`${packageManager} run dev\` - Start development server
- \`${packageManager} run build\` - Build for production
- \`${packageManager} run start\` - Start production server
- \`${packageManager} run lint\` - Run ESLint${config.prettier
? `\n- \`${packageManager} run format\` - Format code with Prettier\n- \`${packageManager} run format:check\` - Check formatting`
: ""}
## 🔧 Configuration
### Technologies used:
- ⚡ **Next.js** - React framework
${config.typescript
? "- 🔷 **TypeScript** - Static typing"
: "- 📜 **JavaScript** - Base language"}
${config.eslint ? "- 🔍 **ESLint** - Code linting" : ""}
${config.prettier ? "- 💅 **Prettier** - Code formatting" : ""}
${config.tailwind ? "- 🎨 **Tailwind CSS** - Utility-first CSS framework" : ""}
## 📚 Documentation
- [Next.js Documentation](https://nextjs.org/docs)
${config.typescript
? "- [TypeScript Documentation](https://www.typescriptlang.org/docs/)"
: ""}
${config.tailwind
? "- [Tailwind CSS Documentation](https://tailwindcss.com/docs)"
: ""}
---
✨ **Generated by INITIA CLI** - For an optimal development experience
`;
await (0, promises_1.writeFile)((0, path_1.join)(projectPath, "README.md"), readme);
}
//# sourceMappingURL=nextjs.js.map