实现
大约 3 分钟
实现
相关依赖
commander
:命令行处理工具chalk
:命令行输出美化工具(注意版本,安装4.0.0)inquirer
:命令行交互工具(注意版本,安装8.0.0)ora
:终端loading美化工具(注意版本,安装5.0.0)git-clone
:下载项目模板工具figlet
:终端生成艺术字fs-extra
:用来操作本地目录
具体步骤
一、初始化项目
- 新建文件夹, 例如:
firstScaffold
- 进入文件夹 打开 cmd 进入到
firstScaffold
文件夹中,使用npm init
初始化项目 firstScaffold
文件夹中 新建bin
文件夹- bin 文件夹下面创建 index.js 文件
- 在 index.js 的第一行添加代码
#!/usr/bin/env node
- 最终文件目录
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
注意
package.json
中的 name 不能与 npm 仓库中已有的包名重复,否则发布失败,显示 403- 如果是其他镜像,必须切换到 npm 官方镜像
- 查看镜像:
npm config get registry
- 切换到 npm 镜像:
npm config set registry https://registry.npmjs.org
- 切换到淘宝镜像:
npm config set registry https://registry.npmmirror.com
- 查看镜像: