resolveWcConfig.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. const path = require('path')
  2. const { resolveEntry, fileToComponentName } = require('./resolveWcEntry')
  3. module.exports = (api, { target, entry, name, 'inline-vue': inlineVue }) => {
  4. // Disable CSS extraction and turn on CSS shadow mode for vue-style-loader
  5. process.env.VUE_CLI_CSS_SHADOW_MODE = true
  6. const { log, error } = require('@vue/cli-shared-utils')
  7. const abort = msg => {
  8. log()
  9. error(msg)
  10. process.exit(1)
  11. }
  12. const cwd = api.getCwd()
  13. const webpack = require('webpack')
  14. const vueMajor = require('../../util/getVueMajor')(cwd)
  15. if (vueMajor === 3) {
  16. abort(`Vue 3 support of the web component target is still under development.`)
  17. }
  18. const isAsync = /async/.test(target)
  19. // generate dynamic entry based on glob files
  20. const resolvedFiles = require('globby').sync(entry.split(','), { cwd: api.resolve('.') })
  21. if (!resolvedFiles.length) {
  22. abort(`entry pattern "${entry}" did not match any files.`)
  23. }
  24. let libName
  25. let prefix
  26. if (resolvedFiles.length === 1) {
  27. // in single mode, determine the lib name from filename
  28. libName = name || fileToComponentName('', resolvedFiles[0]).kebabName
  29. prefix = ''
  30. if (libName.indexOf('-') < 0) {
  31. abort(`--name must contain a hyphen when building a single web component.`)
  32. }
  33. } else {
  34. // multi mode
  35. libName = prefix = (name || api.service.pkg.name)
  36. if (!libName) {
  37. abort(`--name is required when building multiple web components.`)
  38. }
  39. }
  40. const dynamicEntry = resolveEntry(prefix, libName, resolvedFiles, isAsync)
  41. function genConfig (minify, genHTML) {
  42. const config = api.resolveChainableWebpackConfig()
  43. // make sure not to transpile wc-wrapper
  44. config.module
  45. .rule('js')
  46. .exclude
  47. .add(/vue-wc-wrapper/)
  48. // only minify min entry
  49. if (!minify) {
  50. config.optimization.minimize(false)
  51. }
  52. config
  53. .plugin('webpack-virtual-modules')
  54. .use(require('webpack-virtual-modules'), [{
  55. [dynamicEntry.filePath]: dynamicEntry.content
  56. }])
  57. config
  58. .plugin('web-component-options')
  59. .use(webpack.DefinePlugin, [{
  60. 'process.env.CUSTOM_ELEMENT_NAME': JSON.stringify(libName)
  61. }])
  62. // enable shadow mode in vue-loader
  63. config.module
  64. .rule('vue')
  65. .use('vue-loader')
  66. .tap(options => {
  67. options.shadowMode = true
  68. return options
  69. })
  70. if (genHTML) {
  71. config
  72. .plugin('demo-html')
  73. .use(require('html-webpack-plugin'), [{
  74. template: path.resolve(__dirname, `./demo-wc.html`),
  75. inject: false,
  76. filename: 'demo.html',
  77. libName,
  78. vueMajor,
  79. components:
  80. prefix === ''
  81. ? [libName]
  82. : resolvedFiles.map(file => {
  83. return fileToComponentName(prefix, file).kebabName
  84. })
  85. }])
  86. }
  87. // set entry/output last so it takes higher priority than user
  88. // configureWebpack hooks
  89. // set proxy entry for *.vue files
  90. config.resolve
  91. .alias
  92. .set('~root', api.resolve('.'))
  93. const rawConfig = api.resolveWebpackConfig(config)
  94. // externalize Vue in case user imports it
  95. rawConfig.externals = [
  96. ...(Array.isArray(rawConfig.externals) ? rawConfig.externals : [rawConfig.externals]),
  97. { ...(inlineVue || { vue: 'Vue' }) }
  98. ].filter(Boolean)
  99. const entryName = `${libName}${minify ? `.min` : ``}`
  100. rawConfig.entry = {
  101. [entryName]: dynamicEntry.filePath
  102. }
  103. Object.assign(rawConfig.output, {
  104. filename: `${entryName}.js`,
  105. chunkFilename: `${libName}.[name]${minify ? `.min` : ``}.js`,
  106. // use dynamic publicPath so this can be deployed anywhere
  107. // the actual path will be determined at runtime by checking
  108. // document.currentScript.src.
  109. publicPath: ''
  110. })
  111. // to ensure that multiple copies of async wc bundles can co-exist
  112. // on the same page.
  113. rawConfig.output.uniqueName = `vue-lib-${libName}`
  114. return rawConfig
  115. }
  116. return [
  117. genConfig(false, true),
  118. genConfig(true, false)
  119. ]
  120. }