实现

SOBER大约 3 分钟

实现

具体代码仓库地址:https://gitee.com/spoony_Z/setting-scaffoldingopen in new window

相关依赖

  • commander:命令行处理工具
  • chalk:命令行输出美化工具(注意版本,安装4.0.0)
  • inquirer:命令行交互工具(注意版本,安装8.0.0)
  • ora:终端loading美化工具(注意版本,安装5.0.0)
  • git-clone:下载项目模板工具
  • figlet:终端生成艺术字
  • fs-extra:用来操作本地目录

具体步骤

一、初始化项目

  1. 新建文件夹, 例如:firstScaffold
  2. 进入文件夹 打开 cmd 进入到 firstScaffold 文件夹中,使用 npm init 初始化项目
  3. firstScaffold 文件夹中 新建 bin 文件夹
  4. bin 文件夹下面创建 index.js 文件
  5. 在 index.js 的第一行添加代码
    #!/usr/bin/env node
    
  6. 最终文件目录
    firstScaffold # 项目根目录(所有指令必须在这个目录运行)
    
        ├─ bin # 项目源码目录
        │   │
        │   └─ index.js # 脚手架的入口文件
    
        └─ package.json # 依赖文件
    

二、package.json

添加 "bin": "bin/index.js" 具体如下

{
  "name": "firstscaffold",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "bin": "bin/index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

三、建立连接

在项目根目录中执行

npm link

提示

npm link 可以帮助我们模拟包安装后的状态,它会在系统中做一个快捷方式映射,让本地的包就好像 install 过一样,可以直接使用。在MAC中,我们在终端可以直接敲命令,其实是在执行 /usr/local/bin 目录下的脚本,这个目录可以认为是我们的全局命令所在的地方。

如果开发的是 node 包,则执行的命令名和真实执行的文件入口,会通过项目的 package.json 里 bin 的配置来获取。

四、完整代码

bin/index.js

#!/usr/bin/env node

const { program } = require("commander");
const figlet = require("figlet");
const chalk = require("chalk");
const inquirer = require("inquirer");
const ora = require("ora");
const fs = require("fs-extra");
const path = require("path");
const gitClone = require("git-clone");

const projectList = {
    "168wangxiao-ui": "https://gitee.com/spoony_Z/168wangxiao-ui",
    "ts-tool-list": "https://gitee.com/spoony_Z/spoony-z",
    "168wangxiao-ui&ts": "tsxxxxx",
    "ts-tool-list&ts": "tsxxxxxx"
}

// 首行提示
program.name("firstscaffold").usage("<command> [option]");

// 版本号
program.version(`${require("../package.json").version}`);

/**
 * 命令
 */
// 创建项目
program
    .command('create <app-name>')
    .description('创建项目')
    .action(async (source, destination) => {
        /**
         * 创建项目的逻辑
         * 创建一个名字为 source 的文件夹,把我们模板项目的代码都放到这个文件夹下面
         * 1. 先判断有没有名字为 source 的文件夹
         * 2. 如果创建的文件夹名称已存在 询问用户是否覆盖
         * 3. 如果没有,直接创建
         */
        const targetPath = path.join(process.cwd(), source)
        // process.cwd() 获取当前执行命令的文件夹的路径
        if (fs.existsSync(targetPath)) {
            // 如果创建的文件夹名称已存在
            const answers = await inquirer.prompt([
                {
                    type: "confirm",
                    name: "overwrite",
                    message: `${source}文件夹已存在,是否覆盖?`,
                    default: false
                },
            ])
            if (answers.overwrite) {
                fs.remove(targetPath);
            } else {
                // 不覆盖,直接返回,重新起名,重新创建
                return false;
            }
        }

        // 如果没有,直接创建
        const res = await inquirer.prompt([
            {
                type: "list",
                name: "type",
                message: `${source}文件夹已存在,是否覆盖?`,
                choices: [
                    {
                        name: "ts-tool-list",
                        value: "ts-tool-list"
                    },
                    {
                        name: "168wangxiao-ui",
                        value: "168wangxiao-ui"
                    },
                ]
            },
            {
                type: "confirm",
                name: "ts",
                message: `是否使用TS?`,
                default: false
            },
        ]);
        // 这里为了测试,是否选择 ts 都会返回空
        const key = projectList[res.type + (res.ts ? "&ts" : "")]
        /**
         * gitClone 参数解读
         * key:项目仓库地址
         * source:项目名称(文件夹名称)
         * checkout:分支
         * function: 回调
         */
        const spinner = ora('创建中···').start();
        gitClone(key, source, { checkout: "master" }, function (res) {
            if (res) {
                spinner.fail("创建失败!");
            } else {
                spinner.succeed("创建成功!");
                fs.remove(path.join(targetPath, ".git"));
                console.log("Done, now run:");
                console.log(chalk.green(`\n cd ${source}`));
                console.log(chalk.green(` npm install`));
                console.log(chalk.green(` npm run dev\n`));
            }
        })
    });

// 给 help 添加提示
program.on('--help', function () {
    console.log(figlet.textSync('firstscaffold', {
        font: 'Ghost',
        horizontalLayout: 'default',
        verticalLayout: 'default',
        width: 100,
        whitespaceBreak: true
    }));
})

program.parse(process.argv);

五、发布脚手架

根目录下执行

npm publish

注意

  1. package.json 中的 name 不能与 npm 仓库中已有的包名重复,否则发布失败,显示 403
  2. 如果是其他镜像,必须切换到 npm 官方镜像
    • 查看镜像:npm config get registry
    • 切换到 npm 镜像:npm config set registry https://registry.npmjs.org
    • 切换到淘宝镜像:npm config set registry https://registry.npmmirror.com