初始化脚手架相关文件
- 在根目录下初始化一个 package.json
npm init -y
- 新建一个 bin 文件,bin 内新建一个 main 文件(不带任何后缀)
#! /usr/bin/env node
console.log('脚手架工具入口文件...')
main 是base脚本,需要在第一行通过 #! /usr/bin/env node 指定脚本的解释语言,我们使用的语言是node。
- 在 package.json 中添加配置
"bin": {
"junxu-cli": "bin/main"
}
- 在终端通过执行命令
junxu-cli正常打印出 “脚手架工具入口文件...”
配置 options
我们需要用到一个插件 commander, 安装一下:
npm install commander
- 在 main 文件中添加如下配置代码
const program = require('commander')
const version = require('../package.json').version
program
.name('junxu-cli')
.usage(`<command> [option]`)
.version(version)
program.parse(process.argv);
增加提示
为了增加提示,我们需要引入插件chalk:
npm install chalk
chalk是用来美化字体的插件,也就是改变字体, 背景颜色等等。
执行命令: junxu-cli --help
配置脚手架命令(command)
脚手架的核心是命令,例如vue create xxx,所以,我们也需要实现自己的脚手架命令(毕竟开发脚手架就是为了这个)。
我们现在明确这个脚手架的需求是:使用这个脚手架,可以选择拉取vue2或者vue3的模版代码(当然,以后你们的脚手架可能是拉取公司的基础框架代码或者某些模块)。
-
添加命令模块
现在,我们需要有一个模块(比如create模块),来完成创建指令。
program
.command('create <project-name>')
.description('create a new project')
.option('-f, --force', 'overwrite target directory if it exists')
.action((projectName, options) => {
console.log(projectName, options);
})
program.command是定义一个命令,命令的格式是junxu-cli create xxxdescription就是这个命令的描述,不多介绍了option是命令后面可以携带的参数以及参数的相关描述action后面是一个回调,回调的第一个参数就是上面的xxx,第二个参数就是--后面的
运行命令
junxu-cli create test --force
编写create模块
由于create模块可能有很多功能,比如校验目录重复,获取版本信息,拉取远程代码或模块等等功能。所以我们先创建一个类,根目录下新建一个文件夹lib,并增加一个文件create.js,代码如下:
class Creator {
constructor(projectName, options){
this.projectName = projectName;
this.options = options
}
// 执行 create 方法
async create(){
...
}
}
module.exports = async function(projectName, options){
const creator = new Creator(projectName, options);
await creator.create()
}
处理工程文件名重复的情况
使用 junxu-cli create xxx 创建一个项目时, xxx 文件名可能存在,则需要在创建前进行校验。
- 使用了参数
--force则删除原有目录,然后创建上诉目录文件- 如果没有使用
--force参数,则会询问是否覆盖上诉同名文件,如果选择覆盖则执行,如果不覆盖则终止执行询问用户是否覆盖的时候需要使用
inquirer插件,使用fs-extra模块来判断是否存在同名文件
class Creator {
constructor(projectName, options) {
this.projectName = projectName;
this.options = options;
}
// 创建
async create() {
const isOverwrite = await this.handleDirectory();
if(!isOverwrite) return;
console.log('todo....');
}
// 处理是否有相同目录
async handleDirectory() {
const targetDirectory = path.join(cwd, this.projectName);
// 如果目录中存在了需要创建的目录
if (fs.existsSync(targetDirectory)) {
if (this.options.force) {
await fs.remove(targetDirectory);
} else {
let { isOverwrite } = await new Inquirer.prompt([
{
name: 'isOverwrite',
type: 'list',
message: '是否强制覆盖已存在的同名目录?',
choices: [
{ name: '覆盖', value: true },
{ name: '不覆盖', value: false }
]
}
]);
if (isOverwrite) {
await fs.remove(targetDirectory);
} else {
console.log(chalk.red.bold('不覆盖文件夹,创建终止'));
return false;
}
}
};
return true;
}
}
运行
junxu-cli create test命令
增加调取模版API
从远程去获取需要拉取的模版列表拉取到本地,先封装一下API,创建一个api目录
TODO ...
拉取远程数据需要时间,增加一个 loading 效果,需要安装 ora 库:
npm install ora@5
在 create.js 中增加方法 getCollectRepo 代码如下:
const ora = require('ora');
const api = require('./api/interface');
// ...
async create() {
// ...
await this.getCollectRepo();
}
// ...
// 获取可拉取的仓库列表
async getCollectRepo() {
const loading = ora('正在获取模版信息...');
loading.start();
// const {data: list} = await api.getRepoList({per_page: 100});
const {data: list} = await api.getRepoList();
loading.succeed();
// list = [
// {
// topics: ['template'],
// name: 'vue2-template'
// },
// {
// topics: ['template'],
// name: 'vue3-template'
// }
// ]
const collectTemplateNameList = list.filter(item => item.topics.includes('template')).map(item => item.name);
let { choiceTemplateName } = await new Inquirer.prompt([
{
name: 'choiceTemplateName',
type: 'list',
message: '请选择模版',
choices: collectTemplateNameList
}
]);
console.log('选择了模版:' + choiceTemplateName);
}
下载对应模版
根据用户选择的模版来定向拉取对应的模版到本地了,拉取的模版地址已经准备好了: ShiJunXu/test。
我们需要使用download-git-repo插件来把git上面的项目拉取到本地,由于这个插件不支持promise,所以又需要使用node自带的util工具来支持。
安装插件:
npm install download-git-repo
在 create.js 中添加如下代码:
const util = require('util');
const downloadGitRepo = require('download-git-repo');
// ...
class Creator {
// ...
// 获取可拉取的仓库列表
async getCollectRepo() {
// ...
this.downloadTemplate(choiceTemplateName);
}
// 下载模板仓库
async downloadTemplate(choiceTemplateName) {
this.downloadGitRepo = util.promisify(downloadGitRepo);
const templateUrl = `ShiJunXu/${choiceTemplateName}`;
const loading = ora('正在拉取模版...');
loading.start();
await this.downloadGitRepo(templateUrl, path.join(cwd, this.projectName));
loading.succeed();
}
}
运行
junxu-cli create test
当模板拉取完成,在根目录下会有 test 文件的模板代码
模版提示
模板拉取完成,需要告诉用户如何操作
在 create.js 中新增如下代码:
class Creator {
// ...
// 下载仓库
async downloadTemplate(choiceTemplateName) {
// ...
this.showTemplateHelp();
}
// 模版使用提示
showTemplateHelp() {
console.log(`\r\nSuccessfully created project ${chalk.cyan(this.projectName)}`);
console.log(`\r\n cd ${chalk.cyan(this.projectName)}\r\n`);
console.log(" npm install");
console.log(" npm run dev\r\n");
}
}
执行 junxu-cli create test 脚手架命令从模板中创建项目:
发布脚手架
在 npm 官网注册账号密码,终端执行命令:npm login
这个过程中可能会报错: 需要设置 npm 源镜像, 通过
npm config get registry查看镜像,如果不为https://registr.npmjs.org, 则需要按 顺序执行如下代码即可:
npm config get proxynpm config get https-proxynpm config set registry https://registry.npmjs.org
执行 npm publish 发布
这个过程可能会出现如下问题:
这是由于
package.json文件的name属性名与npm库中重名了,修改一下即可发布成功。
每次更新的时候,修改你的package.json中的version,再次npm publish即可。