configure-webpack.js 14.0 KB
Newer Older
fxy060608's avatar
fxy060608 已提交
1 2 3 4 5 6 7
const fs = require('fs')
const path = require('path')
const webpack = require('webpack')
const CopyWebpackPlugin = require('copy-webpack-plugin')

const merge = require('webpack-merge')

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

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

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

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

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

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

  function createMatcher (fakeFile) {
    return (rule, i) => {
      const clone = Object.assign({}, rule)
      delete clone.include
41 42 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
      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')
fxy060608's avatar
fxy060608 已提交
72 73 74 75 76 77 78 79
      const normalized = RuleSet.normalizeRule(clone, {}, '')
      return (
        !rule.enforce &&
        normalized.resource &&
        normalized.resource(fakeFile)
      )
    }
  }
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

  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 已提交
103 104
  const context = isInHBuilderX ? process.env.UNI_INPUT_DIR : process.env.UNI_CLI_CONTEXT

fxy060608's avatar
fxy060608 已提交
105
  const tsLoaderOptions = {
fxy060608's avatar
fxy060608 已提交
106
    context,
fxy060608's avatar
fxy060608 已提交
107 108 109
    configFile: tsConfigJsonFile,
    transpileOnly: false,
    compilerOptions: {
fxy060608's avatar
fxy060608 已提交
110
      baseUrl: context,
fxy060608's avatar
fxy060608 已提交
111 112 113 114 115 116 117 118 119
      typeRoots: [resolveModule('@dcloudio/types'), resolveModule('@types')],
      types: [
        'uni-app',
        'webpack-env'
      ],
      paths: {
        '@/*': [
          path.join(process.env.UNI_INPUT_DIR, '*')
        ],
fxy060608's avatar
fxy060608 已提交
120
        vue: [
fxy060608's avatar
fxy060608 已提交
121 122
          resolveModule('vue')
        ],
fxy060608's avatar
fxy060608 已提交
123
        vuex: [
fxy060608's avatar
fxy060608 已提交
124 125 126 127 128 129 130 131
          resolveModule('vuex')
        ],
        'vue-class-component': [
          resolveModule('vue-class-component')
        ],
        'vue-property-decorator': [
          resolveModule('vue-property-decorator')
        ],
fxy060608's avatar
fxy060608 已提交
132
        tslib: [
fxy060608's avatar
fxy060608 已提交
133 134 135 136 137 138 139 140 141 142 143 144
          resolveModule('tslib')
        ],
        'mpvue-page-factory': [
          resolveModule('@dcloudio/vue-cli-plugin-uni/packages/mpvue-page-factory')
        ]
      }
    },
    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 已提交
145
          ' script 节点必须使用 lang="ts",文档参考地址:https://uniapp.dcloud.io/frame?id=vue-ts'
fxy060608's avatar
fxy060608 已提交
146 147 148 149 150 151 152 153 154 155 156 157 158
      }
      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) {
      if (runByHBuilderX) {
        matchRule.use.forEach(matchUse => {
fxy060608's avatar
fxy060608 已提交
159
          if (matchUse.loader.includes('ts-loader')) {
fxy060608's avatar
fxy060608 已提交
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
            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)
      if (pluginIndex !== -1) { // 移除fork-ts-checker-webpack-plugin
        rawPlugins.splice(pluginIndex, 1)
      }
    }
  }
176 177
  const babelLoaderRe = /^babel-loader|(\/|\\|@)babel-loader/
  const cacheLoaderRe = /^cache-loader|(\/|\\|@)cache-loader/
fxy060608's avatar
fxy060608 已提交
178 179 180 181 182 183
  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)) {
184 185
        if (uses.find(use => babelLoaderRe.test(use.loader))) {
          const index = uses.findIndex(use => cacheLoaderRe.test(use.loader))
186
          if (index >= 0) {
fxy060608's avatar
fxy060608 已提交
187 188 189 190 191 192 193 194
          if (process.env.UNI_USING_CACHE) {
            Object.assign(uses[index].options, api.genCacheConfig(
              'babel-loader/' + process.env.UNI_PLATFORM,
              getPartialIdentifier()
            ))
          } else {
            uses.splice(index, 1)
          }
fxy060608's avatar
fxy060608 已提交
195 196 197
        }
      }
    }
198
    }
fxy060608's avatar
fxy060608 已提交
199 200

    // js preprocess
201
    updateJsLoader(rawRules, 'foo.js', babelLoaderRe, {
fxy060608's avatar
fxy060608 已提交
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
      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
    })
    // 如果在 HBuilderX 中
    removeForkTsCheckerWebpackPlugin(webpackConfig.plugins)

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

fxy060608's avatar
fxy060608 已提交
226 227 228 229 230
    const plugins = []

    const isAppView = process.env.UNI_PLATFORM === 'app-plus' &&
      vueOptions.pluginOptions &&
      vueOptions.pluginOptions['uni-app-plus'] &&
fxy060608's avatar
fxy060608 已提交
231
      vueOptions.pluginOptions['uni-app-plus'].view
fxy060608's avatar
fxy060608 已提交
232 233 234 235

    if (!isAppView) { // app-plus view不需要copy
      plugins.push(new CopyWebpackPlugin(getCopyWebpackPluginOptions(manifestPlatformOptions, vueOptions)))
    }
236
    if (!process.env.UNI_SUBPACKGE || !process.env.UNI_MP_PLUGIN) {
237 238 239 240 241 242 243 244 245 246 247 248 249 250
      try {
        const automatorJson = require.resolve('@dcloudio/uni-automator/dist/automator.json')
        plugins.push(new CopyWebpackPlugin([{
          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 已提交
251
          }
252 253 254
        }]))
      } catch (e) {}
    }
fxy060608's avatar
fxy060608 已提交
255

fxy060608's avatar
fxy060608 已提交
256 257 258 259 260 261 262 263 264 265 266 267
    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')
268
      const onWarnings = require('../util/on-warnings')
fxy060608's avatar
fxy060608 已提交
269
      plugins.push(new WebpackErrorsPlugin({
fxy060608's avatar
fxy060608 已提交
270
        onErrors,
271
        onWarnings
fxy060608's avatar
fxy060608 已提交
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
      }))
    }

    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
        }]
      })
    }

D
DCloud_LXH 已提交
303
    if (process.env.NODE_ENV === 'development' || (process.env.NODE_ENV === 'production' && process.env.SOURCEMAP === 'true')) {
fxy060608's avatar
fxy060608 已提交
304 305
      const sourceMap = require('@dcloudio/uni-cli-shared/lib/source-map')
      let isAppService = false
fxy060608's avatar
fxy060608 已提交
306 307 308 309 310
      if (
        process.env.UNI_PLATFORM === 'app-plus' &&
        vueOptions.pluginOptions &&
        vueOptions.pluginOptions['uni-app-plus']
      ) {
fxy060608's avatar
fxy060608 已提交
311 312
        isAppService = !!vueOptions.pluginOptions['uni-app-plus'].service
      }
D
DCloud_LXH 已提交
313 314 315

      const useEvalSourceMap = process.env.UNI_PLATFORM === 'h5' || isAppService
      const useSourceMap = process.env.UNI_PLATFORM.indexOf('mp-') === 0 &&
fxy060608's avatar
fxy060608 已提交
316 317 318
        process.env.UNI_PLATFORM !== 'mp-baidu' &&
        process.env.UNI_PLATFORM !== 'mp-alipay' &&
        process.env.UNI_PLATFORM !== 'quickapp-webview' // 目前 ov 的开发工具支持 eval 模式
D
DCloud_LXH 已提交
319 320 321 322 323 324

      if (process.env.NODE_ENV === 'production') {
        const sourceMapOptions = {
          noSources: true,
          append: false
        }
fxy060608's avatar
fxy060608 已提交
325
        if (isInHBuilderX && process.env.SOURCEMAP_PATH) { sourceMapOptions.filename = process.env.SOURCEMAP_PATH }
D
DCloud_LXH 已提交
326 327 328 329 330 331 332 333 334
        if (useEvalSourceMap || useSourceMap) {
          plugins.push(sourceMap.createSourceMapDevToolPlugin(!sourceMapOptions.filename, sourceMapOptions))
        }
      } else {
        if (useEvalSourceMap) {
          plugins.push(sourceMap.createEvalSourceMapDevToolPlugin())
        } else if (useSourceMap) {
          plugins.push(sourceMap.createSourceMapDevToolPlugin(process.env.UNI_PLATFORM === 'mp-weixin' || process.env.UNI_PLATFORM === 'mp-toutiao'))
        }
fxy060608's avatar
fxy060608 已提交
335 336
      }
    }
fxy060608's avatar
fxy060608 已提交
337

fxy060608's avatar
fxy060608 已提交
338 339 340 341 342 343 344 345 346 347 348 349
    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
        }))
      }
    } catch (e) {}

Q
qiang 已提交
350 351 352
    const resolveLoaderAlias = {}
    const modules = ['@vue/cli-plugin-babel', '@vue/cli-service']
    modules.forEach(m => {
fxy060608's avatar
fxy060608 已提交
353 354 355
      const {
        dependencies
      } = require(`${m}/package.json`)
Q
qiang 已提交
356 357 358 359 360 361 362
      Object.keys(dependencies).forEach(key => {
        if (/-loader$/.test(key)) {
          resolveLoaderAlias[key] = require.resolve(key)
        }
      })
    })

fxy060608's avatar
fxy060608 已提交
363
    return merge({
fxy060608's avatar
fxy060608 已提交
364
      devtool: false,
fxy060608's avatar
fxy060608 已提交
365 366 367
      resolve: {
        alias: {
          '@': path.resolve(process.env.UNI_INPUT_DIR),
fxy060608's avatar
fxy060608 已提交
368 369
          './@': path.resolve(process.env
            .UNI_INPUT_DIR), // css中的'@/static/logo.png'会被转换成'./@/static/logo.png'加载
fxy060608's avatar
fxy060608 已提交
370
          vue$: getPlatformVue(vueOptions),
fxy060608's avatar
fxy060608 已提交
371 372 373 374 375
          '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'
D
DCloud_LXH 已提交
376 377
            }),
          vuex: require.resolve('@dcloudio/vue-cli-plugin-uni/packages/vuex3')
fxy060608's avatar
fxy060608 已提交
378 379 380 381 382 383 384 385 386 387
        },
        modules: [
          process.env.UNI_INPUT_DIR,
          path.resolve(process.env.UNI_INPUT_DIR, 'node_modules')
        ]
      },
      module: {
        noParse: /^(vue|vue-router|vuex|vuex-router-sync)$/,
        rules
      },
Q
qiang 已提交
388 389 390
      resolveLoader: {
        alias: resolveLoaderAlias
      },
fxy060608's avatar
fxy060608 已提交
391 392 393 394 395
      plugins,
      performance: {
        assetFilter (assetFilename) {
          return !(/\.map$/.test(assetFilename)) && !(/vendor/.test(assetFilename))
        }
fxy060608's avatar
fxy060608 已提交
396 397
      },
      watchOptions: require('./util').getWatchOptions()
fxy060608's avatar
fxy060608 已提交
398 399
    }, platformWebpackConfig)
  }
D
DCloud_LXH 已提交
400
}