From 86f4f5fceb8eb8065ccca171a7da4e78ab53a54e Mon Sep 17 00:00:00 2001 From: Roman Pavlov Date: Mon, 19 Aug 2019 16:05:46 +0300 Subject: [PATCH] feat(build): add `--inline-vue` flag to optionally disable externalization of Vue (#4261) Resolves #4055. --- docs/guide/build-targets.md | 12 ++++ docs/guide/cli-service.md | 1 + docs/ru/guide/build-targets.md | 12 ++++ docs/ru/guide/cli-service.md | 1 + .../cli-service/__tests__/buildLib.spec.js | 56 +++++++++++++++++++ .../cli-service/__tests__/buildWc.spec.js | 55 ++++++++++++++++++ .../@vue/cli-service/bin/vue-cli-service.js | 1 + .../cli-service/lib/commands/build/index.js | 1 + .../lib/commands/build/resolveLibConfig.js | 14 +++-- .../lib/commands/build/resolveWcConfig.js | 6 +- 10 files changed, 149 insertions(+), 10 deletions(-) diff --git a/docs/guide/build-targets.md b/docs/guide/build-targets.md index 04d61a9483..e3aa1bf965 100644 --- a/docs/guide/build-targets.md +++ b/docs/guide/build-targets.md @@ -15,6 +15,12 @@ App is the default build target. In this mode: ::: tip Note on Vue Dependency In lib mode, Vue is *externalized*. This means the bundle will not bundle Vue even if your code imports Vue. If the lib is used via a bundler, it will attempt to load Vue as a dependency through the bundler; otherwise, it falls back to a global `Vue` variable. + +To avoid this behavior provide `--inline-vue` flag to `build` command. + +``` +vue-cli-service build --target lib --inline-vue +``` ::: You can build a single entry as a library using @@ -68,6 +74,12 @@ Web Component mode does not support IE11 and below. [More details](https://githu ::: tip Note on Vue Dependency In web component mode, Vue is *externalized.* This means the bundle will not bundle Vue even if your code imports Vue. The bundle will assume `Vue` is available on the host page as a global variable. + +To avoid this behavior provide `--inline-vue` flag to `build` command. + +``` +vue-cli-service build --target wc --inline-vue +``` ::: You can build a single entry as a web component using diff --git a/docs/guide/cli-service.md b/docs/guide/cli-service.md index 713d6a9b82..a29d55d5da 100644 --- a/docs/guide/cli-service.md +++ b/docs/guide/cli-service.md @@ -75,6 +75,7 @@ Options: --modern build app targeting modern browsers with auto fallback --target app | lib | wc | wc-async (default: app) --formats list of output formats for library builds (default: commonjs,umd,umd-min) + --inline-vue include the Vue module in the final bundle of library or web component target --name name for lib or web-component mode (default: "name" in package.json or entry filename) --no-clean do not remove the dist directory before building the project --report generate report.html to help analyze bundle content diff --git a/docs/ru/guide/build-targets.md b/docs/ru/guide/build-targets.md index 6102761117..f1918710f3 100644 --- a/docs/ru/guide/build-targets.md +++ b/docs/ru/guide/build-targets.md @@ -19,6 +19,12 @@ ::: tip Примечание о зависимости Vue В режиме библиотеки Vue *экстернализируется*. Это означает, что сборка не будет содержать Vue, даже если ваш код его импортирует. Если библиотека используется через сборщик, он должен попытаться загрузить Vue в качестве зависимости через сборщик; в противном случае, он должен вернуться к глобальной переменной `Vue`. + +Чтобы избежать экстернализиции Vue установите флаг `--inline-vue` для команды `build`. + +``` +vue-cli-service build --target lib --inline-vue +``` ::: Вы можете запустить сборку одной точки входа в качестве библиотеки с помощью: @@ -72,6 +78,12 @@ module.exports = { ::: tip Примечание зависимости Vue В режиме веб-компонентов Vue *экстернализируется.* Это означает, что сборка не будет содержать Vue, даже если ваш код его импортирует. Сборка будет подразумевать, что `Vue` доступен на странице в качестве глобальной переменной. + +Чтобы избежать экстернализиции Vue установите флаг `--inline-vue` для команды `build`. + +``` +vue-cli-service build --target wc --inline-vue +``` ::: Вы можете запустить сборку одной точки входа в качестве веб-компонента с помощью: diff --git a/docs/ru/guide/cli-service.md b/docs/ru/guide/cli-service.md index 25568caeb5..c054b09e8f 100644 --- a/docs/ru/guide/cli-service.md +++ b/docs/ru/guide/cli-service.md @@ -67,6 +67,7 @@ npx vue-cli-service serve --dest определить каталог сборки (по умолчанию: dist) --modern собирать приложение для современных браузеров с авто-фоллбэком для старых --target app | lib | wc | wc-async (по умолчанию: app) + --inline-vue включить Vue в содержимое сборки библиотеки или веб-компонента --name имя библиотеки или режим веб-компонента (по умолчанию: "name" в package.json или имя файла точки входа) --no-clean не удалять каталог dist перед сборкой проекта --report сгенерировать report.html для анализа содержимого сборки diff --git a/packages/@vue/cli-service/__tests__/buildLib.spec.js b/packages/@vue/cli-service/__tests__/buildLib.spec.js index 4476f15e53..7aa6915a6c 100644 --- a/packages/@vue/cli-service/__tests__/buildLib.spec.js +++ b/packages/@vue/cli-service/__tests__/buildLib.spec.js @@ -214,3 +214,59 @@ test('build as lib without --name and --filename options (default to package nam expect(project.has('dist/test-lib.umd.js')).toBe(true) expect(project.has('dist/test-lib.umd.min.js')).toBe(true) }) + +test('build as lib with --inline-vue', async () => { + const project = await create('build-lib-inline-vue', defaultPreset) + + await project.write('src/main-lib.js', ` + import Vue from 'vue' + import App from "./components/App.vue" + + document.addEventListener("DOMContentLoaded", function() { + new Vue({ + render: h => h(App), + }).$mount('body'); + }); + `) + + await project.write('src/components/App.vue', ` + + + `) + + const { stdout } = await project.run('vue-cli-service build --target lib --inline-vue --name testLib src/main-lib.js') + expect(stdout).toMatch('Build complete.') + + expect(project.has('dist/demo.html')).toBe(true) + expect(project.has('dist/testLib.common.js')).toBe(true) + expect(project.has('dist/testLib.umd.js')).toBe(true) + expect(project.has('dist/testLib.umd.min.js')).toBe(true) + + const port = await portfinder.getPortPromise() + server = createServer({ root: path.join(project.dir, 'dist') }) + + await new Promise((resolve, reject) => { + server.listen(port, err => { + if (err) return reject(err) + resolve() + }) + }) + + const launched = await launchPuppeteer(`http://localhost:${port}/demo.html`) + browser = launched.browser + page = launched.page + const divText = await page.evaluate(() => { + return document.querySelector('div').textContent + }) + expect(divText).toMatch('Hello from Lib') +}) diff --git a/packages/@vue/cli-service/__tests__/buildWc.spec.js b/packages/@vue/cli-service/__tests__/buildWc.spec.js index b59676d4e7..4dcd67255d 100644 --- a/packages/@vue/cli-service/__tests__/buildWc.spec.js +++ b/packages/@vue/cli-service/__tests__/buildWc.spec.js @@ -89,6 +89,61 @@ test('build as single wc', async () => { expect(h1Text).toMatch('Welcome to Your Vue.js App') }) +test('build as wc with --inline-vue', async () => { + const project = await create('build-wc-inline-vue', defaultPreset) + + await project.write('src/main-wc.js', ` + import Vue from 'vue' + import App from "./components/App.vue" + + document.addEventListener("DOMContentLoaded", function() { + new Vue({ + render: h => h(App), + }).$mount('body'); + }); + `) + + await project.write('src/components/App.vue', ` + + + `) + + const { stdout } = await project.run('vue-cli-service build --target wc --inline-vue --name single-wc src/main-wc.js') + expect(stdout).toMatch('Build complete.') + + expect(project.has('dist/demo.html')).toBe(true) + expect(project.has('dist/single-wc.js')).toBe(true) + expect(project.has('dist/single-wc.min.js')).toBe(true) + + const port = await portfinder.getPortPromise() + server = createServer({ root: path.join(project.dir, 'dist') }) + + await new Promise((resolve, reject) => { + server.listen(port, err => { + if (err) return reject(err) + resolve() + }) + }) + + const launched = await launchPuppeteer(`http://localhost:${port}/demo.html`) + browser = launched.browser + page = launched.page + const divText = await page.evaluate(() => { + return document.querySelector('div').textContent + }) + expect(divText).toMatch('Hello from Wc') +}) + afterEach(async () => { if (browser) { await browser.close() diff --git a/packages/@vue/cli-service/bin/vue-cli-service.js b/packages/@vue/cli-service/bin/vue-cli-service.js index d8a578bb9f..bb780fa01a 100755 --- a/packages/@vue/cli-service/bin/vue-cli-service.js +++ b/packages/@vue/cli-service/bin/vue-cli-service.js @@ -22,6 +22,7 @@ const args = require('minimist')(rawArgv, { 'modern', 'report', 'report-json', + 'inline-vue', 'watch', // serve 'open', diff --git a/packages/@vue/cli-service/lib/commands/build/index.js b/packages/@vue/cli-service/lib/commands/build/index.js index 9c1227dd76..54e07662c9 100644 --- a/packages/@vue/cli-service/lib/commands/build/index.js +++ b/packages/@vue/cli-service/lib/commands/build/index.js @@ -29,6 +29,7 @@ module.exports = (api, options) => { '--modern': `build app targeting modern browsers with auto fallback`, '--no-unsafe-inline': `build app without introducing inline scripts`, '--target': `app | lib | wc | wc-async (default: ${defaults.target})`, + '--inline-vue': 'include the Vue module in the final bundle of library or web component target', '--formats': `list of output formats for library builds (default: ${defaults.formats})`, '--name': `name for lib or web-component mode (default: "name" in package.json or entry filename)`, '--filename': `file name for output, only usable for 'lib' target (default: value of --name)`, diff --git a/packages/@vue/cli-service/lib/commands/build/resolveLibConfig.js b/packages/@vue/cli-service/lib/commands/build/resolveLibConfig.js index 2fc0554f50..cea6a23f0b 100644 --- a/packages/@vue/cli-service/lib/commands/build/resolveLibConfig.js +++ b/packages/@vue/cli-service/lib/commands/build/resolveLibConfig.js @@ -1,7 +1,7 @@ const fs = require('fs') const path = require('path') -module.exports = (api, { entry, name, formats, filename }, options) => { +module.exports = (api, { entry, name, formats, filename, 'inline-vue': inlineVue }, options) => { const { log, error } = require('@vue/cli-shared-utils') const abort = msg => { log() @@ -97,11 +97,13 @@ module.exports = (api, { entry, name, formats, filename }, options) => { rawConfig.externals = [ ...(Array.isArray(rawConfig.externals) ? rawConfig.externals : [rawConfig.externals]), { - vue: { - commonjs: 'vue', - commonjs2: 'vue', - root: 'Vue' - } + ...(inlineVue || { + vue: { + commonjs: 'vue', + commonjs2: 'vue', + root: 'Vue' + } + }) } ].filter(Boolean) diff --git a/packages/@vue/cli-service/lib/commands/build/resolveWcConfig.js b/packages/@vue/cli-service/lib/commands/build/resolveWcConfig.js index 875e2fc4f2..7f3a6ceba3 100644 --- a/packages/@vue/cli-service/lib/commands/build/resolveWcConfig.js +++ b/packages/@vue/cli-service/lib/commands/build/resolveWcConfig.js @@ -2,7 +2,7 @@ const path = require('path') const webpack = require('webpack') const { resolveEntry, fileToComponentName } = require('./resolveWcEntry') -module.exports = (api, { target, entry, name }) => { +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 @@ -99,9 +99,7 @@ module.exports = (api, { target, entry, name }) => { // externalize Vue in case user imports it rawConfig.externals = [ ...(Array.isArray(rawConfig.externals) ? rawConfig.externals : [rawConfig.externals]), - { - vue: 'Vue' - } + { ...(inlineVue || { vue: 'Vue' }) } ].filter(Boolean) const entryName = `${libName}${minify ? `.min` : ``}`