Skip to content

Commit e6b313d

Browse files
author
test
committed
feat(build): honor custom component name for single file wc builds (fixes #1146)
Fixes issue that prevented single entry wc builds from honoring the `--name` argument. The build process would just use the entry file name for the component name when registering the component previously
1 parent a0a7dc6 commit e6b313d

File tree

2 files changed

+63
-51
lines changed

2 files changed

+63
-51
lines changed

packages/@vue/cli-service/lib/commands/build/resolveWcConfig.js

+39-40
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ module.exports = (api, { target, entry, name }) => {
1717
const isAsync = /async/.test(target)
1818

1919
// generate dynamic entry based on glob files
20-
const resolvedFiles = require('globby').sync([entry], { cwd: api.resolve('.') })
20+
const resolvedFiles = require('globby').sync([entry], {
21+
cwd: api.resolve('.')
22+
})
23+
2124
if (!resolvedFiles.length) {
2225
abort(`entry pattern "${entry}" did not match any files.`)
2326
}
@@ -28,17 +31,19 @@ module.exports = (api, { target, entry, name }) => {
2831
libName = name || fileToComponentName('', resolvedFiles[0]).kebabName
2932
prefix = ''
3033
if (libName.indexOf('-') < 0) {
31-
abort(`--name must contain a hyphen when building a single web component.`)
34+
abort(
35+
`--name must contain a hyphen when building a single web component.`
36+
)
3237
}
3338
} else {
3439
// multi mode
35-
libName = prefix = (name || api.service.pkg.name)
40+
libName = prefix = name || api.service.pkg.name
3641
if (!libName) {
3742
abort(`--name is required when building multiple web components.`)
3843
}
3944
}
4045

41-
const dynamicEntry = resolveEntry(prefix, resolvedFiles, isAsync)
46+
const dynamicEntry = resolveEntry(prefix, libName, resolvedFiles, isAsync)
4247

4348
function genConfig (minify, genHTML) {
4449
const config = api.resolveChainableWebpackConfig()
@@ -47,18 +52,11 @@ module.exports = (api, { target, entry, name }) => {
4752
const entryName = `${libName}${minify ? `.min` : ``}`
4853

4954
// set proxy entry for *.vue files
50-
config
51-
.entry(entryName)
52-
.add(dynamicEntry)
53-
config.resolve
54-
.alias
55-
.set('~root', api.resolve('.'))
55+
config.entry(entryName).add(dynamicEntry)
56+
config.resolve.alias.set('~root', api.resolve('.'))
5657

5758
// make sure not to transpile wc-wrapper
58-
config.module
59-
.rule('js')
60-
.exclude
61-
.add(/vue-wc-wrapper/)
59+
config.module.rule('js').exclude.add(/vue-wc-wrapper/)
6260

6361
// only minify min entry
6462
if (!minify) {
@@ -74,47 +72,48 @@ module.exports = (api, { target, entry, name }) => {
7472
.publicPath('')
7573

7674
// externalize Vue in case user imports it
77-
config
78-
.externals({
79-
vue: 'Vue'
80-
})
75+
config.externals({
76+
vue: 'Vue'
77+
})
8178

8279
config
8380
.plugin('web-component-options')
84-
.use(require('webpack/lib/DefinePlugin'), [{
81+
.use(require('webpack/lib/DefinePlugin'), [
82+
{
8583
'process.env': {
8684
CUSTOM_ELEMENT_NAME: JSON.stringify(libName)
8785
}
88-
}])
86+
}
87+
])
8988

9089
// enable shadow mode in vue-loader
9190
config.module
9291
.rule('vue')
93-
.use('vue-loader')
94-
.tap(options => {
95-
options.shadowMode = true
96-
return options
97-
})
92+
.use('vue-loader')
93+
.tap(options => {
94+
options.shadowMode = true
95+
return options
96+
})
9897

9998
if (genHTML) {
100-
config
101-
.plugin('demo-html')
102-
.use(require('html-webpack-plugin'), [{
103-
template: path.resolve(__dirname, `./demo-wc.html`),
104-
inject: false,
105-
filename: 'demo.html',
106-
libName,
107-
components: resolvedFiles.map(file => {
108-
return fileToComponentName(prefix, file).kebabName
109-
})
110-
}])
99+
config.plugin('demo-html').use(require('html-webpack-plugin'), [
100+
{
101+
template: path.resolve(__dirname, `./demo-wc.html`),
102+
inject: false,
103+
filename: 'demo.html',
104+
libName,
105+
components:
106+
prefix === ''
107+
? [libName]
108+
: resolvedFiles.map(file => {
109+
return fileToComponentName(prefix, file).kebabName
110+
})
111+
}
112+
])
111113
}
112114

113115
return api.resolveWebpackConfig(config)
114116
}
115117

116-
return [
117-
genConfig(false, true),
118-
genConfig(true, false)
119-
]
118+
return [genConfig(false, true), genConfig(true, false)]
120119
}

packages/@vue/cli-service/lib/commands/build/resolveWcEntry.js

+24-11
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,30 @@ const path = require('path')
33

44
const camelizeRE = /-(\w)/g
55
const camelize = str => {
6-
return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
6+
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''))
77
}
88

99
const hyphenateRE = /\B([A-Z])/g
1010
const hyphenate = str => {
1111
return str.replace(hyphenateRE, '-$1').toLowerCase()
1212
}
1313

14+
/**
15+
* Creates the script to add the component to the custom elements
16+
* @param {string} prefix The prefix for the component library
17+
* @param {string} component The component name for single entry builds, component file for multi-entry builds
18+
* @param {string} file The file for the component
19+
* @param {boolean} async Whether to load component async or not
20+
*/
21+
const createElement = (prefix, component, file, async) => {
22+
const { camelName, kebabName } = exports.fileToComponentName(prefix, component)
23+
24+
return async
25+
? `window.customElements.define('${kebabName}', wrap(Vue, () => import('~root/${file}')))\n`
26+
: `import ${camelName} from '~root/${file}'\n` +
27+
`window.customElements.define('${kebabName}', wrap(Vue, ${camelName}))\n`
28+
}
29+
1430
exports.fileToComponentName = (prefix, file) => {
1531
const basename = path.basename(file).replace(/\.(jsx?|vue)$/, '')
1632
const camelName = camelize(basename)
@@ -22,8 +38,13 @@ exports.fileToComponentName = (prefix, file) => {
2238
}
2339
}
2440

25-
exports.resolveEntry = (prefix, files, async) => {
41+
exports.resolveEntry = (prefix, libName, files, async) => {
2642
const filePath = path.resolve(__dirname, 'entry-wc.js')
43+
const elements =
44+
prefix === ''
45+
? [createElement('', libName, files[0])]
46+
: files.map(file => createElement(prefix, file, file, async)).join('\n')
47+
2748
const content = `
2849
import Vue from 'vue'
2950
import wrap from '@vue/web-component-wrapper'
@@ -40,15 +61,7 @@ import 'vue-loader/lib/runtime/component-normalizer'
4061
}
4162
})()
4263
43-
${files.map(file => {
44-
const { camelName, kebabName } = exports.fileToComponentName(prefix, file)
45-
return async
46-
? `window.customElements.define('${kebabName}', wrap(Vue, () => import('~root/${file}')))\n`
47-
: (
48-
`import ${camelName} from '~root/${file}'\n` +
49-
`window.customElements.define('${kebabName}', wrap(Vue, ${camelName}))\n`
50-
)
51-
}).join('\n')}`.trim()
64+
${elements}`.trim()
5265
fs.writeFileSync(filePath, content)
5366
return filePath
5467
}

0 commit comments

Comments
 (0)