最佳实践初始化项目公用cli

package.json入口实现

js 复制代码
{
  "name": "xxx-cli",
  "version": "1.0.0",
  "description": "Command line interface for rapid project development",
  "bin": {
    "xxx-cli": "bin/cli.mjs"
  },
  "repository": {
    "type": "git",
    "url": "git+https://git地址"
  },
  "keywords": [
    "cli"
  ],
  "author": "",
  "license": "MIT",
  "bugs": {
    "url": "https://git地址/issues"
  },
  "engines": {
    "node": ">= 14.18.0"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "chalk": "^5.0.1",
    "child_process": "^1.0.2",
    "commander": "^9.4.0",
    "compressing": "^1.7.0",
    "figlet": "^1.5.2",
    "fs-extra": "^10.1.0",
    "inquirer": "^9.1.0",
    "ora": "^6.1.2",
    "semver": "^7.3.7",
    "shelljs": "^0.8.5"
  }
}

bin/cli.mjs内部实现

js 复制代码
#!/usr/bin/env node
// `#!/usr/bin/env node`:指定使用 Node.js 执行此脚本。

// 导入需要的模块
import { readFile } from 'fs/promises'; // 异步文件读取
import chalk from 'chalk'; // 控制台文本样式
import semverSatisfies from 'semver/functions/satisfies.js'; // 版本比较
import { program } from 'commander'; // 命令行参数解析
import create from '../lib/create.mjs'; // 创建项目命令

const CLI_NAME = 'xxx-cli';

// 读取 package.json 获取 Node 版本要求和 CLI 版本
const { engines: { node: requiredVersion }, version } = JSON.parse(
    await readFile(
        new URL('../package.json', import.meta.url)
    )
);

// 检查 Node 版本是否满足要求
function checkNodeVersion(wanted, id) {
    if (!semverSatisfies(process.version, wanted, { includePrerelease: true })) {
        console.log(chalk.red(
            'You are using Node ' + process.version + ', but this version of ' + id +
            ' requires Node ' + wanted + '.\nPlease upgrade your Node version.'
        ));
        process.exit(1); // 退出程序
    }
}

// 执行 Node 版本检查
checkNodeVersion(requiredVersion, CLI_NAME);

// 初始化 commander 程序
program
    .name(CLI_NAME) // CLI 工具名称
    .version(version); // CLI 工具版本

// 定义 create 命令
program
    .command('create <app-name>') // 命令名称和参数
    .description('create a new project powered by xxx-cli') // 命令描述
    .option("-f, --force", "overwrite target directory if it exists") // 命令选项
    .action((name, options) => { // 命令执行函数
        console.log(chalk.cyan(`Creating project: ${name}`));
        create(name, options); // 调用 create 函数
    });

// 解析命令行参数
program.parse(process.argv);

lib/create.mjs内部实现

js 复制代码
import ora from 'ora';
import chalk from 'chalk'
import fse from 'fs-extra'
import shelljs from 'shelljs'
import path from 'path'
import inquirer from 'inquirer'

const PROJECT_TYPES = [ 
  { name: 'pc', value: 'pc' }, 
  { name: 'h5', value: 'h5' }, 
]; 
const PROJECT_MAP = { 
  pc: 'pc最佳实践git地址', 
  h5: 'h5最佳实践git地址', 
};

async function create(projectName, options) {
  const progress = ora('创建中...')
  const targetDirectory = path.join(process.cwd(), projectName)
  try {
    // 判断是否存在git,不存在则提示并退出
    if (!shelljs.which('git')) {
      console(chalk.red('Sorry, xxx-cli requires git'));
      return
    }
    const isExist = await fse.pathExists(targetDirectory)
    // 判断目录下是否存在同名文件夹
    if (isExist) {
      // 如果有 --force 选项,则删除已存在的目录
      if (options.force) {
        await fse.remove(targetDirectory);
      } else {
        const { isOverwrite } = await new inquirer.prompt([
          {
            name: "isOverwrite", // 与返回值对应
            type: "list",
            message: "目标目录已存在,请选择操作:",
            choices: [
              { name: "Overwrite", value: true },
              { name: "Cancel", value: false },
            ],
          },
        ]);
        // 移除同名文件夹
        if (isOverwrite) {
          console.log('remove existing directory...')
          await fse.remove(targetDirectory);
        } else {
          return;
        }
      }
    }
    // 项目类型
    const { projectType } = await new inquirer.prompt([
      {
        name: "projectType", // 与返回值对应
        type: "list",
        message: "Please select project type:",
        choices: PROJECT_TYPES,
      },
    ]);

    // 安装依赖项目
    shelljs.exec(`git clone ${PROJECT_MAP[projectType]} ${projectName}`, async (code, stdout, stderr) => {
      if (code === 0) {
        progress.start()
        try {
          // 删除原有.git
          await fse.remove(path.join(process.cwd(), projectName, '.git'))
        } catch (error) {
          console.log(error)
        }
        progress.succeed()
        console.log(`\r\nSuccessfully created project ${chalk.cyan(projectName)}`);
        console.log(`\r\n  cd ${chalk.cyan(projectName)}`);
        console.log("  git init");
        console.log("  pnpm install");
          console.log("  pnpm dev");
      }
    })
  } catch (error) {
    progress.fail(chalk.red('创建项目失败:', error));
    console.log(error);
  }
}

export default create
相关推荐
来自星星的坤3 小时前
Vue 3中如何封装API请求:提升开发效率的最佳实践
前端·javascript·vue.js
vvilkim5 小时前
全面解析React内存泄漏:原因、解决方案与最佳实践
前端·javascript·react.js
vvilkim5 小时前
React批处理(Batching)更新机制深度解析
前端·javascript·react.js
Bayi·5 小时前
前端面试场景题
开发语言·前端·javascript
程序猿熊跃晖6 小时前
Vue中如何优雅地处理 `<el-dialog>` 的关闭事件
前端·javascript·vue.js
进取星辰6 小时前
12、高阶组件:魔法增幅器——React 19 HOC模式
前端·javascript·react.js
拉不动的猪6 小时前
前端低代码开发
前端·javascript·面试
程序员张36 小时前
Vue3集成sass
前端·css·sass
夜跑者6 小时前
axios 在请求拦截器中设置Content-Type无效问题
前端
知识分享小能手6 小时前
JavaScript学习教程,从入门到精通,Ajax与Node.js Web服务器开发全面指南(24)
开发语言·前端·javascript·学习·ajax·node.js·html5