resolveWcConfig.js 3.6 KB

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