base.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. const path = require('path')
  2. /** @type {import('@vue/cli-service').ServicePlugin} */
  3. module.exports = (api, options) => {
  4. const cwd = api.getCwd()
  5. const webpack = require('webpack')
  6. const vueMajor = require('../util/getVueMajor')(cwd)
  7. api.chainWebpack(webpackConfig => {
  8. const isLegacyBundle = process.env.VUE_CLI_MODERN_MODE && !process.env.VUE_CLI_MODERN_BUILD
  9. const resolveLocal = require('../util/resolveLocal')
  10. // https://github.com/webpack/webpack/issues/14532#issuecomment-947525539
  11. webpackConfig.output.set('hashFunction', 'xxhash64')
  12. // https://github.com/webpack/webpack/issues/11467#issuecomment-691873586
  13. webpackConfig.module
  14. .rule('esm')
  15. .test(/\.m?jsx?$/)
  16. .resolve.set('fullySpecified', false)
  17. webpackConfig
  18. .mode('development')
  19. .context(api.service.context)
  20. .entry('app')
  21. .add('./src/main.js')
  22. .end()
  23. .output
  24. .path(api.resolve(options.outputDir))
  25. .filename(isLegacyBundle ? '[name]-legacy.js' : '[name].js')
  26. .publicPath(options.publicPath)
  27. webpackConfig.resolve
  28. .extensions
  29. .merge(['.mjs', '.js', '.jsx', '.vue', '.json', '.wasm'])
  30. .end()
  31. .modules
  32. .add('node_modules')
  33. .add(api.resolve('node_modules'))
  34. .add(resolveLocal('node_modules'))
  35. .end()
  36. .alias
  37. .set('@', api.resolve('src'))
  38. webpackConfig.resolveLoader
  39. .modules
  40. .add('node_modules')
  41. .add(api.resolve('node_modules'))
  42. .add(resolveLocal('node_modules'))
  43. webpackConfig.module
  44. .noParse(/^(vue|vue-router|vuex|vuex-router-sync)$/)
  45. // js is handled by cli-plugin-babel ---------------------------------------
  46. // vue-loader --------------------------------------------------------------
  47. let cacheLoaderPath
  48. try {
  49. cacheLoaderPath = require.resolve('cache-loader')
  50. } catch (e) {}
  51. if (vueMajor === 2) {
  52. // for Vue 2 projects
  53. const partialIdentifier = {
  54. 'vue-loader': require('@vue/vue-loader-v15/package.json').version,
  55. '@vue/component-compiler-utils': require('@vue/component-compiler-utils/package.json').version
  56. }
  57. try {
  58. partialIdentifier['vue-template-compiler'] = require('vue-template-compiler/package.json').version
  59. } catch (e) {
  60. // For Vue 2.7 projects, `vue-template-compiler` is not required
  61. }
  62. const vueLoaderCacheConfig = api.genCacheConfig('vue-loader', partialIdentifier)
  63. webpackConfig.resolve
  64. .alias
  65. .set(
  66. 'vue$',
  67. options.runtimeCompiler
  68. ? 'vue/dist/vue.esm.js'
  69. : 'vue/dist/vue.runtime.esm.js'
  70. )
  71. if (cacheLoaderPath) {
  72. webpackConfig.module
  73. .rule('vue')
  74. .test(/\.vue$/)
  75. .use('cache-loader')
  76. .loader(cacheLoaderPath)
  77. .options(vueLoaderCacheConfig)
  78. }
  79. webpackConfig.module
  80. .rule('vue')
  81. .test(/\.vue$/)
  82. .use('vue-loader')
  83. .loader(require.resolve('@vue/vue-loader-v15'))
  84. .options(Object.assign({
  85. compilerOptions: {
  86. whitespace: 'condense'
  87. }
  88. }, cacheLoaderPath ? vueLoaderCacheConfig : {}))
  89. webpackConfig
  90. .plugin('vue-loader')
  91. .use(require('@vue/vue-loader-v15').VueLoaderPlugin)
  92. // some plugins may implicitly relies on the `vue-loader` dependency path name
  93. // such as vue-cli-plugin-apollo
  94. // <https://github.com/Akryum/vue-cli-plugin-apollo/blob/d9fe48c61cc19db88fef4e4aa5e49b31aa0c44b7/index.js#L88>
  95. // so we need a hotfix for that
  96. webpackConfig
  97. .resolveLoader
  98. .modules
  99. .prepend(path.resolve(__dirname, './vue-loader-v15-resolve-compat'))
  100. } else if (vueMajor === 3) {
  101. // for Vue 3 projects
  102. const vueLoaderCacheConfig = api.genCacheConfig('vue-loader', {
  103. 'vue-loader': require('vue-loader/package.json').version
  104. })
  105. webpackConfig.resolve
  106. .alias
  107. .set(
  108. 'vue$',
  109. options.runtimeCompiler
  110. ? 'vue/dist/vue.esm-bundler.js'
  111. : 'vue/dist/vue.runtime.esm-bundler.js'
  112. )
  113. if (cacheLoaderPath) {
  114. webpackConfig.module
  115. .rule('vue')
  116. .test(/\.vue$/)
  117. .use('cache-loader')
  118. .loader(cacheLoaderPath)
  119. .options(vueLoaderCacheConfig)
  120. }
  121. webpackConfig.module
  122. .rule('vue')
  123. .test(/\.vue$/)
  124. .use('vue-loader')
  125. .loader(require.resolve('vue-loader'))
  126. .options({
  127. ...vueLoaderCacheConfig,
  128. babelParserPlugins: ['jsx', 'classProperties', 'decorators-legacy']
  129. })
  130. webpackConfig
  131. .plugin('vue-loader')
  132. .use(require('vue-loader').VueLoaderPlugin)
  133. // feature flags <http://link.vuejs.org/feature-flags>
  134. webpackConfig
  135. .plugin('feature-flags')
  136. .use(webpack.DefinePlugin, [{
  137. __VUE_OPTIONS_API__: 'true',
  138. __VUE_PROD_DEVTOOLS__: 'false'
  139. }])
  140. }
  141. // https://github.com/vuejs/vue-loader/issues/1435#issuecomment-869074949
  142. webpackConfig.module
  143. .rule('vue-style')
  144. .test(/\.vue$/)
  145. .resourceQuery(/type=style/)
  146. .sideEffects(true)
  147. // Other common pre-processors ---------------------------------------------
  148. const maybeResolve = name => {
  149. try {
  150. return require.resolve(name)
  151. } catch (error) {
  152. return name
  153. }
  154. }
  155. webpackConfig.module
  156. .rule('pug')
  157. .test(/\.pug$/)
  158. .oneOf('pug-vue')
  159. .resourceQuery(/vue/)
  160. .use('pug-plain-loader')
  161. .loader(maybeResolve('pug-plain-loader'))
  162. .end()
  163. .end()
  164. .oneOf('pug-template')
  165. .use('raw')
  166. .loader(maybeResolve('raw-loader'))
  167. .end()
  168. .use('pug-plain-loader')
  169. .loader(maybeResolve('pug-plain-loader'))
  170. .end()
  171. .end()
  172. const resolveClientEnv = require('../util/resolveClientEnv')
  173. webpackConfig
  174. .plugin('define')
  175. .use(webpack.DefinePlugin, [
  176. resolveClientEnv(options)
  177. ])
  178. webpackConfig
  179. .plugin('case-sensitive-paths')
  180. .use(require('case-sensitive-paths-webpack-plugin'))
  181. // friendly error plugin displays very confusing errors when webpack
  182. // fails to resolve a loader, so we provide custom handlers to improve it
  183. const { transformer, formatter } = require('../util/resolveLoaderError')
  184. webpackConfig
  185. .plugin('friendly-errors')
  186. .use(require('@soda/friendly-errors-webpack-plugin'), [{
  187. additionalTransformers: [transformer],
  188. additionalFormatters: [formatter]
  189. }])
  190. const TerserPlugin = require('terser-webpack-plugin')
  191. const terserOptions = require('./terserOptions')
  192. webpackConfig.optimization
  193. .minimizer('terser')
  194. .use(TerserPlugin, [terserOptions(options)])
  195. })
  196. }