Skip to content

Commit

Permalink
feat: build --target wc-async
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Feb 3, 2018
1 parent 2c61d23 commit 50fdd9b
Show file tree
Hide file tree
Showing 14 changed files with 148 additions and 130 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ node_modules
template
packages/test
temp
entry-wc.js
27 changes: 17 additions & 10 deletions packages/@vue/cli-service/__tests__/buildWc.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
jest.setTimeout(30000)
jest.setTimeout(15000)

const path = require('path')
const portfinder = require('portfinder')
Expand All @@ -8,10 +8,10 @@ const create = require('@vue/cli-test-utils/createTestProject')
const launchPuppeteer = require('@vue/cli-test-utils/launchPuppeteer')

let server, browser, page
test('build as single wc', async () => {
test('build as wc', async () => {
const project = await create('build-wc', defaultPreset)

const { stdout } = await project.run('vue-cli-service build --target wc src/components/HelloWorld.vue')
const { stdout } = await project.run(`vue-cli-service build --target wc **/*.vue`)
expect(stdout).toMatch('Build complete.')

expect(project.has('dist/demo.html')).toBe(true)
Expand All @@ -33,17 +33,24 @@ test('build as single wc', async () => {
page = launched.page

const styleCount = await page.evaluate(() => {
return document.querySelector('build-wc').shadowRoot.querySelectorAll('style').length
return document.querySelector('build-wc-app').shadowRoot.querySelectorAll('style').length
})
expect(styleCount).toBe(1)
expect(styleCount).toBe(2) // should contain styles from both app and child

await page.evaluate(() => {
document.querySelector('build-wc').msg = 'hello'
})
const h1Text = await page.evaluate(() => {
return document.querySelector('build-wc').shadowRoot.querySelector('h1').textContent
return document.querySelector('build-wc-app').shadowRoot.querySelector('h1').textContent
})
expect(h1Text).toMatch('Welcome to Your Vue.js App')

const childStyleCount = await page.evaluate(() => {
return document.querySelector('build-wc-hello-world').shadowRoot.querySelectorAll('style').length
})
expect(childStyleCount).toBe(1)

const h2Text = await page.evaluate(() => {
return document.querySelector('build-wc-hello-world').shadowRoot.querySelector('h2').textContent
})
expect(h1Text).toBe('hello')
expect(h2Text).toMatch('Essential Links')
})

afterAll(async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
jest.setTimeout(30000)
jest.setTimeout(15000)

const path = require('path')
const portfinder = require('portfinder')
Expand All @@ -8,15 +8,21 @@ const create = require('@vue/cli-test-utils/createTestProject')
const launchPuppeteer = require('@vue/cli-test-utils/launchPuppeteer')

let server, browser, page
test('build as single wc', async () => {
const project = await create('build-multi-wc', defaultPreset)
test('build as wc in async mode', async () => {
const project = await create('build-wc-async', defaultPreset)

const { stdout } = await project.run(`vue-cli-service build --target multi-wc **/*.vue`)
const { stdout } = await project.run(`vue-cli-service build --target wc-async **/*.vue`)
expect(stdout).toMatch('Build complete.')

expect(project.has('dist/demo.html')).toBe(true)
expect(project.has('dist/build-multi-wc.js')).toBe(true)
expect(project.has('dist/build-multi-wc.min.js')).toBe(true)
expect(project.has('dist/build-wc-async.js')).toBe(true)
expect(project.has('dist/build-wc-async.min.js')).toBe(true)

// code-split chunks
expect(project.has('dist/build-wc-async.0.js')).toBe(true)
expect(project.has('dist/build-wc-async.0.min.js')).toBe(true)
expect(project.has('dist/build-wc-async.1.js')).toBe(true)
expect(project.has('dist/build-wc-async.1.min.js')).toBe(true)

const port = await portfinder.getPortPromise()
server = createServer({ root: path.join(project.dir, 'dist') })
Expand All @@ -33,22 +39,22 @@ test('build as single wc', async () => {
page = launched.page

const styleCount = await page.evaluate(() => {
return document.querySelector('build-multi-wc-app').shadowRoot.querySelectorAll('style').length
return document.querySelector('build-wc-async-app').shadowRoot.querySelectorAll('style').length
})
expect(styleCount).toBe(2) // should contain styles from both app and child

const h1Text = await page.evaluate(() => {
return document.querySelector('build-multi-wc-app').shadowRoot.querySelector('h1').textContent
return document.querySelector('build-wc-async-app').shadowRoot.querySelector('h1').textContent
})
expect(h1Text).toMatch('Welcome to Your Vue.js App')

const childStyleCount = await page.evaluate(() => {
return document.querySelector('build-multi-wc-hello-world').shadowRoot.querySelectorAll('style').length
return document.querySelector('build-wc-async-hello-world').shadowRoot.querySelectorAll('style').length
})
expect(childStyleCount).toBe(1)

const h2Text = await page.evaluate(() => {
return document.querySelector('build-multi-wc-hello-world').shadowRoot.querySelector('h2').textContent
return document.querySelector('build-wc-async-hello-world').shadowRoot.querySelector('h2').textContent
})
expect(h2Text).toMatch('Essential Links')
})
Expand Down
2 changes: 1 addition & 1 deletion packages/@vue/cli-service/lib/commands/build/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
entry-multi-wc.js
entry-wc.js

This file was deleted.

4 changes: 3 additions & 1 deletion packages/@vue/cli-service/lib/commands/build/demo-wc.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
<script src="https://unpkg.com/vue"></script>
<script src="./<%- htmlWebpackPlugin.options.libName %>.js"></script>

<<%= htmlWebpackPlugin.options.libName %>></<%= htmlWebpackPlugin.options.libName %>>
<% for (const comp of htmlWebpackPlugin.options.components) { %>
<<%= comp %>></<%= comp %>>
<% } %>
12 changes: 7 additions & 5 deletions packages/@vue/cli-service/lib/commands/build/entry-wc.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import Vue from 'vue'
import wrap from '@vue/web-component-wrapper'
import Component from '~entry'

window.customElements.define(
process.env.CUSTOM_ELEMENT_NAME,
wrap(Vue, Component)
)
// runtime shared by every component chunk
import 'css-loader/lib/css-base'
import 'vue-style-loader/lib/addStylesShadow'
import 'vue-loader/lib/runtime/component-normalizer'

import myWc from '~root/my-wc.vue'
window.customElements.define('my-wc', wrap(Vue, myWc))

This file was deleted.

5 changes: 3 additions & 2 deletions packages/@vue/cli-service/lib/commands/build/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module.exports = (api, options) => {
options: {
'--mode': `specify env mode (default: ${defaults.mode})`,
'--dest': `specify output directory (default: ${options.outputDir})`,
'--target': `app | lib | wc | multi-wc (default: ${defaults.target})`,
'--target': `app | lib | wc | wc-async (default: ${defaults.target})`,
'--name': `name for lib or (multi-)web-component mode (default: "name" in package.json or entry filename)`
}
}, args => {
Expand Down Expand Up @@ -56,8 +56,9 @@ module.exports = (api, options) => {
webpackConfig = require('./resolveLibConfig')(api, args, options)
} else if (
args.target === 'web-component' ||
args.target === 'web-component-async' ||
args.target === 'wc' ||
args.target === 'multi-wc'
args.target === 'wc-async'
) {
webpackConfig = require('./resolveWebComponentConfig')(api, args, options)
} else {
Expand Down
10 changes: 7 additions & 3 deletions packages/@vue/cli-service/lib/commands/build/resolveLibConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,30 @@ module.exports = (api, { entry, name, dest }, options) => {
const config = api.resolveChainableWebpackConfig()

config.entryPoints.clear()
const entryName = `${libName}.${postfix}`
// set proxy entry for *.vue files
if (/\.vue$/.test(entry)) {
config
.entry(`${libName}.${postfix}`)
.entry(entryName)
.add(require.resolve('./entry-lib.js'))
config.resolve
.alias
.set('~entry', api.resolve(entry))
} else {
config
.entry(`${libName}.${postfix}`)
.entry(entryName)
.add(api.resolve(entry))
}

config.output
.path(api.resolve(dest))
.filename(`[name].js`)
.filename(`${entryName}.js`)
.chunkFilename(`${entryName}.[id].js`)
.library(libName)
.libraryExport('default')
.libraryTarget(format)
// use relative publicPath so this can be deployed anywhere
.publicPath('./')

// adjust css output name so they write to the same file
if (options.css.extract !== false) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,68 +1,61 @@
const path = require('path')
const {
filesToComponentNames,
generateMultiWebComponentEntry
} = require('./generateMultiWcEntry')
resolveEntry,
fileToComponentName
} = require('./resolveWebComponentEntry')

module.exports = (api, { target, entry, name, dest }) => {
// setting this disables app-only configs
process.env.VUE_CLI_TARGET = 'web-component'
// Disable CSS extraction and turn on CSS shadow mode for vue-style-loader
process.env.VUE_CLI_CSS_SHADOW_MODE = true

module.exports = (api, { target, entry, name, dest, prefix }) => {
const { log, error } = require('@vue/cli-shared-utils')
const abort = msg => {
log()
error(msg)
process.exit(1)
}

const libName = (
name ||
api.service.pkg.name ||
path.basename(entry).replace(/\.(jsx?|vue)$/, '')
)
if (libName.indexOf('-') < 0 && target !== 'multi-wc') {
abort(`--name must contain a hyphen with --target web-component. (got "${libName}")`)
}
const isAsync = /async/.test(target)

let dynamicEntry
let resolvedFiles
if (target === 'multi-wc') {
if (!entry) {
abort(`a glob pattern is required with --target multi-web-component.`)
// generate dynamic entry based on glob files
const resolvedFiles = require('globby').sync([entry], { 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.`)
}
// generate dynamic entry based on glob files
resolvedFiles = require('globby').sync([entry], { cwd: api.resolve('.') })
if (!resolvedFiles.length) {
abort(`glob pattern "${entry}" did not match any files.`)
} else {
// multi mode
libName = prefix = (name || api.service.pkg.name)
if (!libName) {
abort(`--name is required when building multiple web components.`)
}
dynamicEntry = generateMultiWebComponentEntry(libName, resolvedFiles)
}

// setting this disables app-only configs
process.env.VUE_CLI_TARGET = 'web-component'
// inline all static asset files since there is no publicPath handling
process.env.VUE_CLI_INLINE_LIMIT = Infinity
// Disable CSS extraction and turn on CSS shadow mode for vue-style-loader
process.env.VUE_CLI_CSS_SHADOW_MODE = true
const dynamicEntry = resolveEntry(prefix, resolvedFiles, isAsync)

function genConfig (minify, genHTML) {
const config = api.resolveChainableWebpackConfig()

config.entryPoints.clear()
const entryName = `${libName}${minify ? `.min` : ``}`

// set proxy entry for *.vue files
if (target === 'multi-wc') {
config
.entry(`${libName}${minify ? `.min` : ``}`)
.add(dynamicEntry)
config.resolve
.alias
.set('~root', api.resolve('.'))
} else {
config
.entry(`${libName}${minify ? `.min` : ``}`)
.add(require.resolve('./entry-wc.js'))
config.resolve
.alias
.set('~entry', api.resolve(entry))
}
config
.entry(entryName)
.add(dynamicEntry)
config.resolve
.alias
.set('~root', api.resolve('.'))

// make sure not to transpile wc-wrapper
config.module
Expand All @@ -77,7 +70,10 @@ module.exports = (api, { target, entry, name, dest, prefix }) => {

config.output
.path(api.resolve(dest))
.filename(`[name].js`)
.filename(`${entryName}.js`)
.chunkFilename(`${libName}.[id]${minify ? `.min` : ``}.js`)
// use relative publicPath so this can be deployed anywhere
.publicPath('./')

// externalize Vue in case user imports it
config
Expand Down Expand Up @@ -106,13 +102,13 @@ module.exports = (api, { target, entry, name, dest, prefix }) => {
config
.plugin('demo-html')
.use(require('html-webpack-plugin'), [{
template: path.resolve(__dirname, `./demo-${target}.html`),
template: path.resolve(__dirname, `./demo-wc.html`),
inject: false,
filename: 'demo.html',
libName,
components: target === 'multi-wc'
? filesToComponentNames(libName, resolvedFiles).map(c => c.kebabName)
: null
components: resolvedFiles.map(file => {
return fileToComponentName(prefix, file).kebabName
})
}])
}

Expand Down
Loading

0 comments on commit 50fdd9b

Please sign in to comment.