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

概述

开发此工具的目的是为了统一公司前端项目脚手架,技术选型,集成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)
}
相关推荐
前端程序猿i10 分钟前
用本地代理 + ZIP 打包 + Excel 命名,优雅批量下载跨域 PDF
前端·javascript·vue.js·html
Danny_FD18 分钟前
Vue2 中使用vue-markdown实现编辑器
前端·javascript·vue.js
用户游民19 分钟前
Flutter 项目热更新加载 libapp.so 文件
前端
coding随想19 分钟前
Vue和React对DOM事件流的处理方法解析
前端
用户479492835691520 分钟前
字节面试官:forEach 为什么不能被中断?
前端·javascript
ccnocare21 分钟前
window.electronAPI.send、on 和 once
前端·electron
tager26 分钟前
🍪 让你从此告别“Cookie去哪儿了?”
前端·javascript·后端
阿吉被迫了解低代码31 分钟前
前端:“学算法?狗都不... !”
前端
前端赵哈哈37 分钟前
Vue 3 + TypeScript 项目模板
前端·vue.js·vite
南囝coding40 分钟前
命令行神器 The Fuck,敲错命令的后悔药
前端·后端