业务脚手架搭建——基础篇

·  阅读 520

基础工具总览

搭建脚手架的目的是为了快速的搭建项目的基本结构并给团队提供一致的规范,让团队成员按照约定进行编码。目前日常工作中常用的脚手架有vue-clicreate-react-appvite 等等,都是通过简单的初始化命令,完成内容的快速构建,但这些都仅仅提供了最基础的功能,本文将从基础工具的角度阐述怎样搭建一套服务于业务的脚手架。

在了解脚手架之前,我们先谈谈在实际生产中搭建一个脚手架或者阅读其他脚手架源码的时候需要了解的一些工具库:

工具名备注
commander.js完整的 node.js 命令行解决方案。
chalk终端样式美化
Inquirer.js常用交互式命令行用户界面的集合
ora终端控制loading样式
download-git-repo下载远程模版
fs-extra系统fs的扩展
ejs嵌入式javascript模板
semvernpm语义版本控制器
boxen在终端中创建框

commander.js 命令行指令

初始化一个简单的cli项目

{
  "name": "@thrusterx/cli",
  "version": "0.0.1-beta.0",
  "description": "thrusterx frontend team",
  "main": "dist/scripts/bin/index.js",
  "author": "thrusterX",
  "license": "ISC",
  "bin": {
    "t7x": "dist/scripts/bin/index.js"
  },
  "files": [
    "dist"
  ],
  "dependencies": {
    "commander": "^8.3.0",
  }
}
复制代码

目录结构:                 

thrusterx-cli    
 ├─ scripts                
 │   ├─ bin                
 │   ├─   └─ index.ts          
 ├─ package-lock.json  
 └─ package.json
复制代码

引入commander

#!/usr/bin/env node
import { Command } from 'commander'
import pkg from '../../package.json'
const program = new Command()

program.name('t7x').usage('<command> [options]')

program.version(`@thrusterx/cli ${pkg.version}`).usage('<command> [options]')
program
    .command('init <app-name>')
    .description('init a new project')
    .option('-d, --default', 'skip init project options')
    .option('-c --all-config', 'create project with all config')
    .option('-a, --all-plugins', 'create project with all plugins')
    .option('-u, --uninstalled', 'skip install denpendencies')
    .action((name: string, options: InitCliOptions) => {
        console.log('do something')
    })
program.parse()
复制代码

npm link 链接到全局

  • 执行 npm link 将应用 @thrusterx/cli 链接到全局
  • 完成之后,在命令行中执行 t7x

 

$ t7x -h
Usage: t7x <command> [options]

Options:
  -V, --version              output the version number
  -h, --help                 display help for command

Commands:
  init [options] <app-name>  init a new project
复制代码

用chalk美化你的控制台

program
    .command('init <app-name>')
    .description('init a new project')
    .option('-d, --default', 'skip init project options')
    .option('-c --all-config', 'create project with all config')
    .option('-a, --all-plugins', 'create project with all plugins')
    .option('-u, --uninstalled', 'skip install denpendencies')
    .action((name: string, options: InitCliOptions) => {
       // 颜色
        console.log('project name is ' + chalk.yellow(name))
        console.log('project name is ' + chalk.green(name))
        // 背景色
        console.log('project name is ' + chalk.bgBlue(name))
        // 文本样式
        console.log('project name is ' + chalk.bold(name))
        // RGB颜色&十六进制
        console.log('project name is ' + chalk.rgb(255, 191, 0).underline(name))
        console.log('project name is ' + chalk.hex('#3C3C3C').bold(name))
        console.log('project name is ' + chalk.bgHex('#FF0000').bold(name))
    })
program.parse(process.argv)
复制代码

 

inquirer 让脚手架更灵活

inquirer 可以让你的脚手架通过简单的终端交互变得更灵活,官方文档

import { QuestionCollection } from 'inquirer'
export const promptList: Array<QuestionCollection> = [
    {
        type: 'input',
        message: 'Version:',
        name: 'version',
        default: '0.0.1',
    },
    {
        type: 'input',
        message: 'Author:',
        name: 'author',
        default: 'thruster-x',
    },
    {
        type: 'input',
        message: 'Description:',
        name: 'description',
        default: 'thruster-x project',
    },
    {
        type: 'list',
        message: 'Please set the project template',
        name: 'layoutTemp',
        choices: ['baseLayout', 'emptyLayout', 'none'],
        filter: (val: string) => {
            return val
        },
    },
]

async function checkOptions() {
    await inquirer
        .prompt(promptList)
        .then((answers: any) => {
            console.log(answers)
            Object.assign(defaultOptions, answers)
        })
        .catch((error: any) => {
            return error
        })
}
复制代码

 

ora 命令行 loading 动效

  

配置策略下载模板

关于下载模板通常都是通过 download-git-repo 拉取远程仓库模板,参考 create-vite 仓库提供了不同项目的模板,然后通过 ejs 将配置的参数写入到文件中

download('flippidippi/download-git-repo-fixture', 'test/tmp', function (err) {
  console.log(err ? 'Error' : 'Success')
})

const template: string = fs.readFileSync(filePath, 'utf-8')
const outputCode: string = ejs.compile(template, {})(config)
fs.writeFileSync(filePath, outputCode)
复制代码

子进程安装依赖

import { exec } from 'child_process'
exec(`cd ${config.name} && git init && npm install`, (err: any) => {
    console.log(err ? 'error' : 'success')
})
复制代码

 

脚手架版本升级校验

脚手架版本控制遵循 semantic versioning 2.0.0,当仓库有新的版本发布时,你执行本地的脚手架命令,控制台会输出升级提示

const nodeModules = resolve('../../../package.json')
const { latest, beta } = await getCliVersion()
fs.readFile(nodeModules, (err, data: any) => {
    if (semver.lt(version, latest)) {
         console.log(boxen('xxxxxx'))
    } else {
        ....
    }
})
复制代码

 当新版本可用时:

61dd2a8f6d46261f8946758dFVoi6dTj01.png

当beta版本发布后:

61dd2acdcd054864e039de21jNbY0GyP01.png

分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改