前端工程化之脚手架创建工具

概述

开发此工具的目的是为了统一公司前端项目脚手架,技术选型,集成CICD方案,集成内部依赖包等。

原理

跟常规的npm包一样,通过在package.json中配置bin(PATH环境变量软链)安装的时候可以在shell终端使用命令来执行我们的nodejs脚本,通过提问式获取最终配置项,来创建项目的脚手架。

程序设计图:

核心设计

提问式模式(Prompt Design)

  • 将脚手架功能细化成程序运行平台、基础框架选型、脚手架能力、CICD集成方案等模块化能力,通过用户选择将其动态组合起来,最终构建出目标工程。具体代码实现:
js 复制代码
const inquirer = require('inquirer')
// 获取提供给用户的选择项
const getPromptModules = () => {
  return ['feature-x', 'feature-xx', 'feature-xxx', 'feature-xxxx'].map(file =>
    require(`../promptModules/${file}`)
  )
}
const promptAPI = new PromptModuleAPI(this)
getPromptModules().forEach(m => m(promptAPI))
// 展示选项在终端中,合并最终选择项目
const resolveFinalPrompts = () => {
    this.injectedPrompts.forEach(prompt => {
      const originalWhen = prompt.when || (() => true)
      prompt.when = answers => {
        return isManualMode(answers) && originalWhen(answers)
      }
    })
    let prompts = ''
    if (this.featurePrompt.choices.length > 0) {
      prompts = [
        this.presetPrompt,
        this.featurePrompt,
        ...this.injectedPrompts,
        ...this.outroPrompts,
      ]
    } else {
      prompts = [this.presetPrompt, ...this.injectedPrompts, ...this.outroPrompts]
    }
    debug('paxc:prompts')(prompts)
    return prompts
}
const answers = await inquirer.prompt(resolveFinalPrompts())

插件机制(Plugin System)

  • 提供给前端用户选择的内容都通过插件式的方式实现,方便第三方继承和扩展该能力,丰富功能。
  • 有利于将核心能力聚合,对外暴露接口,形成高内聚低耦合的设计方式

具体代码:

js 复制代码
// 获取本地插件
async resolvePlugins(rawPlugins, preset) {
    for (const plugin of rawPlugins) {
      let apply = ''
      if (plugin.isCore) {
        apply = require(`./plugins/core/${plugin.id}`).apply
      } else {
        apply = require(`./plugins/${plugin.id}`).apply
      }
      plugin.apply = apply
      if (!plugin.options) {
        plugin.options = preset
      }
    }
}
// 对插件的执行优先级排序,初始化插件,应用插件
_sortPlugins() {
    const plugins = this.plugins
    for (let i = 0; i < plugins.length; i++) {
      const plugin = plugins[i]
      if (plugin.isCore && i === 0) break
      if (plugin.isCore) {
        plugins[i] = plugins[0]
        plugins[0] = plugin
        break
      }
    }
}
async initPlugins() {
    const pluginIds = this.plugins.map(p => p.id)
    // apply generators from plugins
    for (const plugin of this.plugins) {
      const { id, apply, options } = plugin
      if (apply) {
        const api = new GeneratorAPI(id, this, options, this.preset)
        await apply(api, options, this.preset)
        if (apply.hooks) {
          await apply.hooks(api, options, this.preset, pluginIds)
        }
      }
    }
}
// 生成脚手架文件
async generate({ sortPackageJson = true } = {}) {
    // core plugin first
    this._sortPlugins()
    await this.initPlugins()
    const initialFiles = Object.assign({}, this.files)
    // wait for file resolve
    await this.resolveFiles()
    if (this.pkg) {
      // set package.json
      if (sortPackageJson) {
        this.sortPkg()
      }

      this.files['package.json'] = JSON.stringify(this.pkg, null, 2) + '\n'
    }
    // write/update file tree to disk
    await writeFileTree(this.context, this.files, initialFiles)
}
相关推荐
拉不动的猪3 分钟前
前端常见数组分析
前端·javascript·面试
小吕学编程20 分钟前
ES练习册
java·前端·elasticsearch
Asthenia041227 分钟前
Netty编解码器详解与实战
前端
袁煦丞32 分钟前
每天省2小时!这个网盘神器让我告别云存储混乱(附内网穿透神操作)
前端·程序员·远程工作
一个专注写代码的程序媛2 小时前
vue组件间通信
前端·javascript·vue.js
一笑code2 小时前
美团社招一面
前端·javascript·vue.js
懒懒是个程序员2 小时前
layui时间范围
前端·javascript·layui
NoneCoder2 小时前
HTML响应式网页设计与跨平台适配
前端·html
凯哥19702 小时前
在 Uni-app 做的后台中使用 Howler.js 实现强大的音频播放功能
前端
烛阴2 小时前
面试必考!一招教你区分JavaScript静态函数和普通函数,快收藏!
前端·javascript