144 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			144 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| const path = require('path')
 | |
| const { resolveEntry, fileToComponentName } = require('./resolveWcEntry')
 | |
| 
 | |
| module.exports = (api, { target, entry, name, 'inline-vue': inlineVue }) => {
 | |
|   // Disable CSS extraction and turn on CSS shadow mode for vue-style-loader
 | |
|   process.env.VUE_CLI_CSS_SHADOW_MODE = true
 | |
| 
 | |
|   const { log, error } = require('@vue/cli-shared-utils')
 | |
|   const abort = msg => {
 | |
|     log()
 | |
|     error(msg)
 | |
|     process.exit(1)
 | |
|   }
 | |
| 
 | |
|   const cwd = api.getCwd()
 | |
|   const webpack = require('webpack')
 | |
|   const vueMajor = require('../../util/getVueMajor')(cwd)
 | |
|   if (vueMajor === 3) {
 | |
|     abort(`Vue 3 support of the web component target is still under development.`)
 | |
|   }
 | |
| 
 | |
|   const isAsync = /async/.test(target)
 | |
| 
 | |
|   // generate dynamic entry based on glob files
 | |
|   const resolvedFiles = require('globby').sync(entry.split(','), { cwd: api.resolve('.') })
 | |
| 
 | |
|   if (!resolvedFiles.length) {
 | |
|     abort(`entry pattern "${entry}" did not match any files.`)
 | |
|   }
 | |
|   let libName
 | |
|   let prefix
 | |
|   if (resolvedFiles.length === 1) {
 | |
|     // in single mode, determine the lib name from filename
 | |
|     libName = name || fileToComponentName('', resolvedFiles[0]).kebabName
 | |
|     prefix = ''
 | |
|     if (libName.indexOf('-') < 0) {
 | |
|       abort(`--name must contain a hyphen when building a single web component.`)
 | |
|     }
 | |
|   } else {
 | |
|     // multi mode
 | |
|     libName = prefix = (name || api.service.pkg.name)
 | |
|     if (!libName) {
 | |
|       abort(`--name is required when building multiple web components.`)
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const dynamicEntry = resolveEntry(prefix, libName, resolvedFiles, isAsync)
 | |
| 
 | |
|   function genConfig (minify, genHTML) {
 | |
|     const config = api.resolveChainableWebpackConfig()
 | |
| 
 | |
|     // make sure not to transpile wc-wrapper
 | |
|     config.module
 | |
|       .rule('js')
 | |
|         .exclude
 | |
|           .add(/vue-wc-wrapper/)
 | |
| 
 | |
|     // only minify min entry
 | |
|     if (!minify) {
 | |
|       config.optimization.minimize(false)
 | |
|     }
 | |
| 
 | |
|     config
 | |
|       .plugin('webpack-virtual-modules')
 | |
|         .use(require('webpack-virtual-modules'), [{
 | |
|           [dynamicEntry.filePath]: dynamicEntry.content
 | |
|         }])
 | |
| 
 | |
|     config
 | |
|       .plugin('web-component-options')
 | |
|         .use(webpack.DefinePlugin, [{
 | |
|           'process.env.CUSTOM_ELEMENT_NAME': JSON.stringify(libName)
 | |
|         }])
 | |
| 
 | |
|     // enable shadow mode in vue-loader
 | |
|     config.module
 | |
|       .rule('vue')
 | |
|         .use('vue-loader')
 | |
|           .tap(options => {
 | |
|             options.shadowMode = true
 | |
|             return options
 | |
|           })
 | |
| 
 | |
|     if (genHTML) {
 | |
|       config
 | |
|         .plugin('demo-html')
 | |
|           .use(require('html-webpack-plugin'), [{
 | |
|             template: path.resolve(__dirname, `./demo-wc.html`),
 | |
|             inject: false,
 | |
|             filename: 'demo.html',
 | |
|             libName,
 | |
|             vueMajor,
 | |
|             components:
 | |
|               prefix === ''
 | |
|                 ? [libName]
 | |
|                 : resolvedFiles.map(file => {
 | |
|                   return fileToComponentName(prefix, file).kebabName
 | |
|                 })
 | |
|           }])
 | |
|     }
 | |
| 
 | |
|     // set entry/output last so it takes higher priority than user
 | |
|     // configureWebpack hooks
 | |
| 
 | |
|     // set proxy entry for *.vue files
 | |
|     config.resolve
 | |
|       .alias
 | |
|         .set('~root', api.resolve('.'))
 | |
| 
 | |
|     const rawConfig = api.resolveWebpackConfig(config)
 | |
| 
 | |
|     // externalize Vue in case user imports it
 | |
|     rawConfig.externals = [
 | |
|       ...(Array.isArray(rawConfig.externals) ? rawConfig.externals : [rawConfig.externals]),
 | |
|       { ...(inlineVue || { vue: 'Vue' }) }
 | |
|     ].filter(Boolean)
 | |
| 
 | |
|     const entryName = `${libName}${minify ? `.min` : ``}`
 | |
|     rawConfig.entry = {
 | |
|       [entryName]: dynamicEntry.filePath
 | |
|     }
 | |
| 
 | |
|     Object.assign(rawConfig.output, {
 | |
|       filename: `${entryName}.js`,
 | |
|       chunkFilename: `${libName}.[name]${minify ? `.min` : ``}.js`,
 | |
|       // use dynamic publicPath so this can be deployed anywhere
 | |
|       // the actual path will be determined at runtime by checking
 | |
|       // document.currentScript.src.
 | |
|       publicPath: ''
 | |
|     })
 | |
| 
 | |
|     // to ensure that multiple copies of async wc bundles can co-exist
 | |
|     // on the same page.
 | |
|     rawConfig.output.uniqueName = `vue-lib-${libName}`
 | |
| 
 | |
|     return rawConfig
 | |
|   }
 | |
| 
 | |
|   return [
 | |
|     genConfig(false, true),
 | |
|     genConfig(true, false)
 | |
|   ]
 | |
| }
 |