# 简单脚手架的搭建
脚手架就是个工具,方便新建项目。其实本可以用 git clone url 来新建(复制)项目。脚手架的本质也是从远程下载一个模板来进行一个新项目。脚手架可是高级版的克隆,它主要是提供了交互式的命令让我们可以动态的更改模板。
目标:写一个简易版的脚手架 xr-cli,目标是实现一个 xr init template-name project-name 这样的命令。
# commander
这是用来编写指令和处理命令行的
const program = require("commander");
// 定义指令
program
.version('0.0.1')
.command('init', 'Generate a new project from a template')
.action(() => {
// 回调函数
})
// 解析命令行参数
program.parse(process.argv);
曾用过的 vue init 的命令就是这样声明的
# inquirer
这是个强大的交互式命令行工具
const inquirer = require('inquirer');
inquirer
.prompt([
// 一些交互式的问题
])
.then(answers => {
// 回调函数,answers 就是用户输入的内容,是个对象
});
# chalk
用来修改控制台输出内容样式的,比如颜色
const chalk = require('chalk');
console.log(chalk.green('success'));
console.log(chalk.red('error'));
# ora
一个好看的加载,就是下载的时候会有个转圈圈的那种效果
const ora = require('ora')
let spinner = ora('downloading template ...')
spinner.start()
# download-git-repo
看名字很明显了,这是用来下载远程模板的,支持 GitHub、 GitLab 和 Bitbucket 等
const download = require('download-git-repo')
download(repository, destination, options, callback)
其中 repository 是远程仓库地址;destination 是存放下载的文件路径,也可以直接写文件名,默认就是当前目录;options 是一些选项,比如 { clone:boolean } 表示用 http download 还是 git clone 的形式下载。
# 目录搭建
创建一个文件夹,并取名叫 xr-cli。
在该目录下执行 npm init 命令,一路回车,就会生成一个生成 package.json 文件,在 package.json 里面写入以下依赖并执行 npm install 安装
"dependencies": {
"chalk": "^2.4.2",
"commander": "^2.19.0",
"download-git-repo": "^1.1.0",
"inquirer": "^6.2.2",
"ora": "^3.2.0"
}
- 新建一个 bin 文件夹,并在 bin 目录下新建一个无后缀名的 xr 文件
#!/usr/bin/env node
const program = require('commander')
// 定义当前版本
// 定义使用方法
// 定义四个指令
program
.version(require('../package').version)
.usage('<command> [options]')
.command('add', 'add a new template')
.command('delete', 'delete a template')
.command('list', 'list all the templates')
.command('init', 'generate a new project from a template')
// 解析命令行参数
program.parse(process.argv)
#!/usr/bin/env node 这个语句必须加上,主要是为了让系统看到这一行的时候,会沿着该路径去查找 node 并执行,主要是为了兼容 Mac ,确保可执行。
node ./bin/xr 运行一下看效果
- 加上其他四个文件 xr-add xr-delete xr-list xr-init
- 在package.json 里面的配置一个 bin选项
"bin": {
"xr": "bin/xr",
"xr-add": "bin/xr-add",
"xr-delete": "bin/xr-delete",
"xr-list": "bin/xr-list",
"xr-init": "bin/xr-init"
}
6.执行npm link命令绑定全局(如果之前使用过需要先解绑npm unlink)
# xr-add
#!/usr/bin/env node
// 交互式命令行
const inquirer = require('inquirer')
// 修改控制台字符串的样式
const chalk = require('chalk')
// node 内置文件模块
const fs = require('fs')
// 读取根目录下的 template.json
const tplObj = require(`${__dirname}/../template`)
// 自定义交互式命令行的问题及简单的校验
let question = [
{
name: "name",
type: 'input',
message: "请输入模板名称",
validate (val) {
if (val === '') {
return 'Name is required!'
} else if (tplObj[val]) {
return 'Template has already existed!'
} else {
return true
}
}
},
{
name: "url",
type: 'input',
message: "请输入模板地址",
validate (val) {
if (val === '') return 'The url is required!'
return true
}
}
]
inquirer
.prompt(question).then(answers => {
// answers 就是用户输入的内容,是个对象
let { name, url } = answers;
// 过滤 unicode 字符
tplObj[name] = url.replace(/[\u0000-\u0019]/g, '')
// 把模板信息写入 template.json 文件中
fs.writeFile(`${__dirname}/../template.json`, JSON.stringify(tplObj), 'utf-8', err => {
if (err) console.log(err)
console.log('\n')
console.log(chalk.green('Added successfully!\n'))
console.log(chalk.grey('The latest template list is: \n'))
console.log(tplObj)
console.log('\n')
})
})
# xr-delete
#!/usr/bin/env node
const inquirer = require('inquirer')
const chalk = require('chalk')
const fs = require('fs')
const tplObj = require(`${__dirname}/../template`)
let question = [
{
name: "name",
message: "请输入要删除的模板名称",
validate (val) {
if (val === '') {
return 'Name is required!'
} else if (!tplObj[val]) {
return 'Template does not exist!'
} else {
return true
}
}
}
]
inquirer
.prompt(question).then(answers => {
let { name } = answers;
delete tplObj[name]
// 更新 template.json 文件
fs.writeFile(`${__dirname}/../template.json`, JSON.stringify(tplObj), 'utf-8', err => {
if (err) console.log(err)
console.log('\n')
console.log(chalk.green('Deleted successfully!\n'))
console.log(chalk.grey('The latest template list is: \n'))
console.log(tplObj)
console.log('\n')
})
})
# xr-list
#!/usr/bin/env node
const tplObj = require(`${__dirname}/../template`)
console.log(tplObj)
# xr-init
这步的重点就是执行 download 方法,并传入相应参数
#!/usr/bin/env node
const program = require('commander')
const chalk = require('chalk')
const ora = require('ora')
const download = require('download-git-repo')
const tplObj = require(`${__dirname}/../template`)
program
.usage('<template-name> [project-name]')
program.parse(process.argv)
// 当没有输入参数的时候给个提示
if (program.args.length < 1) return program.help()
// 好比 vue init webpack project-name 的命令一样,第一个参数是 webpack,第二个参数是 project-name
let templateName = program.args[0]
let projectName = program.args[1]
// 小小校验一下参数
if (!tplObj[templateName]) {
console.log(chalk.red('\n Template does not exit! \n '))
return
}
if (!projectName) {
console.log(chalk.red('\n Project should not be empty! \n '))
return
}
url = tplObj[templateName]
console.log(chalk.white('\n Start generating... \n'))
// 出现加载图标
const spinner = ora("Downloading...");
spinner.start();
// 执行下载方法并传入参数
download (
url,
projectName,
err => {
if (err) {
spinner.fail();
console.log(chalk.red(`Generation failed. ${err}`))
return
}
// 结束加载图标
spinner.succeed();
console.log(chalk.green('\n Generation completed!'))
console.log('\n To get started')
console.log(`\n cd ${projectName} \n`)
}
)
执行一下 xr init simple test ,就可生成test文件夹及模板代码
# 发布到npm
- 删除 test 文件夹
- 在根目录下新建 README.md 文件
- 在根目录下新建 .npmignore 文件,并写入 /node_modules ,意思就是发布的时候忽略 node_modules 文件夹,
- 去 npm 官网注册个账号(很简单的),同时搜索一下 xr-cli 这个名字,看看有没有人用,有的话就换一个罗
- 执行 npm login 登入 npm 账号,再执行 npm publish 发布
npm unpublish --force删除包
记得先用 npm unlink 解绑一下命令,不然会相互影响。下面打开终端,输入 npm i xr-cli -g 全局安装一下脚手架,然后执行 xr ,如果出现下图中的模样就说明已经安装成功了。
# 搭建脚手架
← npm上发布插件