一:什么是脚手架?
脚手架就是「为了减少重复性工作而做的工具」
二:怎么搭建脚手架?
2.1 搭建脚手架所需要的构建工具
- commander.js 用于读取和解析命令行参数取值
- Inquirer.js 交互式的读取命令行参数
- download-git-repo 可以用于下载git仓库代码
- ora 加载动画
2.2 最简单搭建过程
- 创建文件,npm初始化
mkdir chenjinqiu-cli
npm init
- 创建bin文件夹,下面创建index.js,作为主的文口,并在package.json添加入口
"bin": { "chenjinqiu-cli": "./bin/index.js" }
- 在执行命令的index.js文件下添加如下代码
#!/usr/bin/env node
console.log(1)
- 命令行操作
npm link
chenjinqiu-cli
以上就是最简单的搭建过程,但是我们的需求远远不止于此,我们可能会选择不同的模板,我们需要拉取github的代码,我们需要优雅的交互等等,下面就看看我们从零到一的整个过程
2.3 脚手架从零到一
- 使用command创建读取命令文件夹名称,通过inquirer,选择不同模板,config配置不同的下载地址,并配置不同创建目录下载,ora加载动画
const config = {
"chenjinqiu-component":{
url: "chenjnqiu/createComponent",
},
"chenjinqiu-reduxHooks":{
url: "chenjnqiu/chenjinqiu-reduxHooks",
}
}
const program = require('commander')
const inquirer = require('inquirer')
const download = require('download-git-repo')
const loading = ora('Loading');
program.command('create <app-name>')
.action((appName) => {
inquirer.prompt([{
type: 'input',
name: 'description',
message: '请输入项目描述信息:',
}, {
type: "list",
message: "请选择一个模板下载:",
name: "templateName",
choices: Object.keys(config)
}]).then(async (answers) => {
const { description, templateName } = answers
// 当前目录创建根据appName输入项目目录文件
const appDir = path.join(process.cwd(), appName);
try {
loading.start();
download(config[templateName], appDir, (err) => {
if(err) {
loading.fail();
} else {
loading.succeed();
}
})
} catch (error) {
console.log(error);
}
})
})
download-git-repo 的地址使用方式有多种,具体可以看官方文档www.npmjs.com/package/dow… , 试过{clone: true}的方式指定分支,总是会有问题,所以最后转而用最简单直接的文件目录方式,并把默认的main分支改为master
- 每次下载之前先检测文件是否存在,存在的话删除文件
const delDir = (path, isRoot) => {
return new Promise((resolve) => {
let files = [];
if (fs.existsSync(path)) {
files = fs.readdirSync(path);
files.forEach((file, index) => {
let curPath = path + "/" + file;
if (fs.statSync(curPath).isDirectory()) {
delDir(curPath); //递归删除文件夹
} else {
fs.unlinkSync(curPath); //删除文件
}
});
fs.rmdirSync(path);
}
if (isRoot && !fs.existsSync(path)) {
resolve({ status: true })
}
})
}
- 更改package.json的name和描述内容
updatePackage = (appDir, creditData) => {
return new Promise((resolve, reject) => {
const filename = path.join(appDir,'package.json');
fs.readFile(filename, 'utf8', (err, data) => {
if (!err) {
const packageJson = JSON.parse(data);
const newPackageJson = JSON.stringify({ ...packageJson, ...creditData}, null, '\t') // \t为了输出更好看
fs.writeFileSync(filename, newPackageJson)
}
})
})
};
- 自动安装,我们依然可以通过inquirer的交互方式,选择是npm安装或者yarn安装
const installType = {
yarn: "yarn install",
npm: "npm install"
}
inquirer.prompt({
type: "list",
message: "请选择安装方式:",
name: "installType",
choices: Object.keys(installType)
}).then(async (answers) => {
const { installType } = answers
const workerProcess = exec(
installType,
{
cwd: appDir,
},
(err) => {
if (err) {
console.log(err);
reject(err);
} else {
resolve(null);
}
}
);
workerProcess.stdout.on('data', function (data) {
console.log(data);
});
workerProcess.stderr.on('data', function (data) {
console.log(data);
});
})
添加自动启动,也是类似的方式处理
三:发npm 包
- 如果是非npm 镜像,切换成npm镜像
npm config set registry https://registry.npmjs.org
- 登录账号
npm login
- 发版
npm publish
以上就是整个cli创建过程,具体代码参考github.com/chenjnqiu/c…
参考文章地址: juejin.cn/post/717428… zhuanlan.zhihu.com/p/414966591