configure-webpack.js 15.9 KB
Newer Older
fxy060608's avatar
fxy060608 已提交
1 2 3 4
const fs = require('fs')
const path = require('path')
const webpack = require('webpack')
const CopyWebpackPlugin = require('copy-webpack-plugin')
5
const CopyWebpackPluginVersion = Number(require('copy-webpack-plugin/package.json').version.split('.')[0])
fxy060608's avatar
fxy060608 已提交
6 7 8

const merge = require('webpack-merge')

fxy060608's avatar
fxy060608 已提交
9 10 11 12
const {
  getPartialIdentifier
} = require('./util')

fxy060608's avatar
fxy060608 已提交
13 14 15 16 17
function resolve (dir) {
  return path.resolve(__dirname, '..', dir)
}

function resolveModule (dir) {
Q
qiang 已提交
18
  return path.resolve(process.env.UNI_CLI_CONTEXT, './node_modules', dir)
fxy060608's avatar
fxy060608 已提交
19 20
}

fxy060608's avatar
fxy060608 已提交
21
module.exports = function configureWebpack (platformOptions, manifestPlatformOptions, vueOptions, api) {
fxy060608's avatar
fxy060608 已提交
22 23 24 25 26
  const {
    runByHBuilderX, // 使用 HBuilderX 运行
    isInHBuilderX, // 在 HBuilderX 的插件中
    hasModule,
    jsPreprocessOptions,
fxy060608's avatar
fxy060608 已提交
27 28
    htmlPreprocessOptions,
    uts
fxy060608's avatar
fxy060608 已提交
29 30
  } = require('@dcloudio/uni-cli-shared')

fxy060608's avatar
fxy060608 已提交
31 32 33 34
  const {
    getPlatformVue
  } = require('@dcloudio/uni-cli-shared/lib/platform')

fxy060608's avatar
fxy060608 已提交
35 36 37 38 39 40 41 42
  const {
    getCopyWebpackPluginOptions
  } = require('./copy-webpack-options')

  function createMatcher (fakeFile) {
    return (rule, i) => {
      const clone = Object.assign({}, rule)
      delete clone.include
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
      if (webpack.version[0] > 4) {
        const BasicEffectRulePlugin = require('webpack/lib/rules/BasicEffectRulePlugin')
        const BasicMatcherRulePlugin = require('webpack/lib/rules/BasicMatcherRulePlugin')
        const RuleSetCompiler = require('webpack/lib/rules/RuleSetCompiler')
        const UseEffectRulePlugin = require('webpack/lib/rules/UseEffectRulePlugin')
        const ruleSetCompiler = new RuleSetCompiler([
          new BasicMatcherRulePlugin('test', 'resource'),
          new BasicMatcherRulePlugin('include', 'resource'),
          new BasicMatcherRulePlugin('exclude', 'resource', true),
          new BasicMatcherRulePlugin('resource'),
          new BasicMatcherRulePlugin('conditions'),
          new BasicMatcherRulePlugin('resourceQuery'),
          new BasicMatcherRulePlugin('realResource'),
          new BasicMatcherRulePlugin('issuer'),
          new BasicMatcherRulePlugin('compiler'),
          new BasicEffectRulePlugin('type'),
          new BasicEffectRulePlugin('sideEffects'),
          new BasicEffectRulePlugin('parser'),
          new BasicEffectRulePlugin('resolve'),
          new BasicEffectRulePlugin('generator'),
          new UseEffectRulePlugin()
        ])
        const ruleSet = ruleSetCompiler.compile([{
          rules: [clone]
        }])
        const rules = ruleSet.exec({
          resource: fakeFile
        })
        return rules.length > 0 && rule.use
      } else {
        const RuleSet = require('webpack/lib/RuleSet')
Q
qiang 已提交
74 75 76
        const normalized = RuleSet.normalizeRule(clone, {}, '')
        return (
          !rule.enforce &&
fxy060608's avatar
fxy060608 已提交
77 78
          normalized.resource &&
          normalized.resource(fakeFile)
Q
qiang 已提交
79 80
        )
      }
fxy060608's avatar
fxy060608 已提交
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
    }
  }

  function updateJsLoader (rawRules, fakeFile, checkLoaderRegex, loader) {
    const matchRule = rawRules.find(createMatcher(fakeFile))

    const matchUse = matchRule.use

    const matchLoaderUseIndex = matchUse.findIndex(u => {
      return checkLoaderRegex.test(u.loader)
    })

    if (matchLoaderUseIndex < 0) {
      throw new Error(`No matching use for ${fakeFile}`)
    }

    matchUse.push(loader)
  }

  const userTsConfigJson = path.resolve(process.env.UNI_INPUT_DIR, 'tsconfig.json')
  const defaultTsConfigJson = path.resolve(process.env.UNI_CLI_CONTEXT, 'tsconfig.json')

  const tsConfigJsonFile = fs.existsSync(userTsConfigJson) ? userTsConfigJson : defaultTsConfigJson

fxy060608's avatar
fxy060608 已提交
105 106
  const context = isInHBuilderX ? process.env.UNI_INPUT_DIR : process.env.UNI_CLI_CONTEXT

fxy060608's avatar
fxy060608 已提交
107
  const tsLoaderOptions = {
fxy060608's avatar
fxy060608 已提交
108
    context,
fxy060608's avatar
fxy060608 已提交
109 110
    configFile: tsConfigJsonFile,
    compilerOptions: {
fxy060608's avatar
fxy060608 已提交
111
      baseUrl: context,
Q
qiang 已提交
112 113 114 115 116
      typeRoots: [
        resolveModule('@dcloudio/types'),
        resolveModule('@types'),
        path.resolve(process.env.UNI_CLI_CONTEXT, 'types')
      ],
fxy060608's avatar
fxy060608 已提交
117 118
      types: [
        'uni-app',
Q
qiang 已提交
119
        'uni-app-vue2',
fxy060608's avatar
fxy060608 已提交
120 121 122 123 124 125
        'webpack-env'
      ],
      paths: {
        '@/*': [
          path.join(process.env.UNI_INPUT_DIR, '*')
        ],
fxy060608's avatar
fxy060608 已提交
126
        vue: [
fxy060608's avatar
fxy060608 已提交
127 128
          resolveModule('vue')
        ],
fxy060608's avatar
fxy060608 已提交
129
        vuex: [
fxy060608's avatar
fxy060608 已提交
130 131 132 133 134 135 136 137
          resolveModule('vuex')
        ],
        'vue-class-component': [
          resolveModule('vue-class-component')
        ],
        'vue-property-decorator': [
          resolveModule('vue-property-decorator')
        ],
fxy060608's avatar
fxy060608 已提交
138
        tslib: [
fxy060608's avatar
fxy060608 已提交
139 140 141 142
          resolveModule('tslib')
        ],
        'mpvue-page-factory': [
          resolveModule('@dcloudio/vue-cli-plugin-uni/packages/mpvue-page-factory')
Q
qiang 已提交
143 144 145 146 147 148
        ],
        '@vue/composition-api': [
          resolveModule('@dcloudio/vue-cli-plugin-uni/packages/@vue/composition-api')
        ],
        '@dcloudio/uni-app': [
          resolveModule('@dcloudio/uni-app')
fxy060608's avatar
fxy060608 已提交
149 150 151 152 153 154 155 156
        ]
      }
    },
    errorFormatter (error, colors) {
      const messageColor = error.severity === 'warning' ? colors.bold.yellow : colors.bold.red
      const filePath = path.relative(process.env.UNI_INPUT_DIR, error.file).replace('.vue.ts', '.vue')
      if (error.code === 2307 && error.content.includes('.vue')) {
        error.content = error.content.replace('Cannot find module ', '') +
fxy060608's avatar
fxy060608 已提交
157
          ' script 节点必须使用 lang="ts",文档参考地址:https://uniapp.dcloud.io/frame?id=vue-ts'
fxy060608's avatar
fxy060608 已提交
158 159 160 161 162 163 164 165 166 167 168
      }
      return messageColor(
        `[tsl] ERROR at ${filePath}:${error.line}
  TS${error.code}:${error.content}`
      )
    }
  }

  function updateTsLoader (rawRules, fakeFile, loader) {
    const matchRule = rawRules.find(createMatcher(fakeFile))
    if (matchRule && matchRule.use) {
Q
qiang 已提交
169
      if (isInHBuilderX) {
fxy060608's avatar
fxy060608 已提交
170
        matchRule.use.forEach(matchUse => {
fxy060608's avatar
fxy060608 已提交
171
          if (matchUse.loader.includes('ts-loader')) {
fxy060608's avatar
fxy060608 已提交
172 173 174 175 176 177 178 179 180 181 182
            Object.assign(matchUse.options, tsLoaderOptions)
          }
        })
      }
      matchRule.use.push(loader)
    }
  }

  function removeForkTsCheckerWebpackPlugin (rawPlugins) {
    if (isInHBuilderX && hasModule('fork-ts-checker-webpack-plugin')) {
      const pluginIndex = rawPlugins.findIndex(rawPlugin => rawPlugin.vue && rawPlugin.typescriptVersion)
183 184
      if (pluginIndex !== -1) {
        // 移除fork-ts-checker-webpack-plugin
fxy060608's avatar
fxy060608 已提交
185
        rawPlugins.splice(pluginIndex, 1)
186 187
        // 恢复vue-loader的ts检查
        tsLoaderOptions.transpileOnly = false
fxy060608's avatar
fxy060608 已提交
188 189 190
      }
    }
  }
191 192
  const babelLoaderRe = /^babel-loader|(\/|\\|@)babel-loader/
  const cacheLoaderRe = /^cache-loader|(\/|\\|@)cache-loader/
fxy060608's avatar
fxy060608 已提交
193 194 195 196 197 198
  return function (webpackConfig) {
    // disable js cache-loader
    const rawRules = webpackConfig.module.rules
    for (let i = rawRules.length - 1; i >= 0; i--) {
      const uses = rawRules[i].use
      if (Array.isArray(uses)) {
199 200 201 202
        const babelLoader = uses.find(use => babelLoaderRe.test(use.loader))
        if (babelLoader) {
          const options = api.genCacheConfig('babel-loader/' + process.env.UNI_PLATFORM, getPartialIdentifier())
          if (webpack.version[0] > 4) {
Q
qiang 已提交
203 204 205 206
            babelLoader.options = babelLoader.options || {}
            Object.assign(babelLoader.options, process.env.UNI_USING_CACHE ? options : {
              cacheDirectory: false
            })
207 208 209 210 211 212 213 214
          } else {
            const index = uses.findIndex(use => cacheLoaderRe.test(use.loader))
            if (index >= 0) {
              if (process.env.UNI_USING_CACHE) {
                Object.assign(uses[index].options, options)
              } else {
                uses.splice(index, 1)
              }
Q
qiang 已提交
215
            }
fxy060608's avatar
fxy060608 已提交
216
          }
fxy060608's avatar
fxy060608 已提交
217 218 219 220
        }
      }
    }

221 222
    // 如果在 HBuilderX 中
    removeForkTsCheckerWebpackPlugin(webpackConfig.plugins)
fxy060608's avatar
fxy060608 已提交
223
    // js preprocess
224
    updateJsLoader(rawRules, 'foo.js', babelLoaderRe, {
fxy060608's avatar
fxy060608 已提交
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
      loader: resolve('packages/webpack-preprocess-loader'),
      options: jsPreprocessOptions
    })
    // ts options and preprocess
    updateTsLoader(rawRules, 'foo.ts', {
      loader: resolve('packages/webpack-preprocess-loader'),
      options: jsPreprocessOptions
    })
    updateTsLoader(rawRules, 'foo.tsx', {
      loader: resolve('packages/webpack-preprocess-loader'),
      options: jsPreprocessOptions
    })

    let platformWebpackConfig = platformOptions.webpackConfig
    if (typeof platformWebpackConfig === 'function') {
fxy060608's avatar
fxy060608 已提交
240
      platformWebpackConfig = platformWebpackConfig(webpackConfig, vueOptions, api)
fxy060608's avatar
fxy060608 已提交
241 242
    }
    // 移除 node_modules 目录,避免受路径上的 node_modules 影响
Q
qiang 已提交
243 244 245
    if (require('@dcloudio/uni-cli-shared/lib/util').isInHBuilderX) {
      webpackConfig.resolve.modules = webpackConfig.resolve.modules.filter(module => module !== 'node_modules')
    }
fxy060608's avatar
fxy060608 已提交
246

fxy060608's avatar
fxy060608 已提交
247
    const plugins = []
fxy060608's avatar
fxy060608 已提交
248 249 250 251

    const isAppView = process.env.UNI_PLATFORM === 'app-plus' &&
      vueOptions.pluginOptions &&
      vueOptions.pluginOptions['uni-app-plus'] &&
fxy060608's avatar
fxy060608 已提交
252
      vueOptions.pluginOptions['uni-app-plus'].view
fxy060608's avatar
fxy060608 已提交
253 254

    if (!isAppView) { // app-plus view不需要copy
255
      const patterns = getCopyWebpackPluginOptions(manifestPlatformOptions, vueOptions)
fxy060608's avatar
fxy060608 已提交
256 257 258
      plugins.push(new CopyWebpackPlugin(CopyWebpackPluginVersion > 5 ? {
        patterns
      } : patterns))
fxy060608's avatar
fxy060608 已提交
259 260 261 262 263

      const uniExtApis = require('@dcloudio/uni-cli-shared/lib/uni_modules/uni_modules')
        .parseUniExtApis(false)
      const keys = Object.keys(uniExtApis)
      if (keys.length) {
fxy060608's avatar
fxy060608 已提交
264
        plugins.push(new webpack.ProvidePlugin(uniExtApis))
fxy060608's avatar
fxy060608 已提交
265
      }
fxy060608's avatar
fxy060608 已提交
266
    }
267
    if (!process.env.UNI_SUBPACKGE || !process.env.UNI_MP_PLUGIN) {
268 269
      try {
        const automatorJson = require.resolve('@dcloudio/uni-automator/dist/automator.json')
270
        const patterns = [{
271 272 273 274 275 276 277 278 279 280 281
          from: automatorJson,
          to: '../.automator/' + (process.env.UNI_SUB_PLATFORM || process.env.UNI_PLATFORM) +
            '/.automator.json',
          transform (content) {
            if (process.env.UNI_AUTOMATOR_WS_ENDPOINT) {
              return JSON.stringify({
                version: require('@dcloudio/uni-automator/package.json').version,
                wsEndpoint: process.env.UNI_AUTOMATOR_WS_ENDPOINT
              })
            }
            return ''
fxy060608's avatar
fxy060608 已提交
282
          }
283
        }]
fxy060608's avatar
fxy060608 已提交
284 285 286
        plugins.push(new CopyWebpackPlugin(CopyWebpackPluginVersion > 5 ? {
          patterns
        } : patterns))
Q
qiang 已提交
287
      } catch (e) { }
288
    }
fxy060608's avatar
fxy060608 已提交
289

fxy060608's avatar
fxy060608 已提交
290 291 292 293 294 295 296 297 298 299 300 301
    if (process.UNI_SCRIPT_ENV && Object.keys(process.UNI_SCRIPT_ENV).length) {
      // custom define
      const envs = Object.create(null)
      Object.keys(process.UNI_SCRIPT_ENV).forEach(name => {
        envs['process.env.' + name] = JSON.stringify(process.UNI_SCRIPT_ENV[name])
      })
      plugins.push(new webpack.DefinePlugin(envs))
    }

    if (runByHBuilderX) { // 使用 HBuilderX 中运行时,调整错误日志输出
      const WebpackErrorsPlugin = require('../packages/webpack-errors-plugin')
      const onErrors = require('../util/on-errors')
302
      const onWarnings = require('../util/on-warnings')
fxy060608's avatar
fxy060608 已提交
303
      plugins.push(new WebpackErrorsPlugin({
fxy060608's avatar
fxy060608 已提交
304
        onErrors,
305
        onWarnings
fxy060608's avatar
fxy060608 已提交
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
      }))
    }

    const rules = [{
      test: path.resolve(process.env.UNI_INPUT_DIR, 'pages.json'),
      use: [{
        loader: 'babel-loader'
      }, {
        loader: '@dcloudio/webpack-uni-pages-loader'
      }],
      type: 'javascript/auto'
    },
    {
      resourceQuery: /vue&type=template/,
      use: [{
        loader: resolve('packages/webpack-preprocess-loader'),
        options: htmlPreprocessOptions
      }]
    }
    ]

    if (!process.env.UNI_USING_COMPONENTS) { // 新版本,在 script-loader 中处理(为了避免 babel generator 移除部分条件编译代码)
      rules.push({
        resourceQuery: /vue&type=script/,
        use: [{
          loader: resolve('packages/webpack-preprocess-loader'),
          options: jsPreprocessOptions
        }]
      })
    }

fxy060608's avatar
fxy060608 已提交
337 338
    if (process.env.NODE_ENV === 'development' || (process.env.NODE_ENV === 'production' && process.env
      .SOURCEMAP === 'true')) {
fxy060608's avatar
fxy060608 已提交
339 340
      const sourceMap = require('@dcloudio/uni-cli-shared/lib/source-map')
      let isAppService = false
fxy060608's avatar
fxy060608 已提交
341 342 343 344 345
      if (
        process.env.UNI_PLATFORM === 'app-plus' &&
        vueOptions.pluginOptions &&
        vueOptions.pluginOptions['uni-app-plus']
      ) {
fxy060608's avatar
fxy060608 已提交
346 347
        isAppService = !!vueOptions.pluginOptions['uni-app-plus'].service
      }
D
DCloud_LXH 已提交
348 349 350

      const useEvalSourceMap = process.env.UNI_PLATFORM === 'h5' || isAppService
      const useSourceMap = process.env.UNI_PLATFORM.indexOf('mp-') === 0 &&
fxy060608's avatar
fxy060608 已提交
351 352 353
        process.env.UNI_PLATFORM !== 'mp-baidu' &&
        process.env.UNI_PLATFORM !== 'mp-alipay' &&
        process.env.UNI_PLATFORM !== 'quickapp-webview' // 目前 ov 的开发工具支持 eval 模式
D
DCloud_LXH 已提交
354 355 356 357 358 359

      if (process.env.NODE_ENV === 'production') {
        const sourceMapOptions = {
          noSources: true,
          append: false
        }
fxy060608's avatar
fxy060608 已提交
360 361 362
        if (isInHBuilderX && process.env.SOURCEMAP_PATH) {
          sourceMapOptions.filename = process.env.SOURCEMAP_PATH
        }
D
DCloud_LXH 已提交
363 364 365 366 367 368 369
        if (useEvalSourceMap || useSourceMap) {
          plugins.push(sourceMap.createSourceMapDevToolPlugin(!sourceMapOptions.filename, sourceMapOptions))
        }
      } else {
        if (useEvalSourceMap) {
          plugins.push(sourceMap.createEvalSourceMapDevToolPlugin())
        } else if (useSourceMap) {
fxy060608's avatar
fxy060608 已提交
370 371
          plugins.push(sourceMap.createSourceMapDevToolPlugin(process.env.UNI_PLATFORM === 'mp-weixin' || process
            .env.UNI_PLATFORM === 'mp-toutiao'))
D
DCloud_LXH 已提交
372
        }
fxy060608's avatar
fxy060608 已提交
373 374
      }
    }
fxy060608's avatar
fxy060608 已提交
375

fxy060608's avatar
fxy060608 已提交
376 377 378 379 380 381 382 383 384 385
    try {
      if (process.env.UNI_HBUILDERX_PLUGINS) {
        require(path.resolve(process.env.UNI_HBUILDERX_PLUGINS, 'uni_helpers/lib/bytenode'))
        const {
          W
        } = require(path.resolve(process.env.UNI_HBUILDERX_PLUGINS, 'uni_helpers'))
        plugins.push(new W({
          dir: process.env.UNI_INPUT_DIR
        }))
      }
Q
qiang 已提交
386
    } catch (e) { }
fxy060608's avatar
fxy060608 已提交
387

Q
qiang 已提交
388 389 390
    const resolveLoaderAlias = {}
    const modules = ['@vue/cli-plugin-babel', '@vue/cli-service']
    modules.forEach(m => {
fxy060608's avatar
fxy060608 已提交
391 392 393
      const {
        dependencies
      } = require(`${m}/package.json`)
Q
qiang 已提交
394 395 396 397 398 399 400
      Object.keys(dependencies).forEach(key => {
        if (/-loader$/.test(key)) {
          resolveLoaderAlias[key] = require.resolve(key)
        }
      })
    })

401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
    const alias = {
      '@': path.resolve(process.env.UNI_INPUT_DIR),
      './@': path.resolve(process.env
        .UNI_INPUT_DIR), // css中的'@/static/logo.png'会被转换成'./@/static/logo.png'加载
      vue$: getPlatformVue(vueOptions),
      'uni-pages': path.resolve(process.env.UNI_INPUT_DIR, 'pages.json'),
      'uni-stat-config': path.resolve(process.env.UNI_INPUT_DIR, 'pages.json') +
        '?' +
        JSON.stringify({
          type: 'stat'
        }),
      vuex: require.resolve('@dcloudio/vue-cli-plugin-uni/packages/vuex3'),
      '@vue/composition-api': require.resolve('@dcloudio/vue-cli-plugin-uni/packages/@vue/composition-api')
    }

    if (process.env.UNI_PLATFORM.startsWith('mp')) {
      const BabelRuntimeVersions = require('@babel/runtime/package.json').version.split('.')
      if (BabelRuntimeVersions[0] === '7' && Number(BabelRuntimeVersions[1]) >= 18) {
        alias['@babel/runtime/regenerator'] = require.resolve('@dcloudio/vue-cli-plugin-uni/packages/@babel/runtime/regenerator')
      }
    }

fxy060608's avatar
fxy060608 已提交
423
    return merge({
fxy060608's avatar
fxy060608 已提交
424
      devtool: false,
fxy060608's avatar
fxy060608 已提交
425
      resolve: {
426
        alias,
fxy060608's avatar
fxy060608 已提交
427 428 429
        modules: [
          process.env.UNI_INPUT_DIR,
          path.resolve(process.env.UNI_INPUT_DIR, 'node_modules')
fxy060608's avatar
fxy060608 已提交
430 431 432
        ],
        plugins: [
          new uts.UTSResolverPlugin()
fxy060608's avatar
fxy060608 已提交
433 434 435 436 437 438
        ]
      },
      module: {
        noParse: /^(vue|vue-router|vuex|vuex-router-sync)$/,
        rules
      },
Q
qiang 已提交
439 440 441
      resolveLoader: {
        alias: resolveLoaderAlias
      },
fxy060608's avatar
fxy060608 已提交
442 443 444 445 446
      plugins,
      performance: {
        assetFilter (assetFilename) {
          return !(/\.map$/.test(assetFilename)) && !(/vendor/.test(assetFilename))
        }
fxy060608's avatar
fxy060608 已提交
447 448
      },
      watchOptions: require('./util').getWatchOptions()
fxy060608's avatar
fxy060608 已提交
449 450
    }, platformWebpackConfig)
  }
Q
qiang 已提交
451
}