# 简单脚手架的搭建

脚手架就是个工具,方便新建项目。其实本可以用 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 的形式下载。

# 目录搭建

  1. 创建一个文件夹,并取名叫 xr-cli。

  2. 在该目录下执行 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"
}
  1. 新建一个 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 运行一下看效果

  1. 加上其他四个文件 xr-add xr-delete xr-list xr-init
  2. 在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')
 })
 })

原文 (opens new window)

# 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 ,如果出现下图中的模样就说明已经安装成功了。

# 搭建脚手架

最后更新: 5/12/2022, 8:31:12 AM