esbuild.ts 4.1 KB
Newer Older
fxy060608's avatar
fxy060608 已提交
1 2 3
import type { Plugin } from 'vite'
import type { BuildOptions, PluginBuild } from 'esbuild'

fxy060608's avatar
fxy060608 已提交
4 5
import path from 'path'
import fs from 'fs-extra'
fxy060608's avatar
fxy060608 已提交
6
import debug from 'debug'
fxy060608's avatar
fxy060608 已提交
7

fxy060608's avatar
fxy060608 已提交
8 9 10 11 12
import {
  APP_CONFIG_SERVICE,
  removeExt,
  transformWithEsbuild,
} from '@dcloudio/uni-cli-shared'
fxy060608's avatar
fxy060608 已提交
13

fxy060608's avatar
fxy060608 已提交
14
import { nvueOutDir } from '../../utils'
fxy060608's avatar
fxy060608 已提交
15
import { esbuildGlobals } from '../utils'
fxy060608's avatar
fxy060608 已提交
16
import { APP_CSS_JS } from './appCss'
fxy060608's avatar
fxy060608 已提交
17

fxy060608's avatar
fxy060608 已提交
18
const debugEsbuild = debug('uni:app-nvue-esbuild')
fxy060608's avatar
fxy060608 已提交
19

fxy060608's avatar
fxy060608 已提交
20
export function uniEsbuildPlugin({
fxy060608's avatar
fxy060608 已提交
21
  appService,
fxy060608's avatar
fxy060608 已提交
22 23
}: {
  renderer?: 'native'
fxy060608's avatar
fxy060608 已提交
24
  appService: boolean
fxy060608's avatar
fxy060608 已提交
25
}): Plugin {
fxy060608's avatar
fxy060608 已提交
26 27
  let buildOptions: BuildOptions
  const outputDir = process.env.UNI_OUTPUT_DIR
fxy060608's avatar
fxy060608 已提交
28 29 30
  return {
    name: 'uni:app-nvue-esbuild',
    enforce: 'post',
fxy060608's avatar
fxy060608 已提交
31 32 33 34
    configResolved(config) {
      buildOptions = {
        format: 'iife',
        minify: config.build.minify ? true : false,
fxy060608's avatar
fxy060608 已提交
35 36 37
        banner: {
          js: `"use weex:vue";`,
        },
fxy060608's avatar
fxy060608 已提交
38 39
        bundle: true,
        write: false,
fxy060608's avatar
fxy060608 已提交
40
        plugins: [esbuildGlobalPlugin(esbuildGlobals(appService))],
fxy060608's avatar
fxy060608 已提交
41 42
      }
    },
fxy060608's avatar
fxy060608 已提交
43 44 45 46 47 48 49 50 51 52 53 54
    async writeBundle(_, bundle) {
      const entryPoints: string[] = []
      Object.keys(bundle).forEach((name) => {
        const chunk = bundle[name]
        if (
          chunk.type === 'chunk' &&
          chunk.facadeModuleId &&
          chunk.facadeModuleId.endsWith('.nvue')
        ) {
          entryPoints.push(name)
        }
      })
fxy060608's avatar
fxy060608 已提交
55 56 57 58
      if (!entryPoints.length) {
        return
      }
      buildAppCss()
fxy060608's avatar
fxy060608 已提交
59
      debugEsbuild('start', entryPoints.length, entryPoints)
fxy060608's avatar
fxy060608 已提交
60
      for (const filename of entryPoints) {
fxy060608's avatar
fxy060608 已提交
61
        await buildNVuePage(filename, buildOptions).then((code) => {
fxy060608's avatar
fxy060608 已提交
62
          return fs.outputFile(path.resolve(outputDir, filename), code)
fxy060608's avatar
fxy060608 已提交
63
        })
fxy060608's avatar
fxy060608 已提交
64
      }
fxy060608's avatar
fxy060608 已提交
65
      debugEsbuild('end')
fxy060608's avatar
fxy060608 已提交
66 67 68 69
    },
  }
}

fxy060608's avatar
fxy060608 已提交
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
/**
 * 将 nvue 全局 css 样式注入 app-config-service.js
 * @returns
 */
function buildAppCss() {
  const appCssJsFilename = path.join(nvueOutDir(), APP_CSS_JS)
  if (!fs.existsSync(appCssJsFilename)) {
    return
  }
  const appConfigServiceFilename = path.join(
    process.env.UNI_OUTPUT_DIR,
    APP_CONFIG_SERVICE
  )
  if (!fs.existsSync(appConfigServiceFilename)) {
    return
  }
  const appCssJsCode = fs.readFileSync(appCssJsFilename, 'utf8')
  const appCssJsFn = new Function('exports', appCssJsCode)
  const exports = { styles: [] }
  appCssJsFn(exports)
  const appCssJsonCode = JSON.stringify(exports.styles)

  if (process.env.UNI_NVUE_APP_STYLES === appCssJsonCode) {
    return
  }
  process.env.UNI_NVUE_APP_STYLES = appCssJsonCode
  const appConfigServiceCode = fs.readFileSync(appConfigServiceFilename, 'utf8')
  fs.writeFileSync(
    appConfigServiceFilename,
    wrapperNVueAppStyles(appConfigServiceCode)
  )
}

function buildNVuePage(filename: string, options: BuildOptions) {
fxy060608's avatar
fxy060608 已提交
104
  return transformWithEsbuild(
fxy060608's avatar
fxy060608 已提交
105
    `import App from './${filename}'
fxy060608's avatar
fxy060608 已提交
106 107
const webview = plus.webview.currentWebview()
const __pageId = parseInt(webview.id)
fxy060608's avatar
fxy060608 已提交
108
const __pagePath = '${removeExt(filename)}'
fxy060608's avatar
fxy060608 已提交
109 110
let __pageQuery = {}
try{ __pageQuery = JSON.parse(webview.__query__) }catch(e){}
fxy060608's avatar
fxy060608 已提交
111
App.mpType = 'page'
fxy060608's avatar
fxy060608 已提交
112
const app = Vue.createPageApp(App,{$store:getApp({allowDefault:true}).$store,__pageId,__pagePath,__pageQuery})
fxy060608's avatar
fxy060608 已提交
113
app.provide('__globalStyles', Vue.useCssStyles([...__uniConfig.styles, ...(App.styles||[])]))
fxy060608's avatar
fxy060608 已提交
114
app.mount('#root')`,
fxy060608's avatar
fxy060608 已提交
115
    path.join(nvueOutDir(), 'main.js'),
fxy060608's avatar
fxy060608 已提交
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
    options
  ).then((res) => {
    if (res.outputFiles) {
      return res.outputFiles[0].text
    }
    return ''
  })
}

function esbuildGlobalPlugin(options: Record<string, string>) {
  const keys = Object.keys(options)
  return {
    name: 'global',
    setup(build: PluginBuild) {
      keys.forEach((key) => {
        const namespace = key + '-ns'
        build.onResolve({ filter: new RegExp('^' + key + '$') }, ({ path }) => {
          return {
            path,
            namespace,
          }
        })
        build.onLoad({ filter: /.*/, namespace }, () => ({
          contents: `module.exports = ${options[key]}`,
          loader: 'js',
        }))
fxy060608's avatar
fxy060608 已提交
142 143 144 145
      })
    },
  }
}
fxy060608's avatar
fxy060608 已提交
146 147 148 149 150 151 152

export function wrapperNVueAppStyles(code: string) {
  return code.replace(
    /__uniConfig.styles=(.*);\/\/styles/,
    `__uniConfig.styles=${process.env.UNI_NVUE_APP_STYLES || '[]'};//styles`
  )
}