base.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. module.exports = (api, options) => {
  2. api.chainWebpack(webpackConfig => {
  3. const isLegacyBundle = process.env.VUE_CLI_MODERN_MODE && !process.env.VUE_CLI_MODERN_BUILD
  4. const resolveLocal = require('../util/resolveLocal')
  5. const getAssetPath = require('../util/getAssetPath')
  6. const inlineLimit = 4096
  7. const genAssetSubPath = dir => {
  8. return getAssetPath(
  9. options,
  10. `${dir}/[name]${options.filenameHashing ? '.[hash:8]' : ''}.[ext]`
  11. )
  12. }
  13. const genUrlLoaderOptions = dir => {
  14. return {
  15. limit: inlineLimit,
  16. // use explicit fallback to avoid regression in url-loader>=1.1.0
  17. fallback: {
  18. loader: require.resolve('file-loader'),
  19. options: {
  20. name: genAssetSubPath(dir)
  21. }
  22. }
  23. }
  24. }
  25. webpackConfig
  26. .mode('development')
  27. .context(api.service.context)
  28. .entry('app')
  29. .add('./src/main.js')
  30. .end()
  31. .output
  32. .path(api.resolve(options.outputDir))
  33. .filename(isLegacyBundle ? '[name]-legacy.js' : '[name].js')
  34. .publicPath(options.publicPath)
  35. webpackConfig.resolve
  36. // This plugin can be removed once we switch to Webpack 6
  37. .plugin('pnp')
  38. .use({ ...require('pnp-webpack-plugin') })
  39. .end()
  40. .extensions
  41. .merge(['.mjs', '.js', '.jsx', '.vue', '.json', '.wasm'])
  42. .end()
  43. .modules
  44. .add('node_modules')
  45. .add(api.resolve('node_modules'))
  46. .add(resolveLocal('node_modules'))
  47. .end()
  48. .alias
  49. .set('@', api.resolve('src'))
  50. .set(
  51. 'vue$',
  52. options.runtimeCompiler
  53. ? 'vue/dist/vue.esm.js'
  54. : 'vue/dist/vue.runtime.esm.js'
  55. )
  56. webpackConfig.resolveLoader
  57. .plugin('pnp-loaders')
  58. .use({ ...require('pnp-webpack-plugin').topLevelLoader })
  59. .end()
  60. .modules
  61. .add('node_modules')
  62. .add(api.resolve('node_modules'))
  63. .add(resolveLocal('node_modules'))
  64. webpackConfig.module
  65. .noParse(/^(vue|vue-router|vuex|vuex-router-sync)$/)
  66. // js is handled by cli-plugin-babel ---------------------------------------
  67. // vue-loader --------------------------------------------------------------
  68. const vueLoaderCacheIdentifier = {
  69. 'vue-loader': require('vue-loader/package.json').version
  70. }
  71. // The following 2 deps are sure to exist in Vue 2 projects.
  72. // But once we switch to Vue 3, they're no longer mandatory.
  73. // (In Vue 3 they are replaced by @vue/compiler-sfc)
  74. // So wrap them in a try catch block.
  75. try {
  76. vueLoaderCacheIdentifier['@vue/component-compiler-utils'] =
  77. require('@vue/component-compiler-utils/package.json').version
  78. vueLoaderCacheIdentifier['vue-template-compiler'] =
  79. require('vue-template-compiler/package.json').version
  80. } catch (e) {}
  81. const vueLoaderCacheConfig = api.genCacheConfig('vue-loader', vueLoaderCacheIdentifier)
  82. webpackConfig.module
  83. .rule('vue')
  84. .test(/\.vue$/)
  85. .use('cache-loader')
  86. .loader(require.resolve('cache-loader'))
  87. .options(vueLoaderCacheConfig)
  88. .end()
  89. .use('vue-loader')
  90. .loader(require.resolve('vue-loader'))
  91. .options(Object.assign({
  92. compilerOptions: {
  93. whitespace: 'condense'
  94. }
  95. }, vueLoaderCacheConfig))
  96. webpackConfig
  97. .plugin('vue-loader')
  98. .use(require('vue-loader/lib/plugin'))
  99. // static assets -----------------------------------------------------------
  100. webpackConfig.module
  101. .rule('images')
  102. .test(/\.(png|jpe?g|gif|webp)(\?.*)?$/)
  103. .use('url-loader')
  104. .loader(require.resolve('url-loader'))
  105. .options(genUrlLoaderOptions('img'))
  106. // do not base64-inline SVGs.
  107. // https://github.com/facebookincubator/create-react-app/pull/1180
  108. webpackConfig.module
  109. .rule('svg')
  110. .test(/\.(svg)(\?.*)?$/)
  111. .use('file-loader')
  112. .loader(require.resolve('file-loader'))
  113. .options({
  114. name: genAssetSubPath('img')
  115. })
  116. webpackConfig.module
  117. .rule('media')
  118. .test(/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/)
  119. .use('url-loader')
  120. .loader(require.resolve('url-loader'))
  121. .options(genUrlLoaderOptions('media'))
  122. webpackConfig.module
  123. .rule('fonts')
  124. .test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/i)
  125. .use('url-loader')
  126. .loader(require.resolve('url-loader'))
  127. .options(genUrlLoaderOptions('fonts'))
  128. // Other common pre-processors ---------------------------------------------
  129. const maybeResolve = name => {
  130. try {
  131. return require.resolve(name)
  132. } catch (error) {
  133. return name
  134. }
  135. }
  136. webpackConfig.module
  137. .rule('pug')
  138. .test(/\.pug$/)
  139. .oneOf('pug-vue')
  140. .resourceQuery(/vue/)
  141. .use('pug-plain-loader')
  142. .loader(maybeResolve('pug-plain-loader'))
  143. .end()
  144. .end()
  145. .oneOf('pug-template')
  146. .use('raw')
  147. .loader(maybeResolve('raw-loader'))
  148. .end()
  149. .use('pug-plain-loader')
  150. .loader(maybeResolve('pug-plain-loader'))
  151. .end()
  152. .end()
  153. // shims
  154. webpackConfig.node
  155. .merge({
  156. // prevent webpack from injecting useless setImmediate polyfill because Vue
  157. // source contains it (although only uses it if it's native).
  158. setImmediate: false,
  159. // process is injected via DefinePlugin, although some 3rd party
  160. // libraries may require a mock to work properly (#934)
  161. process: 'mock',
  162. // prevent webpack from injecting mocks to Node native modules
  163. // that does not make sense for the client
  164. dgram: 'empty',
  165. fs: 'empty',
  166. net: 'empty',
  167. tls: 'empty',
  168. child_process: 'empty'
  169. })
  170. const resolveClientEnv = require('../util/resolveClientEnv')
  171. webpackConfig
  172. .plugin('define')
  173. .use(require('webpack').DefinePlugin, [
  174. resolveClientEnv(options)
  175. ])
  176. webpackConfig
  177. .plugin('case-sensitive-paths')
  178. .use(require('case-sensitive-paths-webpack-plugin'))
  179. // friendly error plugin displays very confusing errors when webpack
  180. // fails to resolve a loader, so we provide custom handlers to improve it
  181. const { transformer, formatter } = require('../util/resolveLoaderError')
  182. webpackConfig
  183. .plugin('friendly-errors')
  184. .use(require('@soda/friendly-errors-webpack-plugin'), [{
  185. additionalTransformers: [transformer],
  186. additionalFormatters: [formatter]
  187. }])
  188. const TerserPlugin = require('terser-webpack-plugin')
  189. const terserOptions = require('./terserOptions')
  190. webpackConfig.optimization
  191. .minimizer('terser')
  192. .use(TerserPlugin, [terserOptions(options)])
  193. })
  194. }