从0到1建立属于自己的脚手架二

587 阅读3分钟

有了上一篇文章的的基础准备知识,下面我们开始动手做吧

创建你的脚手架文件夹及名称

mkdir mcf-cli  && cd mcf-cli

npm init

此时,你的文件夹下回自动生成一个package.json,里面的基本信息就是你在终端输入的基本信息

创建bin文件夹和tempalte.json

此时基本的文件目录如下

/bin  # ------ 命令执行文件
package.json  #------ 配置文件
template.json  # ------ 模板列表对象

template.json 为空对象

{}

在bin目录下创建mcf

#!/usr/bin/env node  
//定义执行解释器
const program = require('commander')
const chalk = require('chalk')
console.log('**********************************************************************');
console.log('*      '+chalk.red('mcf-cli(闵长峰脚手架) 为自己写的一个脚手架工具:')+'               *');
console.log('*      '+chalk.green('1、让项目从"搭建-开发-部署"更加快速以及规范')+'                   *');
console.log('*      '+chalk.green('2、避免创建项目时手动复制不全的尴尬')+'                           *');
console.log('*      '+chalk.green('3、多了解点原理,多学点东西总是好的')+'                           *');
console.log('**********************************************************************');
program
  .version(require('../package').version)
  .usage('<command> [options]')
  .command('add', '添加新的源 mcf add')
  .command('delete', '删除源 mcf list')
  .command('list', '展示源列表 mcf list')
  .command('init', '从源初始化项目 mcf init <模板名> 项目名')
  .command('create-vue', '快速构建新vue项目 mcf create-vue <项目名>')

program.parse(process.argv)
  1. version('0.0.1') 版本号;

  2. usage('') 名字

  3. command('') 定义子命令;

  4. parse(process.argv); 至于末尾,解析命令行输入的命令;

其他的还包括:

  1. description(‘hello ,I\’m zhao’) 描述
  2. allowUnknownOption 取消接收到未定义option时报错的机制,不报错;
  3. alias(‘a’) 定义子命令的短命令;
  4. action(cb) 回调
  5. option(‘-p, –peppers’,’Add oeooers’) 自定义选项参数和描述

将mcf命令行配置到package中

在 package.json中,添加bin属性

{
    "bin":{
        "mcf":"bin/mcf"
    }
    ...
    "dependencies": {
    "commander": "^2.19.0",
    "metalsmith": "^2.3.0",
    "download-git-repo": "^1.1.0",
    "inquirer": "^6.2.2",
    "ora": "^3.2.0",
    "chalk": "^2.4.2",
    "latest-version": "^3.1.0",
    "log-symbols": "^2.2.0",
    "glob": "^7.1.2",
    "handlebars": "^4.0.11"
  }
  ...
}

npm link

将当前开发的npm关联到node环境中

打开cmd,输入mcf 回车查看你的mcf是否是你想要的结果

QQ截图20191009144638.png

依次创建mcf-add、mcf-init、mcf-list、mcf-delete

mcf-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 name 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('O(∩_∩)~ Added successfully!\n'))
      console.log(chalk.grey('The latest template list is: \n'))
      console.log(tplObj)
      console.log('\n')
    })
  })

mcf-add命令行的作用主要在于,添加一个模板到脚手架本地库中,输入用模板名以及模板对应的url(github上只需写到用户名/项目名)

mcf-list文件

#!/usr/bin/env node

const tplObj = require(`${__dirname}/../template`)
console.log(tplObj)

查看当地的模板列表

mcf-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')
    })
  })

mcf-delete <模板名>

从本地mcf脚手架模板列表中删除你要删除的模板

mcf-init文件

#!/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`)
  }
)

mcf-init <模板名> <项目名>

将模板copy到本地

添加进package.json

{
    ...
    "bin":{
        "mcf-add": "bin/mcf-add",
        "mcf-delete": "bin/mcf-delete",
        "mcf-list": "bin/mcf-list",
        "mcf-init": "bin/mcf-init",
    }
    ...
}

总结

到这里你会发现,其实这只是基础的git clone的方法,你也许会说,写这么多,还不如我以一句git clone命令行来得实在