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

概述

开发此工具的目的是为了统一公司前端项目脚手架,技术选型,集成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)
}
相关推荐
MengMeng_10233 小时前
soc平台告警分析思路及chrome插件
前端·chrome·安全威胁分析
落落落洛克3 小时前
WAIC 2026收官复盘:国产AI打通大模型、智能体、物理机器人全产业闭环
前端
小时代的大玩家4 小时前
HarmonyOS新特性-沉浸光感在叠叠消小游戏中的落地实践
前端·harmonyos
hunterandroid4 小时前
[鸿蒙从零到一] ArkUI 状态管理实战:从 @State 到 @Provide 与 @Consume
前端
Hilaku4 小时前
为什么很多人觉得前端很简单?
前端·javascript·程序员
hunterandroid4 小时前
[鸿蒙从零到一] ArkUI 列表与网格实战:List、Grid 与 LazyForEach
前端
李明卫杭州4 小时前
Vue2 数组响应式为什么总"不更新"?深入源码对比 Vue3 的 Proxy 改造
前端·javascript·vue.js
索西引擎4 小时前
【React】useReducer 与 useState 的比较研究:复杂状态管理场景下的选型
前端·react.js·前端框架
hunterandroid4 小时前
前台服务适配与线上排查:通知权限、启动限制和任务保活
android·前端
我是大卫4 小时前
【图】React源码解析-从“上帝视角”俯瞰React的认知框架
前端·react.js·源码