build.ts 4.2 KB
Newer Older
fxy060608's avatar
fxy060608 已提交
1
import fs from 'fs'
fxy060608's avatar
fxy060608 已提交
2
import path from 'path'
fxy060608's avatar
fxy060608 已提交
3
import debug from 'debug'
fxy060608's avatar
fxy060608 已提交
4 5
import { UserConfig } from 'vite'

fxy060608's avatar
fxy060608 已提交
6
import {
fxy060608's avatar
fxy060608 已提交
7
  emptyDir,
fxy060608's avatar
fxy060608 已提交
8
  EXTNAME_JS_RE,
fxy060608's avatar
fxy060608 已提交
9
  normalizePath,
fxy060608's avatar
fxy060608 已提交
10 11
  hasJsonFile,
  removeExt,
fxy060608's avatar
fxy060608 已提交
12
  resolveMainPathOnce,
13
  normalizeMiniProgramFilename,
fxy060608's avatar
fxy060608 已提交
14
  isCSSRequest,
fxy060608's avatar
fxy060608 已提交
15
} from '@dcloudio/uni-cli-shared'
fxy060608's avatar
fxy060608 已提交
16
import { GetManualChunk, GetModuleInfo, Plugin, PreRenderedChunk } from 'rollup'
fxy060608's avatar
fxy060608 已提交
17 18 19 20 21
import {
  isUniComponentUrl,
  isUniPageUrl,
  parseVirtualComponentPath,
  parseVirtualPagePath,
fxy060608's avatar
fxy060608 已提交
22
} from '../plugins/entry'
fxy060608's avatar
fxy060608 已提交
23

fxy060608's avatar
fxy060608 已提交
24 25
const debugChunk = debug('vite:uni:chunk')

fxy060608's avatar
fxy060608 已提交
26
export function buildOptions(): UserConfig['build'] {
fxy060608's avatar
fxy060608 已提交
27
  const inputDir = process.env.UNI_INPUT_DIR
fxy060608's avatar
fxy060608 已提交
28 29 30 31 32
  const outputDir = process.env.UNI_OUTPUT_DIR
  // 开始编译时,清空输出目录
  if (fs.existsSync(outputDir)) {
    emptyDir(outputDir)
  }
fxy060608's avatar
fxy060608 已提交
33 34
  return {
    // sourcemap: 'inline', // TODO
fxy060608's avatar
fxy060608 已提交
35
    // target: ['chrome53'], // 由小程序自己启用 es6 编译
fxy060608's avatar
fxy060608 已提交
36
    emptyOutDir: false, // 不清空输出目录,否则会影响自定义的一些文件输出,比如wxml
37 38 39 40 41 42
    lib: {
      // 必须使用 lib 模式,否则会生成 preload 等代码
      fileName: 'app.js',
      entry: resolveMainPathOnce(inputDir),
      formats: ['cjs'],
    },
fxy060608's avatar
fxy060608 已提交
43 44
    rollupOptions: {
      output: {
fxy060608's avatar
fxy060608 已提交
45
        entryFileNames: 'app.js',
fxy060608's avatar
fxy060608 已提交
46
        format: 'cjs',
fxy060608's avatar
fxy060608 已提交
47
        manualChunks: createMoveToVendorChunkFn(),
fxy060608's avatar
fxy060608 已提交
48
        chunkFileNames: createChunkFileNames(inputDir),
fxy060608's avatar
fxy060608 已提交
49
        assetFileNames: '[name][extname]',
fxy060608's avatar
fxy060608 已提交
50
        plugins: [dynamicImportPolyfill()],
fxy060608's avatar
fxy060608 已提交
51 52 53 54
      },
    },
  }
}
fxy060608's avatar
fxy060608 已提交
55

fxy060608's avatar
fxy060608 已提交
56
function isVueJs(id: string) {
fxy060608's avatar
fxy060608 已提交
57
  return id.includes('plugin-vue:export-helper')
fxy060608's avatar
fxy060608 已提交
58 59 60 61
}

const chunkFileNameBlackList = ['main', 'pages.json', 'manifest.json']

fxy060608's avatar
fxy060608 已提交
62 63
function createMoveToVendorChunkFn(): GetManualChunk {
  const cache = new Map<string, boolean>()
fxy060608's avatar
fxy060608 已提交
64
  const inputDir = normalizePath(process.env.UNI_INPUT_DIR)
fxy060608's avatar
fxy060608 已提交
65
  return (id, { getModuleInfo }) => {
fxy060608's avatar
fxy060608 已提交
66
    id = normalizePath(id)
fxy060608's avatar
fxy060608 已提交
67 68 69
    const filename = id.split('?')[0]
    // 处理项目内的js,ts文件
    if (EXTNAME_JS_RE.test(filename)) {
70
      if (filename.startsWith(inputDir) && !filename.includes('node_modules')) {
fxy060608's avatar
fxy060608 已提交
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
        const chunkFileName = removeExt(
          normalizePath(path.relative(inputDir, filename))
        )
        if (
          !chunkFileNameBlackList.includes(chunkFileName) &&
          !hasJsonFile(chunkFileName) // 无同名的page,component
        ) {
          debugChunk(chunkFileName, id)
          return chunkFileName
        }
        return
      }
      // 非项目内的 js 资源,均打包到 vendor
      debugChunk('common/vendor', id)
      return 'common/vendor'
    }
fxy060608's avatar
fxy060608 已提交
87
    if (
fxy060608's avatar
fxy060608 已提交
88 89 90 91
      isVueJs(id) ||
      (id.includes('node_modules') &&
        !isCSSRequest(id) &&
        staticImportedByEntry(id, getModuleInfo, cache))
fxy060608's avatar
fxy060608 已提交
92
    ) {
fxy060608's avatar
fxy060608 已提交
93 94 95
      debugChunk('common/vendor', id)
      return 'common/vendor'
    }
fxy060608's avatar
fxy060608 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
  }
}

function staticImportedByEntry(
  id: string,
  getModuleInfo: GetModuleInfo,
  cache: Map<string, boolean>,
  importStack: string[] = []
): boolean {
  if (cache.has(id)) {
    return cache.get(id) as boolean
  }
  if (importStack.includes(id)) {
    // circular deps!
    cache.set(id, false)
    return false
  }
  const mod = getModuleInfo(id)
  if (!mod) {
    cache.set(id, false)
    return false
  }

  if (mod.isEntry) {
    cache.set(id, true)
    return true
  }
  const someImporterIs = mod.importers.some((importer) =>
    staticImportedByEntry(
      importer,
      getModuleInfo,
      cache,
      importStack.concat(id)
    )
  )
  cache.set(id, someImporterIs)
  return someImporterIs
}
fxy060608's avatar
fxy060608 已提交
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165

function createChunkFileNames(
  inputDir: string
): (chunkInfo: PreRenderedChunk) => string {
  return function chunkFileNames(chunk) {
    if (chunk.isDynamicEntry && chunk.facadeModuleId) {
      let id = chunk.facadeModuleId
      if (isUniPageUrl(id)) {
        id = path.resolve(process.env.UNI_INPUT_DIR, parseVirtualPagePath(id))
      } else if (isUniComponentUrl(id)) {
        id = path.resolve(
          process.env.UNI_INPUT_DIR,
          parseVirtualComponentPath(id)
        )
      }
      return removeExt(normalizeMiniProgramFilename(id, inputDir)) + '.js'
    }
    return '[name].js'
  }
}

function dynamicImportPolyfill(): Plugin {
  return {
    name: 'dynamic-import-polyfill',
    renderDynamicImport() {
      return {
        left: '(',
        right: ')',
      }
    },
  }
}