记录-开发一个脚手架

记录-开发一个脚手架

功能:

  • 类似vue-cli,通过一个命令, 比如vue create hello-world,然后根据一步一步的提示,最终拉取到对应的内容

开发原因:

  • 前端组内部有符合自己业务逻辑的模板项目,为了方便组员快速拉取,需要一个脚手架。
  • 脚手架搭起来后,适应不同需求的模板也可以往脚手架里面加
  • 不同的模板可以用git管理起来,很方便

使用效果:

依然用到了工具平台:内部前端工具平台搭建, 安装:npm i -g @myCompany/feTools

终端输入: fe cli  (tips:启动cli)
打印:一个提示:? 请输入您想创建的文件名 (会创建在当前目录下): (cli-project)  (tips:此时可以输入 想创建的文件名,或者用默认的名字cli-project)
    
按回车往下走:一个提示:? 请选择您想拉取的模板: (Use arrow keys)  (tips:此时可以上下选择想要拉取的模板)
                    ❯ vue2 
                      test 
按回车结束(文件已经拉到了当前目录)
复制代码

开发原理解析:

一些第三方工具包的组合

  1. commander: 识别命令,例如 fe cli -h
  2. inquirer: 终端交互,可以输入文件名,选择模板
  3. download-git-repo: 拉取gitlab的库。类似git clone
  4. 其他,比如执行npm install

image.png

源码:

entry.js : 入口文件

#! /usr/bin/env node
// 上面是配置当前的环境变量地址: mac上是固定的

// 引入依赖
const commander = require('commander') // 完整的 node.js 命令行解决方案: https://www.npmjs.com/package/commander
const createProject = require('./create-project') // 下一步要看的
const inquirer = require('inquirer') // 命令行交互工具: https://www.npmjs.com/package/inquirer
const pkgJson = require('../../package.json')

// 定义版本号以及命令选项
commander
  .version(pkgJson.version, '-v -V --version') // 不写后面这个的话,只能用大写的 -V
  .option('-i --init [name]', 'init a project')
  .parse(process.argv) // let commander can get process.argv

const promptList = [{
  type: 'input',
  message: '请输入您想创建的文件名 (会创建在当前目录下):',
  name: 'name',
  default: 'cli-project' // 默认值
}]

let projectName = ''
if (commander.init) { // 如果 cli -i xxName, 就走这里
  projectName = commander.init
  createProject(projectName)
} else { // 直接 cli 走这里
  inquirer.prompt(promptList).then(answers => {
    projectName = answers.name ? answers.name : 'cli-project'
    createProject(projectName) // 拿到文件名
  })
}
复制代码

create-project.js : 配置用户的选项交互

const chalk = require('chalk') // 彩色console.log
const path = require('path') // 获取路径
const inquirer = require('inquirer')
const GitClone = require('./git-clone.js') // 下一步要看的
const npmInstall = require('./npm-install.js') // 扩展项, 命令行执行lnpm i, 不影响主功能

const promptList = [{
  type: 'list',
  message: '请选择您想拉取的模板:',
  name: 'key',
  choices: [],
  filter: function (val) { // filter result
    return val
  }
}]

GitClone.tplObj.map(e => {
  promptList[0].choices.push(e.key) // 把git模板放入 promptList 中, 最后由inquirer执行
})

module.exports = function createProject (name) {
  inquirer.prompt(promptList).then(answers => {
    // 获取将要构建的项目根目录
    const projectPath = path.resolve(name) // return absolute path
    console.log(`Start to init a project in ${chalk.green(projectPath)}`)

    const key = answers.key
    // 执行git clone
    GitClone.gitCloneFn(key, name)
      .then(msg => {
        console.log(chalk.green(msg))

        // 由于大一点的模板 npm install后, 编辑器很容易卡死, 编辑器执行可以注释掉此处 npm install的功能
        npmInstall(projectPath)
      })
      .catch(err => {
        if (err.includes('128')) {
          console.log(chalk.red(`失败! ${name}已经存在`))
        } else {
          console.log(chalk.red(err))
        }
      })
  })
}
复制代码

git-clone.js : 利用git clone把项目拉下来,拉取的模板可以在这里配置

const download = require('download-git-repo') // 执行git clone拉代码: https://www.npmjs.com/package/download-git-repo

const tplObj = [
  {
    key: 'vue2',
    git: 'https://gitlab.xxx.cn/xxxx/xxxx.git#vue2' // #vue2表示vue2分支
  },
  {
    key: 'test',
    git: 'https://gitlab.xxx.cn/xxxx/test.git'
  }
]

const gitCloneFn = (key, tplName) => {
  return new Promise((resolve, reject) => {
    const obj = tplObj.find(e => e.key === key)
    download('direct:' + obj.git, tplName, { clone: true }, function (err) {
      const msg = `拉取最新${obj.git}: `
      if (err) {
        reject(`${msg}: Error: ${err}`)
      } else {
        resolve(`${msg}: Success`)
      }
    })
  })
}

module.exports = {
  tplObj,
  gitCloneFn
}
复制代码

npm-install.js : 执行 npm install

// 引入依赖
const which = require('which') // 当前环境(电脑环境)是否存在 某 全局变量: 用处: 找是否存在lnpm, 如果不存在则用npm
const chalk = require('chalk')

const { spawn } = require('child_process') // 执行命令行 如: lnpm i

// 开启子进程来执行npm install命令
function runCommand (command, args, fn) {
  args = args || []
  const runner = spawn(command, args, {
    stdio: 'inherit' // (继承父进程的stdio输出) 输出 npm install 的信息
  })

  runner.on('close', function (code) {
    if (fn) {
      fn(code)
    }
  })
}

// 查找系统中用于安装依赖包的命令
function findNpm () {
  const npms = ['lnpm', 'cnpm', 'npm']
  for (let i = 0; i < npms.length; i++) {
    try {
      // 查找环境变量下指定的可执行文件的第一个实例
      which.sync(npms[i])
      console.log('use npm: ' + npms[i])
      return npms[i]
    } catch (e) {
    }
  }
  throw new Error(chalk.red('please install lnpm'))
}

module.exports = function npmInstall (projectPath) {
  console.log('Installing packages...')
  // 将node工作目录更改成构建的项目根目录下
  process.chdir(projectPath)

  setTimeout(() => {
    const npm = findNpm()
    runCommand(which.sync(npm), ['install'], function () {
      console.log(npm + ' install end')
    })
  }, 1000)
}
复制代码

码字不易,点点小赞鼓励

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享