UNPKG

@dao-style/cli

Version:

CLI tool for DAO Style projects - providing project scaffolding, template generation and dependency management

863 lines (844 loc) 29.4 kB
// src/commands/create.ts import { chmod, writeFile } from "node:fs/promises"; import * as path7 from "path"; import chalk2 from "chalk"; import * as fs from "fs-extra"; import ora from "ora"; // src/templates/base/index.ts import { fileURLToPath } from "node:url"; import * as path from "path"; // src/utils/npm.ts import pacote from "pacote"; async function getPackageVersions(packageName, options = {}) { const { registry = "https://registry.npmmirror.com" } = options; try { const manifest = await pacote.manifest(`${packageName}@latest`, { registry }); const versions = { latest: manifest.version }; if (options.includePrerelease) { const packument = await pacote.packument(packageName, { registry, fullMetadata: true // 确保获取完整的元数据 }); const allVersions = Object.keys(packument.versions).sort((a, b) => { const timeA = packument.time[a] || ""; const timeB = packument.time[b] || ""; return new Date(timeB).getTime() - new Date(timeA).getTime(); }); for (const version of allVersions) { if (version.includes("next") && !versions.next) { versions.next = version; } else if (version.includes("beta") && !versions.beta) { versions.beta = version; } else if (version.includes("alpha") && !versions.alpha) { versions.alpha = version; } if (versions.next && versions.beta && versions.alpha) { break; } } } return versions; } catch (error) { throw new Error(`Failed to fetch version for ${packageName}: ${error}`); } } async function getMultiplePackageVersions(packages, options) { const results = await Promise.all( packages.map((pkg) => getPackageVersions(pkg, options)) ); return packages.reduce( (acc, pkg, index) => { acc[pkg] = results[index]; return acc; }, {} ); } // src/templates/base/index.ts var currentDir = path.dirname(fileURLToPath(import.meta.url)); var getTemplateFilesPath = () => { if (currentDir.includes("dist")) { return path.resolve(currentDir, "../src/templates/base/files"); } return path.resolve(currentDir, "files"); }; var baseTemplate = { name: "base", path: getTemplateFilesPath(), postInstall: { files: [ { path: ".vscode/settings.json", sourcePath: "_vscode/settings.json", mergeStrategy: "json" }, { path: ".vscode/extensions.json", sourcePath: "_vscode/extensions.json", mergeStrategy: "json" } ] }, prompts: [ { type: "input", name: "port", message: "Enter the port number for the dev server (default: 8160):", default: "8160", validate: (input) => { const port = parseInt(input); if (Number.isNaN(port) || port < 8e3 || port > 9e3) { return "Please enter a valid port number between 8000 and 9000"; } return true; } }, { type: "checkbox", name: "packages", message: "Select DAO Style packages to include:", choices: [ { name: "@dao-style/core", checked: true }, { name: "@dao-style/extend", checked: true }, { name: "@dao-style/biz", checked: false } ] } ], async transform(data, prompt) { const packages = prompt?.packages || []; const versions = await getMultiplePackageVersions(packages); return { ...data, port: prompt?.port, packageJSON: { ...data.packageJSON, scripts: { ...data.packageJSON?.scripts, serve: "rsbuild dev", "serve:build": "rsbuild dev -m production", build: "rsbuild build", "test:unit": "vitest", "test:unit:ci": "vitest run --coverage --reporter=junit --outputFile=junit.xml" }, dependencies: { ...data.packageJSON?.dependencies, ...packages.reduce( (acc, pkg) => { acc[pkg] = `^${versions[pkg].latest}`; return acc; }, {} ) } } }; } }; // src/templates/ci/index.ts import { fileURLToPath as fileURLToPath2 } from "node:url"; import * as path2 from "path"; var currentDir2 = path2.dirname(fileURLToPath2(import.meta.url)); var getTemplateFilesPath2 = () => { if (currentDir2.includes("dist")) { return path2.resolve(currentDir2, "../src/templates/ci/files"); } return path2.resolve(currentDir2, "files"); }; var ciTemplate = { name: "ci", path: getTemplateFilesPath2(), transform(data) { return { ...data, packageJSON: { ...data.packageJSON, scripts: { ...data.packageJSON?.scripts, "change:version": "esno ./scripts/change-version.ts" } } }; } }; // src/templates/lint/index.ts import { fileURLToPath as fileURLToPath3 } from "node:url"; import * as path3 from "path"; var currentDir3 = path3.dirname(fileURLToPath3(import.meta.url)); var getTemplateFilesPath3 = () => { if (currentDir3.includes("dist")) { return path3.resolve(currentDir3, "../src/templates/lint/files"); } return path3.resolve(currentDir3, "files"); }; var lintTemplate = { name: "lint", path: getTemplateFilesPath3(), postInstall: { files: [ { path: ".vscode/settings.json", sourcePath: "_vscode/settings.json", mergeStrategy: "json" }, { path: ".vscode/extensions.json", sourcePath: "_vscode/extensions.json", mergeStrategy: "json" } ] }, transform(data) { return { ...data, packageJSON: { ...data.packageJSON, scripts: { ...data.packageJSON?.scripts, // support lint:fix prepare: "husky install", commit: "git-cz", lint: "pnpm run lint:style && pnpm run lint:es && pnpm run lint:type && pnpm run lint:doc", "lint:doc:fix": 'prettier "**/**.json" --write --ignore-unknown && textlint "**/*.json" --config .textlintrc.json --fix', "lint:doc": 'prettier "**/**.json" --check --ignore-unknown && textlint "**/*.json" --config .textlintrc.json', "lint:es": 'eslint "src/**/*.{js,jsx,ts,tsx,vue,json}" --config .eslintrc.js --max-warnings=0', "lint:fix": "pnpm run lint:style --fix && pnpm run lint:es --fix && pnpm run lint:doc:fix", "lint:style": 'stylelint "src/**/*.{vue,scss,css}" --config .stylelintrc.js', "lint:type": "vue-tsc --build --force" }, devDependencies: { ...data.packageJSON?.devDependencies, "@commitlint/cli": "^16.0.2", "@commitlint/config-conventional": "^16.0.0", "@commitlint/cz-commitlint": "^16.0.0", "@typescript-eslint/eslint-plugin": "~6.0.0", "@typescript-eslint/parser": "~6.0.0", "@vue/eslint-config-typescript": "~13.0.0", commitizen: "^4.2.4", eslint: "~8.56.0", "eslint-config-airbnb-base": "~15.0.0", "eslint-plugin-import": "2.26.0", "eslint-plugin-jsonc": "~2.7.0", "eslint-plugin-node": "~11.1.0", "eslint-plugin-promise": "~6.4.0", "eslint-plugin-vue": "~9.27.0", "lint-staged": "^15.2.7", husky: "^8.0.3", stylelint: "~14.5.0", "stylelint-config-recess-order": "~3.0.0", "stylelint-config-recommended": "~7.0.0", "stylelint-config-recommended-vue": "~1.1.0", "stylelint-config-sass-guidelines": "~9.0.1", textlint: "~13.4.1", "textlint-plugin-json": "~0.0.1", "textlint-rule-ja-space-between-half-and-full-width": "~2.3.0", "vue-eslint-parser": "~9.1.0" }, config: { ...data.packageJSON?.config, commitizen: { ...data.packageJSON?.config?.commitizen, path: "@commitlint/cz-commitlint" } }, gitHooks: { ...data.packageJSON?.gitHooks, "pre-commit": "lint-staged", "commit-msg": "commitlint -E GIT_PARAMS" } } }; } }; // src/utils/process-template.ts import { readFile } from "node:fs/promises"; import * as path6 from "path"; import chalk from "chalk"; import inquirer from "inquirer"; import { cloneDeep } from "lodash-es"; import { sortPackageJson as sortPackageJson2 } from "sort-package-json"; // src/utils/file.ts import { readdir } from "node:fs/promises"; import * as path4 from "path"; async function getAllFiles(dir) { const entries = await readdir(dir, { withFileTypes: true }); const files = await Promise.all( entries.map((entry) => { const res = path4.resolve(dir, entry.name); return entry.isDirectory() ? getAllFiles(res) : res; }) ); return files.flat(); } // src/utils/json.ts import { isArray, isObject, mergeWith } from "lodash-es"; function sortObjectKeys(obj) { if (Array.isArray(obj)) { return obj.map(sortObjectKeys); } if (isObject(obj)) { const sorted = {}; Object.keys(obj).sort().forEach((key) => { sorted[key] = sortObjectKeys(obj[key]); }); return sorted; } return obj; } function isJsonSubset(subset, superset) { if (subset === superset) return true; if (Array.isArray(subset) && Array.isArray(superset)) { if (subset.every((item) => !isObject(item)) && superset.every((item) => !isObject(item))) { return subset.every((item) => superset.includes(item)); } return subset.every( (subItem) => superset.some((superItem) => isJsonSubset(subItem, superItem)) ); } if (isObject(subset) && isObject(superset)) { return Object.keys(subset).every((key) => { const subValue = subset[key]; const superValue = superset[key]; return superValue !== void 0 && isJsonSubset(subValue, superValue); }); } return false; } function mergeJson(target, source) { return mergeWith({}, target, source, (objValue, srcValue) => { if (isArray(objValue) && isArray(srcValue)) { if (objValue.every((item) => !isObject(item)) && srcValue.every((item) => !isObject(item))) { return [.../* @__PURE__ */ new Set([...objValue, ...srcValue])]; } return srcValue; } }); } function tryParseJson(content) { try { return JSON.parse(content); } catch { return null; } } // src/utils/template.ts import path5 from "path"; import ejs from "ejs"; import { camelCase } from "lodash-es"; import { sortPackageJson } from "sort-package-json"; var capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1); var pascalCase = (str) => capitalize(camelCase(str)); function createTemplateData(data) { return { ...data, helpers: { raw: (options) => options.fn(), capitalize, camelCase, formatDate: (date) => date.toLocaleDateString(), pascalCase } }; } function shouldRenderTemplate(filePath) { const ext = path5.extname(filePath); return ext === ".ejs" || ext === ".html" || ext === ".json" || ext === ".js" || ext === ".ts" || ext === ".vue" || ext === ".md" || !ext; } function processPackageJson(content, data) { try { const pkg = JSON.parse(content); if (data.packageJSON?.scripts) { pkg.scripts = { ...pkg.scripts, ...data.packageJSON?.scripts }; } if (data.packageJSON?.dependencies) { pkg.dependencies = { ...pkg.dependencies, ...data.packageJSON?.dependencies }; } if (data.packageJSON?.devDependencies) { pkg.devDependencies = { ...pkg.devDependencies, ...data.packageJSON?.devDependencies }; } if (data.packageJSON?.gitHooks) { pkg.gitHooks = { ...pkg.gitHooks, ...data.packageJSON?.gitHooks }; } if (data.packageJSON?.["lint-staged"]) { pkg["lint-staged"] = { ...pkg["lint-staged"], ...data.packageJSON?.["lint-staged"] }; } if (data.packageJSON?.config) { pkg.config = { ...pkg.config, ...data.packageJSON?.config }; } return JSON.stringify(sortPackageJson(pkg), null, 2); } catch (error) { console.error("Error processing package.json:", error); return content; } } async function renderTemplate(content, data, templateDir, filePath) { if (!shouldRenderTemplate(filePath)) { return content; } try { const rendered = await ejs.render(content, createTemplateData(data), { filename: filePath, async: true }); if (path5.basename(filePath) === "package.json") { return processPackageJson(rendered, data); } return rendered; } catch (error) { console.error(`Error rendering template ${filePath}:`, error); throw error; } } function processFileName(fileName, data) { let processedName = fileName; if (processedName.startsWith("_")) { processedName = `.${processedName.slice(1)}`; } try { return ejs.render(processedName.replace(/_([^_]+)_/g, "<%= $1 %>"), data); } catch (error) { console.error(`Error processing filename ${fileName}:`, error); return processedName; } } // src/utils/process-template.ts async function processTemplateFile(filePath, templatePath, data) { const content = await readFile(filePath, "utf-8"); const relativePath = path6.relative(templatePath, filePath); const processedPath = processFileName(relativePath, data); return { path: processedPath, content, originalPath: filePath }; } async function processTemplate(template, data, existingFiles) { const transformedData = template.transform ? template.transform(data) : data; const files = await getAllFiles(template.path); const processedFiles = new Map(existingFiles); for (const file of files) { const processed = await processTemplateFile( file, template.path, transformedData ); if (processed.path.endsWith(".json")) { const existingFile = processedFiles.get(processed.path); if (existingFile) { const existingJson = tryParseJson(existingFile.content); const newJson = tryParseJson(processed.content); if (existingJson !== null && newJson !== null) { const mergedJson = mergeJson(existingJson, newJson); const isPackageJSON = processed.path === "package.json"; const sortedJson = isPackageJSON ? sortPackageJson2(mergedJson) : sortObjectKeys(mergedJson); processed.content = JSON.stringify(sortedJson, null, 2) + "\n"; } else { console.log(chalk.red(`Failed to merge JSON files: ${processed.path}`)); throw new Error(`Failed to merge JSON files: ${processed.path}`); } } } processedFiles.set(processed.path, processed); } return processedFiles; } async function renderProcessedTemplate(processed, data) { return renderTemplate( processed.content, data, processed.path, processed.originalPath ); } async function processTemplates(templates, data) { const processedFiles = /* @__PURE__ */ new Map(); for (const template of templates) { if (template.validate) { template.validate(data); } const files = await processTemplate(template, data, processedFiles); for (const [path10, file] of files) { processedFiles.set(path10, file); } } return Array.from(processedFiles.values()); } async function processTemplateData(templateData, templates) { const result = cloneDeep(templateData); for (const template of templates) { let promptAnswers; if (template.prompts) { const answers = await inquirer.prompt(template.prompts); promptAnswers = answers; } if (template.transform) { const transformedData = await template.transform(result, promptAnswers); Object.assign(result, transformedData); } } return result; } // src/commands/create.ts async function addExecutePermissions(projectPath) { const executableFiles = [ "scripts/cherry-pick.sh", "scripts/get-version.sh", ".husky/pre-commit" ]; for (const file of executableFiles) { try { const filePath = path7.join(projectPath, file); await chmod(filePath, "755"); } catch (error) { console.error(chalk2.red(`Failed to add execute permission to ${file}`), error); continue; } } } async function create(name) { const spinner = ora("Creating project..."); try { const packageName = `${name}-ui`; const versions = await getMultiplePackageVersions(["@dao-style/cli"]); const templateData = { name, packageName, packageJSON: { dependencies: { "@dao-style/cli": `^${versions["@dao-style/cli"].latest}` }, scripts: { postinstall: "dao post-install" } } }; const templates = [baseTemplate, lintTemplate, ciTemplate]; const mergedTemplateData = await processTemplateData(templateData, templates); const processedFiles = await processTemplates(templates, mergedTemplateData); spinner.text = "Creating project directory"; spinner.start(); const projectPath = path7.resolve(process.cwd(), packageName); if (await fs.pathExists(projectPath)) { spinner.fail(chalk2.red(`Directory ${packageName} already exists`)); process.exit(1); } await fs.ensureDir(projectPath); spinner.succeed(chalk2.green("Project directory created")); for (const file of processedFiles) { const targetPath = path7.join(projectPath, file.path); await fs.ensureDir(path7.dirname(targetPath)); const renderedContent = await renderProcessedTemplate(file, mergedTemplateData); await writeFile(targetPath, renderedContent); } await addExecutePermissions(projectPath); spinner.succeed(chalk2.green(`Successfully created project ${packageName}`)); console.log("\nNext steps:"); console.log(chalk2.cyan(` cd ${packageName}`)); console.log(chalk2.cyan(" pnpm install")); console.log(chalk2.cyan(" pnpm serve")); } catch (error) { spinner.fail(chalk2.red("Failed to create project")); console.error(error); process.exit(1); } } // src/commands/upgrade.ts import { exec } from "node:child_process"; import { existsSync } from "node:fs"; import { readFile as readFile2 } from "node:fs/promises"; import { promisify } from "node:util"; import * as path8 from "path"; import chalk3 from "chalk"; import { diffLines } from "diff"; import { outputFile } from "fs-extra"; import inquirer2 from "inquirer"; import ora2 from "ora"; import { sortPackageJson as sortPackageJson3 } from "sort-package-json"; var execAsync = promisify(exec); async function checkGitStatus() { try { const { stdout } = await execAsync("git status --porcelain"); return stdout.length === 0; } catch (error) { console.error(chalk3.red("Check git status failed:"), error); throw new Error("Not a git repository"); } } async function promptForContinue() { const { continue: shouldContinue } = await inquirer2.prompt([ { type: "confirm", name: "continue", message: "Working directory is not clean. Continue anyway?", default: false } ]); return shouldContinue; } async function selectTemplates() { const { templates } = await inquirer2.prompt([ { type: "checkbox", name: "templates", message: "Select templates to upgrade:", choices: [ { name: "Base Template", value: "base" }, { name: "CI/CD Template", value: "ci" }, { name: "Lint Template", value: "lint" } ] } ]); return templates; } async function mergeFile(processed, projectPath, content) { const projectFilePath = path8.join(projectPath, processed.path); let renderedContent = content; if (!existsSync(projectFilePath)) { await outputFile(projectFilePath, renderedContent); return; } let projectContent = await readFile2(projectFilePath, "utf-8"); if (processed.path.endsWith(".json")) { const targetJson = tryParseJson(projectContent); const sourceJson = tryParseJson(renderedContent); const mergedJson = mergeJson(targetJson, sourceJson); const isPackageJSON = processed.path === "package.json"; if (!targetJson || !sourceJson) { console.log(chalk3.red(`Failed to merge JSON files: ${processed.path}`)); throw new Error(`Failed to merge JSON files: ${processed.path}`); } const sortedJson = isPackageJSON ? sortPackageJson3(mergedJson) : sortObjectKeys(mergedJson); const sortedTargetJson = isPackageJSON ? sortPackageJson3(targetJson) : sortObjectKeys(targetJson); if (isJsonSubset(sortedJson, sortedTargetJson)) { return; } projectContent = JSON.stringify(sortedTargetJson, null, 2) + "\n"; renderedContent = JSON.stringify(sortedJson, null, 2) + "\n"; } const differences = diffLines(projectContent, renderedContent); if (differences.every((part) => !part.added && !part.removed)) { return; } let mergedContent = ""; let currentIndex = 0; while (currentIndex < differences.length) { const current = differences[currentIndex]; const next = differences[currentIndex + 1]; if (current.removed && next?.added) { mergedContent += "<<<<<<< HEAD\n"; mergedContent += current.value; mergedContent += "=======\n"; mergedContent += next.value; mergedContent += ">>>>>>> template\n"; currentIndex += 2; } else if (current.removed) { mergedContent += "<<<<<<< HEAD\n"; mergedContent += current.value; mergedContent += "=======\n>>>>>>> template\n"; currentIndex++; } else if (current.added) { mergedContent += "<<<<<<< HEAD\n=======\n"; mergedContent += current.value; mergedContent += ">>>>>>> template\n"; currentIndex++; } else { mergedContent += current.value; currentIndex++; } } return { path: projectFilePath, content: mergedContent }; } async function upgrade(options) { const templates = { base: baseTemplate, ci: ciTemplate, lint: lintTemplate }; try { const isClean = await checkGitStatus(); if (!isClean && !options.force) { console.log(chalk3.yellow("Warning: You have uncommitted changes.")); const shouldContinue = await promptForContinue(); if (!shouldContinue) { console.log(chalk3.blue("Upgrade cancelled.")); return; } } const selectedTemplateNames = await selectTemplates(); if (selectedTemplateNames.length === 0) { console.log(chalk3.blue("No templates selected. Upgrade cancelled.")); return; } const projectPath = process.cwd(); const packageJsonPath = path8.join(projectPath, "package.json"); const packageJson2 = JSON.parse(await readFile2(packageJsonPath, "utf-8")); const templateData = { name: packageJson2.name.replace(/-ui$/, ""), packageJSON: { ...packageJson2 } }; const selectedTemplates = selectedTemplateNames.filter((name) => templates[name]).map((name) => templates[name]); const mergedTemplateData = await processTemplateData(templateData, selectedTemplates); const processedFiles = await processTemplates(selectedTemplates, templateData); if (!processedFiles.find((file) => file.path === "package.json")) { processedFiles.push({ content: JSON.stringify(mergedTemplateData.packageJSON, null, 2), originalPath: "package.json", path: "package.json" }); } const packageJSONFileObj = processedFiles.find((file) => file.path === "package.json"); if (packageJSONFileObj) { const packageJSON = JSON.parse(packageJSONFileObj.content); if (packageJSON.version === "0.0.0") { packageJSON.version = "0.1.0"; } if (!packageJSON.dependencies) { packageJSON.dependencies = {}; } if (!packageJSON.scripts) { packageJSON.scripts = {}; } const versions = await getMultiplePackageVersions(["@dao-style/cli"]); packageJSON.dependencies["@dao-style/cli"] = `^${versions["@dao-style/cli"].latest}`; const existingPostinstall = packageJSON.scripts.postinstall || ""; const postInstallCmd = "dao post-install"; if (!existingPostinstall.includes(postInstallCmd)) { packageJSON.scripts.postinstall = existingPostinstall ? `${existingPostinstall} && ${postInstallCmd}` : postInstallCmd; } packageJSONFileObj.content = JSON.stringify(packageJSON, null, 2); } const upgradeSpinner = ora2(`Upgrading template...`).start(); const mergedFilesMap = /* @__PURE__ */ new Map(); for (const file of processedFiles) { const renderedContent = await renderProcessedTemplate(file, mergedTemplateData); const mergedInfo = await mergeFile(file, projectPath, renderedContent); if (mergedInfo) { mergedFilesMap.set(mergedInfo.path, mergedInfo.content); } } for (const [path10, content] of mergedFilesMap) { await outputFile(path10, content); console.log(chalk3.yellow(`Please resolve the conflicts manually. ${path10}`)); } upgradeSpinner.succeed(chalk3.green(`Successfully upgraded template`)); console.log(chalk3.yellow("\nPlease review the changes and resolve any conflicts.")); console.log(chalk3.yellow("Files with conflicts will contain markers: <<<<<<< HEAD, =======, and >>>>>>> template")); } catch (error) { console.error(chalk3.red("Upgrade failed:"), error); process.exit(1); } } // src/cli.ts import { readFileSync } from "node:fs"; import { dirname as dirname6, resolve as resolve6 } from "node:path"; import { fileURLToPath as fileURLToPath4 } from "node:url"; import { Command } from "commander"; // src/commands/post-install.ts import chalk4 from "chalk"; import ora3 from "ora"; // src/utils/post-install.ts import { readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises"; import * as path9 from "node:path"; import * as fs2 from "fs-extra"; import { cloneDeep as cloneDeep2, isArray as isArray2, isObject as isObject2, mergeWith as mergeWith2 } from "lodash-es"; function customizer(objValue, srcValue) { if (isArray2(objValue) && isArray2(srcValue)) { if (objValue.every((item) => !isObject2(item)) && srcValue.every((item) => !isObject2(item))) { return [.../* @__PURE__ */ new Set([...objValue, ...srcValue])]; } return srcValue; } } async function processPostInstallFile(file, template, projectPath) { const sourcePath = path9.join(template.path, file.sourcePath); const targetPath = path9.join(projectPath, file.path); const sourceContent = await readFile3(sourcePath, "utf-8"); return { targetPath, sourceContent, mergeStrategy: file.mergeStrategy }; } async function processPostInstallFiles(templates, projectPath) { const processedFiles = []; for (const template of templates) { if (template.postInstall?.files) { for (const file of template.postInstall.files) { const processed = await processPostInstallFile(file, template, projectPath); processedFiles.push(processed); } } } return processedFiles; } async function renderProcessedPostInstallFile(processed) { if (processed.mergeStrategy === "json") { let targetContent = "{}"; if (await fs2.pathExists(processed.targetPath)) { targetContent = await readFile3(processed.targetPath, "utf-8"); } const targetJson = JSON.parse(targetContent); const sourceJson = JSON.parse(processed.sourceContent); const mergedJson = mergeWith2( {}, cloneDeep2(targetJson), sourceJson, customizer ); return JSON.stringify(mergedJson, null, 2); } return processed.sourceContent; } async function writeProcessedPostInstallFile(processed, content) { if (processed.mergeStrategy !== "json" && await fs2.pathExists(processed.targetPath)) { return; } await fs2.ensureDir(path9.dirname(processed.targetPath)); await writeFile2(processed.targetPath, content); } // src/commands/post-install.ts async function postInstall() { const spinner = ora3("Running post-install tasks...").start(); try { const projectPath = process.cwd(); const templates = [baseTemplate, lintTemplate, ciTemplate]; const processedFiles = await processPostInstallFiles(templates, projectPath); for (const file of processedFiles) { const renderedContent = await renderProcessedPostInstallFile(file); await writeProcessedPostInstallFile(file, renderedContent); } spinner.succeed(chalk4.green("Post-install tasks completed successfully")); } catch (error) { spinner.fail(chalk4.red("Post-install tasks failed")); console.error(error); process.exit(1); } } // src/cli.ts var __filename = fileURLToPath4(import.meta.url); var __dirname = dirname6(__filename); var packageJson = JSON.parse( readFileSync(resolve6(__dirname, "../package.json"), "utf-8") ); function buildCLI() { const program = new Command(); program.name("dao").description("DAO Style CLI tool for project management").version(packageJson.version); program.command("create").description("Create a new project").argument("<name>", "project name").action(create); program.command("upgrade").description("Upgrade dependencies").action(upgrade); program.command("post-install").description("Run post-install tasks").action(postInstall); return program; } export { create, upgrade, buildCLI };