From fae9e617dc74254781a93ccc6e45451f9f3fb350 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Sun, 7 Feb 2021 16:13:19 +0800 Subject: [PATCH 01/10] feat: use html-webpack-plugin v5 by default --- packages/@vue/cli-plugin-pwa/package.json | 2 +- .../__tests__/pluginCompatibility.spec.js | 102 ++++++++++++++++++ .../__tests__/pluginCustomization.spec.js | 21 ++++ .../@vue/cli-plugin-webpack-4/generator.js | 2 + packages/@vue/cli-plugin-webpack-4/index.js | 24 +++-- .../@vue/cli-plugin-webpack-4/package.json | 1 + packages/@vue/cli-service/package.json | 2 +- scripts/test.js | 2 +- yarn.lock | 22 +++- 9 files changed, 164 insertions(+), 14 deletions(-) create mode 100644 packages/@vue/cli-plugin-webpack-4/__tests__/pluginCompatibility.spec.js create mode 100644 packages/@vue/cli-plugin-webpack-4/__tests__/pluginCustomization.spec.js diff --git a/packages/@vue/cli-plugin-pwa/package.json b/packages/@vue/cli-plugin-pwa/package.json index d79b5b1e09..0e766e965e 100644 --- a/packages/@vue/cli-plugin-pwa/package.json +++ b/packages/@vue/cli-plugin-pwa/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@vue/cli-shared-utils": "^5.0.0-alpha.3", - "html-webpack-plugin": "^4.5.0", + "html-webpack-plugin": "^5.0.0", "webpack": "^5.10.0", "workbox-webpack-plugin": "^6.0.2" }, diff --git a/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCompatibility.spec.js b/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCompatibility.spec.js new file mode 100644 index 0000000000..51b1fc5a5f --- /dev/null +++ b/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCompatibility.spec.js @@ -0,0 +1,102 @@ +jest.setTimeout(30000) + +const path = require('path') +const { defaultPreset } = require('@vue/cli/lib/options') +const create = require('@vue/cli-test-utils/createUpgradableProject') +const portfinder = require('portfinder') +const createServer = require('@vue/cli-test-utils/createServer') +const launchPuppeteer = require('@vue/cli-test-utils/launchPuppeteer') + +let server, browser, page + +const webpack4Preset = { + ...defaultPreset, + plugins: { + ...defaultPreset.plugins, + '@vue/cli-plugin-webpack-4': {} + } +} + +test('build', async () => { + // TODO: + // Find a way to test which webpack version the project is built with + // For now it's not necessary as the mocha tests have covered it (mochapack doesn't work with webpack 5) + + const project = await create('e2e-build-webpack-4', webpack4Preset) + // test public copy + project.write('public/foo.js', '1') + // make sure that only /public/index.html is skipped (#3119) + project.write('public/subfolder/index.html', '1') + + const { stdout } = await project.run('vue-cli-service build') + expect(stdout).toMatch('Build complete.') + + expect(project.has('dist/index.html')).toBe(true) + expect(project.has('dist/favicon.ico')).toBe(true) + expect(project.has('dist/js')).toBe(true) + expect(project.has('dist/css')).toBe(true) + expect(project.has('dist/foo.js')).toBe(true) + expect(project.has('dist/subfolder/index.html')).toBe(true) + + const index = await project.read('dist/index.html') + + // should have set the title inferred from the project name + expect(index).toMatch(/e2e-build<\/title>/) + + // should inject scripts + expect(index).toMatch(/<script src="\/js\/chunk-vendors\.\w{8}\.js">/) + expect(index).toMatch(/<script src="\/js\/app\.\w{8}\.js">/) + // should inject css + expect(index).toMatch(/<link href="\/css\/app\.\w{8}\.css" rel="stylesheet">/) + + // should reference favicon with correct base URL + expect(index).toMatch(/<link rel="icon" href="\/favicon.ico">/) + + 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}/`) + browser = launched.browser + page = launched.page + + const h1Text = await page.evaluate(() => { + return document.querySelector('h1').textContent + }) + + expect(h1Text).toMatch('Welcome to Your Vue.js App') +}) + +test('multi-page', async () => { + const project = await create('e2e-build-webpack-4', webpack4Preset) + + await project.write('vue.config.js', ` +module.exports = { + pages: { + foo: 'src/main.js', + bar: 'src/main.js', + } +} + `) + + const { stdout } = await project.run('vue-cli-service build') + expect(stdout).toMatch('Build complete.') + + expect(project.has('dist/foo.html')).toBe(true) + expect(project.has('dist/bar.html')).toBe(true) +}) + +afterAll(async () => { + if (browser) { + await browser.close() + } + if (server) { + server.close() + } +}) diff --git a/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCustomization.spec.js b/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCustomization.spec.js new file mode 100644 index 0000000000..f714c764de --- /dev/null +++ b/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCustomization.spec.js @@ -0,0 +1,21 @@ +jest.setTimeout(30000) + +const { defaultPreset } = require('@vue/cli/lib/options') +const create = require('@vue/cli-test-utils/createUpgradableProject') + +const webpack4Preset = { + ...defaultPreset, + plugins: { + ...defaultPreset.plugins, + '@vue/cli-plugin-webpack-4': {} + } +} + +test('build with customizations', async () => { + const project = await create('e2e-custom-webpack-4', webpack4Preset) + + // TODO: customizations + + const { stdout } = await project.run('vue-cli-service build') + expect(stdout).toMatch('Build complete.') +}) diff --git a/packages/@vue/cli-plugin-webpack-4/generator.js b/packages/@vue/cli-plugin-webpack-4/generator.js index 195d1476a9..eec5515889 100644 --- a/packages/@vue/cli-plugin-webpack-4/generator.js +++ b/packages/@vue/cli-plugin-webpack-4/generator.js @@ -11,4 +11,6 @@ module.exports = (api) => { '@vue/cli-*/webpack': '^4.0.0' } }) + + // TODO: if uses sass, replace sass-loader@11 with sass-loader@10 } diff --git a/packages/@vue/cli-plugin-webpack-4/index.js b/packages/@vue/cli-plugin-webpack-4/index.js index 5497b1d151..356dfd3951 100644 --- a/packages/@vue/cli-plugin-webpack-4/index.js +++ b/packages/@vue/cli-plugin-webpack-4/index.js @@ -1,8 +1,20 @@ /** @type {import('@vue/cli-service').ServicePlugin} */ -module.exports = () => { - // TODO: - // terser-webpack-plugin v4 - // copy-webpack-plugin v6 - // html-webpack-plugin v4 - // css-minimizer-webpack-plugin v1 +module.exports = (api, options) => { + api.chainWebpack(config => { + const HtmlWebpackPlugin4 = require('html-webpack-plugin') + if (!options.pages) { + config.plugin('html') + .init((Plugin, args) => new HtmlWebpackPlugin4(...args)) + } else { + Object.keys(options.pages).forEach(name => { + config.plugin(`html-${name}`) + .init((Plugin, args) => new HtmlWebpackPlugin4(...args)) + }) + } + + // terser-webpack-plugin v4 + // copy-webpack-plugin v6 + // html-webpack-plugin v4 + // css-minimizer-webpack-plugin v1 + }) } diff --git a/packages/@vue/cli-plugin-webpack-4/package.json b/packages/@vue/cli-plugin-webpack-4/package.json index 2cc6923809..cffd611f8f 100644 --- a/packages/@vue/cli-plugin-webpack-4/package.json +++ b/packages/@vue/cli-plugin-webpack-4/package.json @@ -23,6 +23,7 @@ "access": "public" }, "dependencies": { + "html-webpack-plugin": "^4.5.1", "webpack": "^4.44.2" }, "peerDependencies": { diff --git a/packages/@vue/cli-service/package.json b/packages/@vue/cli-service/package.json index 5561fec0ef..464752e07a 100644 --- a/packages/@vue/cli-service/package.json +++ b/packages/@vue/cli-service/package.json @@ -56,7 +56,7 @@ "fs-extra": "^9.0.1", "globby": "^11.0.1", "hash-sum": "^2.0.0", - "html-webpack-plugin": "^4.5.0", + "html-webpack-plugin": "^5.0.0", "launch-editor-middleware": "^2.2.1", "lodash.defaultsdeep": "^4.6.1", "lodash.mapvalues": "^4.6.0", diff --git a/scripts/test.js b/scripts/test.js index abe6082514..c5a0366f1b 100644 --- a/scripts/test.js +++ b/scripts/test.js @@ -10,7 +10,7 @@ if (args.p) { rawArgs.splice(i, 2) } -const e2ePathPattern = 'Migrator|Vue3|mochaPlugin|MochaPlugin' +const e2ePathPattern = 'Migrator|Vue3|mochaPlugin|MochaPlugin|webpack-4' if (args['e2e-only']) { regex = e2ePathPattern diff --git a/yarn.lock b/yarn.lock index 2249ef15a7..f0a845a4a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11906,21 +11906,33 @@ html-tags@^3.1.0: resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== -html-webpack-plugin@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.5.0.tgz#625097650886b97ea5dae331c320e3238f6c121c" - integrity sha512-MouoXEYSjTzCrjIxWwg8gxL5fE2X2WZJLmBYXlaJhQUH5K/b5OrqmV7T4dB7iu0xkmJ6JlUuV6fFVtnqbPopZw== +html-webpack-plugin@^4.5.1: + version "4.5.1" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.5.1.tgz#40aaf1b5cb78f2f23a83333999625c20929cda65" + integrity sha512-yzK7RQZwv9xB+pcdHNTjcqbaaDZ+5L0zJHXfi89iWIZmb/FtzxhLk0635rmJihcQbs3ZUF27Xp4oWGx6EK56zg== dependencies: "@types/html-minifier-terser" "^5.0.0" "@types/tapable" "^1.0.5" "@types/webpack" "^4.41.8" html-minifier-terser "^5.0.1" loader-utils "^1.2.3" - lodash "^4.17.15" + lodash "^4.17.20" pretty-error "^2.1.1" tapable "^1.1.3" util.promisify "1.0.0" +html-webpack-plugin@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.0.0.tgz#457a9defb33ce368135078b4e0387a27f3fe244d" + integrity sha512-kxTyb8cyZwEyUqXTgdHRUOF4C7uCrquzw2T+YTudehm/yspodgCkREjdmc4dXI8k2P4NEjqOVbnOOlPZg4TKJA== + dependencies: + "@types/html-minifier-terser" "^5.0.0" + html-minifier-terser "^5.0.1" + loader-utils "^2.0.0" + lodash "^4.17.20" + pretty-error "^2.1.1" + tapable "^2.0.0" + htmlparser2@^3.3.0: version "3.10.1" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" From 38156ccd8ef2cf80ad1821f6a9a13b4b4db92449 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang <haoqunjiang@gmail.com> Date: Sun, 7 Feb 2021 16:44:35 +0800 Subject: [PATCH 02/10] test: fix webpack-4 ci jobs --- packages/@vue/cli-service/lib/Service.js | 12 ++++++++++++ scripts/test.js | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/@vue/cli-service/lib/Service.js b/packages/@vue/cli-service/lib/Service.js index a477177a07..8f0bd289bc 100644 --- a/packages/@vue/cli-service/lib/Service.js +++ b/packages/@vue/cli-service/lib/Service.js @@ -187,6 +187,18 @@ module.exports = class Service { return idToPlugin(id, resolveModule(id, this.pkgContext)) } }) + + // Add the plugin automatically to simplify the webpack-4 tests + // so that a simple Jest alias would suffice, avoid changing every + // preset used in the tests + if ( + process.env.VUE_CLI_TEST && + process.env.VUE_CLI_USE_WEBPACK4 && + !projectPlugins.some((p) => p.id === '@vue/cli-plugin-webpack-4') + ) { + builtInPlugins.push(idToPlugin('@vue/cli-plugin-webpack-4')) + } + plugins = builtInPlugins.concat(projectPlugins) } diff --git a/scripts/test.js b/scripts/test.js index c5a0366f1b..0a6c0a8f81 100644 --- a/scripts/test.js +++ b/scripts/test.js @@ -10,7 +10,7 @@ if (args.p) { rawArgs.splice(i, 2) } -const e2ePathPattern = 'Migrator|Vue3|mochaPlugin|MochaPlugin|webpack-4' +const e2ePathPattern = 'Migrator|Vue3|mochaPlugin|MochaPlugin|cli-plugin-webpack-4' if (args['e2e-only']) { regex = e2ePathPattern From 7cfa6403a8803f7ea86424ca5a26ccc994d571b3 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang <haoqunjiang@gmail.com> Date: Sun, 7 Feb 2021 17:42:32 +0800 Subject: [PATCH 03/10] fix: fix data uri support --- packages/@vue/cli-plugin-babel/index.js | 5 +++++ packages/@vue/cli-service/package.json | 4 ++-- yarn.lock | 21 ++++++++++++++++----- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/packages/@vue/cli-plugin-babel/index.js b/packages/@vue/cli-plugin-babel/index.js index 1b1df4824e..d42039c46d 100644 --- a/packages/@vue/cli-plugin-babel/index.js +++ b/packages/@vue/cli-plugin-babel/index.js @@ -39,6 +39,11 @@ module.exports = (api, options) => { .test(/\.m?jsx?$/) .exclude .add(filepath => { + // With data URI support in webpack 5, filepath could be undefined + if (!filepath) { + return true + } + // always transpile js in vue files if (/\.vue\.jsx?$/.test(filepath)) { return false diff --git a/packages/@vue/cli-service/package.json b/packages/@vue/cli-service/package.json index 464752e07a..732eb3e2e2 100644 --- a/packages/@vue/cli-service/package.json +++ b/packages/@vue/cli-service/package.json @@ -71,8 +71,8 @@ "terser-webpack-plugin": "^4.2.3", "thread-loader": "^3.0.0", "url-loader": "^4.1.1", - "vue-loader": "^16.1.0", - "vue-loader-v15": "npm:vue-loader@^15.9.5", + "vue-loader": "^16.1.2", + "vue-loader-v15": "npm:vue-loader@^15.9.6", "vue-style-loader": "^4.1.2", "webpack": "^5.4.0", "webpack-bundle-analyzer": "^4.1.0", diff --git a/yarn.lock b/yarn.lock index f0a845a4a9..925740f77f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21457,7 +21457,18 @@ vue-jest@^3.0.5: tsconfig "^7.0.0" vue-template-es2015-compiler "^1.6.0" -"vue-loader-v15@npm:vue-loader@^15.9.5", vue-loader@^15.7.1: +"vue-loader-v15@npm:vue-loader@^15.9.6": + version "15.9.6" + resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.9.6.tgz#f4bb9ae20c3a8370af3ecf09b8126d38ffdb6b8b" + integrity sha512-j0cqiLzwbeImIC6nVIby2o/ABAWhlppyL/m5oJ67R5MloP0hj/DtFgb0Zmq3J9CG7AJ+AXIvHVnJAPBvrLyuDg== + dependencies: + "@vue/component-compiler-utils" "^3.1.0" + hash-sum "^1.0.2" + loader-utils "^1.1.0" + vue-hot-reload-api "^2.3.0" + vue-style-loader "^4.1.0" + +vue-loader@^15.7.1: version "15.9.5" resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.9.5.tgz#7a960dc420a3439deaacdda038fdcdbf7c432706" integrity sha512-oeMOs2b5o5gRqkxfds10bCx6JeXYTwivRgbb8hzOrcThD2z1+GqEKE3EX9A2SGbsYDf4rXwRg6D5n1w0jO5SwA== @@ -21468,10 +21479,10 @@ vue-jest@^3.0.5: vue-hot-reload-api "^2.3.0" vue-style-loader "^4.1.0" -vue-loader@^16.1.0: - version "16.1.1" - resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-16.1.1.tgz#f5b286d60ac6886684c63a17a184391cc9e0199a" - integrity sha512-wz/+HFg/3SBayHWAlZXARcnDTl3VOChrfW9YnxvAweiuyKX/7IGx1ad/4yJHmwhgWlOVYMAbTiI7GV8G33PfGQ== +vue-loader@^16.1.2: + version "16.1.2" + resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-16.1.2.tgz#5c03b6c50d2a5f983c7ceba15c50d78ca2b298f4" + integrity sha512-8QTxh+Fd+HB6fiL52iEVLKqE9N1JSlMXLR92Ijm6g8PZrwIxckgpqjPDWRP5TWxdiPaHR+alUWsnu1ShQOwt+Q== dependencies: chalk "^4.1.0" hash-sum "^2.0.0" From 28218c8e0652dc579242e4cb07aebd44ea9f7aca Mon Sep 17 00:00:00 2001 From: Haoqun Jiang <haoqunjiang@gmail.com> Date: Sun, 7 Feb 2021 18:05:29 +0800 Subject: [PATCH 04/10] fix: html-webpack-plugin is only available for app target --- packages/@vue/cli-plugin-webpack-4/index.js | 26 +++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/@vue/cli-plugin-webpack-4/index.js b/packages/@vue/cli-plugin-webpack-4/index.js index 356dfd3951..b66e534a29 100644 --- a/packages/@vue/cli-plugin-webpack-4/index.js +++ b/packages/@vue/cli-plugin-webpack-4/index.js @@ -1,20 +1,22 @@ /** @type {import('@vue/cli-service').ServicePlugin} */ module.exports = (api, options) => { api.chainWebpack(config => { - const HtmlWebpackPlugin4 = require('html-webpack-plugin') - if (!options.pages) { - config.plugin('html') - .init((Plugin, args) => new HtmlWebpackPlugin4(...args)) - } else { - Object.keys(options.pages).forEach(name => { - config.plugin(`html-${name}`) + if (process.env.VUE_CLI_BUILD_TARGET === 'app') { + const HtmlWebpackPlugin4 = require('html-webpack-plugin') + if (!options.pages) { + config.plugin('html') .init((Plugin, args) => new HtmlWebpackPlugin4(...args)) - }) + } else { + Object.keys(options.pages).forEach(name => { + config.plugin(`html-${name}`) + .init((Plugin, args) => new HtmlWebpackPlugin4(...args)) + }) + } + + // TODO: copy-webpack-plugin v6 } - // terser-webpack-plugin v4 - // copy-webpack-plugin v6 - // html-webpack-plugin v4 - // css-minimizer-webpack-plugin v1 + // TODO: terser-webpack-plugin v4 + // TODO: css-minimizer-webpack-plugin v1 }) } From 46f9d251c91df93744d78d724e91f067f64d8602 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang <haoqunjiang@gmail.com> Date: Mon, 8 Feb 2021 14:34:54 +0800 Subject: [PATCH 05/10] fix: use module-alias for html-webpack-plugin, fix peer dep errors --- .../@vue/cli-plugin-webpack-4/generator.js | 3 +- packages/@vue/cli-plugin-webpack-4/index.js | 29 +++++++------------ .../@vue/cli-plugin-webpack-4/package.json | 1 + 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/packages/@vue/cli-plugin-webpack-4/generator.js b/packages/@vue/cli-plugin-webpack-4/generator.js index eec5515889..1458a6b128 100644 --- a/packages/@vue/cli-plugin-webpack-4/generator.js +++ b/packages/@vue/cli-plugin-webpack-4/generator.js @@ -8,7 +8,8 @@ module.exports = (api) => { // Yarn and PNPM 5.10+ support this feature // So we'll try to use that whenever possible resolutions: { - '@vue/cli-*/webpack': '^4.0.0' + '@vue/cli-*/webpack': '^4.0.0', + 'html-webpack-plugin': '^4.5.1' } }) diff --git a/packages/@vue/cli-plugin-webpack-4/index.js b/packages/@vue/cli-plugin-webpack-4/index.js index b66e534a29..3f692ae078 100644 --- a/packages/@vue/cli-plugin-webpack-4/index.js +++ b/packages/@vue/cli-plugin-webpack-4/index.js @@ -1,22 +1,13 @@ -/** @type {import('@vue/cli-service').ServicePlugin} */ -module.exports = (api, options) => { - api.chainWebpack(config => { - if (process.env.VUE_CLI_BUILD_TARGET === 'app') { - const HtmlWebpackPlugin4 = require('html-webpack-plugin') - if (!options.pages) { - config.plugin('html') - .init((Plugin, args) => new HtmlWebpackPlugin4(...args)) - } else { - Object.keys(options.pages).forEach(name => { - config.plugin(`html-${name}`) - .init((Plugin, args) => new HtmlWebpackPlugin4(...args)) - }) - } +const path = require('path') +const moduleAlias = require('module-alias') - // TODO: copy-webpack-plugin v6 - } +const htmlWebpackPlugin4Path = path.dirname(require.resolve('html-webpack-plugin/package.json')) +// We have to use module-alias for html-webpack-plguin, as it is required by many other plugins +// as peer dependency for its `getHooks` API. +// Should add the alias as early as possible to avoid problems +// TODO: add debugging log here +moduleAlias.addAlias('html-webpack-plugin', htmlWebpackPlugin4Path) - // TODO: terser-webpack-plugin v4 - // TODO: css-minimizer-webpack-plugin v1 - }) +/** @type {import('@vue/cli-service').ServicePlugin} */ +module.exports = (api, options) => { } diff --git a/packages/@vue/cli-plugin-webpack-4/package.json b/packages/@vue/cli-plugin-webpack-4/package.json index cffd611f8f..e8640e6555 100644 --- a/packages/@vue/cli-plugin-webpack-4/package.json +++ b/packages/@vue/cli-plugin-webpack-4/package.json @@ -24,6 +24,7 @@ }, "dependencies": { "html-webpack-plugin": "^4.5.1", + "module-alias": "^2.2.2", "webpack": "^4.44.2" }, "peerDependencies": { From 03753e8f7363c1658b8d989830f4a75aac924a95 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang <haoqunjiang@gmail.com> Date: Mon, 8 Feb 2021 15:08:09 +0800 Subject: [PATCH 06/10] test: fix e2e tests --- .../__tests__/pluginCompatibility.spec.js | 4 ++-- .../__tests__/pluginCustomization.spec.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCompatibility.spec.js b/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCompatibility.spec.js index 51b1fc5a5f..fdc9780961 100644 --- a/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCompatibility.spec.js +++ b/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCompatibility.spec.js @@ -1,4 +1,4 @@ -jest.setTimeout(30000) +jest.setTimeout(3000000) const path = require('path') const { defaultPreset } = require('@vue/cli/lib/options') @@ -74,7 +74,7 @@ test('build', async () => { }) test('multi-page', async () => { - const project = await create('e2e-build-webpack-4', webpack4Preset) + const project = await create('e2e-build-webpack-4-multipage', webpack4Preset) await project.write('vue.config.js', ` module.exports = { diff --git a/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCustomization.spec.js b/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCustomization.spec.js index f714c764de..9cbf3c7482 100644 --- a/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCustomization.spec.js +++ b/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCustomization.spec.js @@ -1,4 +1,4 @@ -jest.setTimeout(30000) +jest.setTimeout(3000000) const { defaultPreset } = require('@vue/cli/lib/options') const create = require('@vue/cli-test-utils/createUpgradableProject') From c4347bf07b8ce31081ac93312e270c76df2b2e76 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang <haoqunjiang@gmail.com> Date: Mon, 8 Feb 2021 16:58:57 +0800 Subject: [PATCH 07/10] fix: compatibility with `scriptLoading: 'defer'` option in html webpack plugin --- .../@vue/cli-service/__tests__/build.spec.js | 4 ++-- .../@vue/cli-service/__tests__/cors.spec.js | 4 ++-- .../cli-service/__tests__/modernMode.spec.js | 12 ++++++------ .../lib/webpack/ModernModePlugin.js | 19 ++++++++++++++----- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/packages/@vue/cli-service/__tests__/build.spec.js b/packages/@vue/cli-service/__tests__/build.spec.js index 92bbe79d91..8ebd58e65a 100644 --- a/packages/@vue/cli-service/__tests__/build.spec.js +++ b/packages/@vue/cli-service/__tests__/build.spec.js @@ -38,8 +38,8 @@ test('build', async () => { // expect(index).toMatch(/<link [^>]+app[^>]+\.css" rel="preload" as="style">/) // should inject scripts - expect(index).toMatch(/<script src="\/js\/chunk-vendors\.\w{8}\.js">/) - expect(index).toMatch(/<script src="\/js\/app\.\w{8}\.js">/) + expect(index).toMatch(/<script defer="defer" src="\/js\/chunk-vendors\.\w{8}\.js">/) + expect(index).toMatch(/<script defer="defer" src="\/js\/app\.\w{8}\.js">/) // should inject css expect(index).toMatch(/<link href="\/css\/app\.\w{8}\.css" rel="stylesheet">/) diff --git a/packages/@vue/cli-service/__tests__/cors.spec.js b/packages/@vue/cli-service/__tests__/cors.spec.js index 9bd62d02d8..64dfd15f8d 100644 --- a/packages/@vue/cli-service/__tests__/cors.spec.js +++ b/packages/@vue/cli-service/__tests__/cors.spec.js @@ -30,8 +30,8 @@ test('build', async () => { // expect(index).toMatch(/<link [^>]+app[^>]+\.css rel=preload as=style crossorigin>/) // should apply crossorigin and add integrity to scripts and css - expect(index).toMatch(/<script src="\/js\/chunk-vendors\.\w{8}\.js" crossorigin integrity="sha384-.{64}\s?">/) - expect(index).toMatch(/<script src="\/js\/app\.\w{8}\.js" crossorigin integrity="sha384-.{64}\s?">/) + expect(index).toMatch(/<script defer="defer" src="\/js\/chunk-vendors\.\w{8}\.js" crossorigin integrity="sha384-.{64}\s?">/) + expect(index).toMatch(/<script defer="defer" src="\/js\/app\.\w{8}\.js" crossorigin integrity="sha384-.{64}\s?">/) expect(index).toMatch(/<link href="\/css\/app\.\w{8}\.css" rel="stylesheet" crossorigin integrity="sha384-.{64}\s?">/) // verify integrity is correct by actually running it diff --git a/packages/@vue/cli-service/__tests__/modernMode.spec.js b/packages/@vue/cli-service/__tests__/modernMode.spec.js index 1542e2486a..b74d9fec8f 100644 --- a/packages/@vue/cli-service/__tests__/modernMode.spec.js +++ b/packages/@vue/cli-service/__tests__/modernMode.spec.js @@ -32,16 +32,16 @@ test('modern mode', async () => { const index = await project.read('dist/index.html') // should use <script type="module" crossorigin="use-credentials"> for modern bundle - expect(index).toMatch(/<script src="\/js\/chunk-vendors\.\w{8}\.js" type="module">/) - expect(index).toMatch(/<script src="\/js\/app\.\w{8}\.js" type="module">/) + expect(index).toMatch(/<script defer="defer" src="\/js\/chunk-vendors\.\w{8}\.js" type="module">/) + expect(index).toMatch(/<script defer="defer" src="\/js\/app\.\w{8}\.js" type="module">/) // should use <link rel="modulepreload" crossorigin="use-credentials"> for modern bundle // expect(index).toMatch(/<link [^>]*js\/chunk-vendors\.\w{8}\.js" rel="modulepreload" as="script">/) // expect(index).toMatch(/<link [^>]*js\/app\.\w{8}\.js" rel="modulepreload" as="script">/) // should use <script nomodule> for legacy bundle - expect(index).toMatch(/<script src="\/js\/chunk-vendors-legacy\.\w{8}\.js" nomodule>/) - expect(index).toMatch(/<script src="\/js\/app-legacy\.\w{8}\.js" nomodule>/) + expect(index).toMatch(/<script defer="defer" src="\/js\/chunk-vendors-legacy\.\w{8}\.js" nomodule>/) + expect(index).toMatch(/<script defer="defer" src="\/js\/app-legacy\.\w{8}\.js" nomodule>/) // should inject Safari 10 nomodule fix const { safariFix } = require('../lib/webpack/ModernModePlugin') @@ -53,8 +53,8 @@ test('modern mode', async () => { expect(stdout2).toMatch('Build complete.') const index2 = await project.read('dist/index.html') // should use <script type="module" crossorigin="use-credentials"> for modern bundle - expect(index2).toMatch(/<script src="\/js\/chunk-vendors\.\w{8}\.js" crossorigin="use-credentials" type="module">/) - expect(index2).toMatch(/<script src="\/js\/app\.\w{8}\.js" crossorigin="use-credentials" type="module">/) + expect(index2).toMatch(/<script defer="defer" src="\/js\/chunk-vendors\.\w{8}\.js" crossorigin="use-credentials" type="module">/) + expect(index2).toMatch(/<script defer="defer" src="\/js\/app\.\w{8}\.js" crossorigin="use-credentials" type="module">/) // should use <link rel="modulepreload" crossorigin="use-credentials"> for modern bundle // expect(index2).toMatch(/<link [^>]*js\/chunk-vendors\.\w{8}\.js" rel="modulepreload" as="script" crossorigin="use-credentials">/) // expect(index2).toMatch(/<link [^>]*js\/app\.\w{8}\.js" rel="modulepreload" as="script" crossorigin="use-credentials">/) diff --git a/packages/@vue/cli-service/lib/webpack/ModernModePlugin.js b/packages/@vue/cli-service/lib/webpack/ModernModePlugin.js index 874b361338..3e3d3a7b44 100644 --- a/packages/@vue/cli-service/lib/webpack/ModernModePlugin.js +++ b/packages/@vue/cli-service/lib/webpack/ModernModePlugin.js @@ -32,7 +32,12 @@ class ModernModePlugin { const htmlPath = path.dirname(data.plugin.options.filename) const tempFilename = path.join(this.targetDir, htmlPath, `legacy-assets-${htmlName}.json`) await fs.mkdirp(path.dirname(tempFilename)) - await fs.writeFile(tempFilename, JSON.stringify(data.bodyTags)) + + let tags = data.bodyTags + if (data.plugin.options.scriptLoading === 'defer') { + tags = data.headTags + } + await fs.writeFile(tempFilename, JSON.stringify(tags)) cb() }) }) @@ -42,8 +47,12 @@ class ModernModePlugin { const ID = `vue-cli-modern-bundle` compiler.hooks.compilation.tap(ID, compilation => { HtmlWebpackPlugin.getHooks(compilation).alterAssetTagGroups.tapAsync(ID, async (data, cb) => { + let tags = data.bodyTags + if (data.plugin.options.scriptLoading === 'defer') { + tags = data.headTags + } // use <script type="module"> for modern assets - data.bodyTags.forEach(tag => { + tags.forEach(tag => { if (tag.tagName === 'script' && tag.attributes) { tag.attributes.type = 'module' } @@ -70,7 +79,7 @@ class ModernModePlugin { if (this.unsafeInline) { // inject inline Safari 10 nomodule fix - data.bodyTags.push({ + tags.push({ tagName: 'script', closeTag: true, innerHTML: safariFix @@ -87,7 +96,7 @@ class ModernModePlugin { return Buffer.byteLength(safariFix) } } - data.bodyTags.push({ + tags.push({ tagName: 'script', closeTag: true, attributes: { @@ -96,7 +105,7 @@ class ModernModePlugin { }) } - data.bodyTags.push(...legacyAssets) + tags.push(...legacyAssets) await fs.remove(tempFilename) cb() }) From 112bc7d4856ac55e7f0fce6b13889a72597f2516 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang <haoqunjiang@gmail.com> Date: Mon, 8 Feb 2021 18:40:17 +0800 Subject: [PATCH 08/10] test: these are covered in VUE_CLI_USE_WEBPACK4 tests --- .../__tests__/pluginCompatibility.spec.js | 102 ------------------ .../__tests__/pluginCustomization.spec.js | 21 ---- 2 files changed, 123 deletions(-) delete mode 100644 packages/@vue/cli-plugin-webpack-4/__tests__/pluginCompatibility.spec.js delete mode 100644 packages/@vue/cli-plugin-webpack-4/__tests__/pluginCustomization.spec.js diff --git a/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCompatibility.spec.js b/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCompatibility.spec.js deleted file mode 100644 index fdc9780961..0000000000 --- a/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCompatibility.spec.js +++ /dev/null @@ -1,102 +0,0 @@ -jest.setTimeout(3000000) - -const path = require('path') -const { defaultPreset } = require('@vue/cli/lib/options') -const create = require('@vue/cli-test-utils/createUpgradableProject') -const portfinder = require('portfinder') -const createServer = require('@vue/cli-test-utils/createServer') -const launchPuppeteer = require('@vue/cli-test-utils/launchPuppeteer') - -let server, browser, page - -const webpack4Preset = { - ...defaultPreset, - plugins: { - ...defaultPreset.plugins, - '@vue/cli-plugin-webpack-4': {} - } -} - -test('build', async () => { - // TODO: - // Find a way to test which webpack version the project is built with - // For now it's not necessary as the mocha tests have covered it (mochapack doesn't work with webpack 5) - - const project = await create('e2e-build-webpack-4', webpack4Preset) - // test public copy - project.write('public/foo.js', '1') - // make sure that only /public/index.html is skipped (#3119) - project.write('public/subfolder/index.html', '1') - - const { stdout } = await project.run('vue-cli-service build') - expect(stdout).toMatch('Build complete.') - - expect(project.has('dist/index.html')).toBe(true) - expect(project.has('dist/favicon.ico')).toBe(true) - expect(project.has('dist/js')).toBe(true) - expect(project.has('dist/css')).toBe(true) - expect(project.has('dist/foo.js')).toBe(true) - expect(project.has('dist/subfolder/index.html')).toBe(true) - - const index = await project.read('dist/index.html') - - // should have set the title inferred from the project name - expect(index).toMatch(/<title>e2e-build<\/title>/) - - // should inject scripts - expect(index).toMatch(/<script src="\/js\/chunk-vendors\.\w{8}\.js">/) - expect(index).toMatch(/<script src="\/js\/app\.\w{8}\.js">/) - // should inject css - expect(index).toMatch(/<link href="\/css\/app\.\w{8}\.css" rel="stylesheet">/) - - // should reference favicon with correct base URL - expect(index).toMatch(/<link rel="icon" href="\/favicon.ico">/) - - 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}/`) - browser = launched.browser - page = launched.page - - const h1Text = await page.evaluate(() => { - return document.querySelector('h1').textContent - }) - - expect(h1Text).toMatch('Welcome to Your Vue.js App') -}) - -test('multi-page', async () => { - const project = await create('e2e-build-webpack-4-multipage', webpack4Preset) - - await project.write('vue.config.js', ` -module.exports = { - pages: { - foo: 'src/main.js', - bar: 'src/main.js', - } -} - `) - - const { stdout } = await project.run('vue-cli-service build') - expect(stdout).toMatch('Build complete.') - - expect(project.has('dist/foo.html')).toBe(true) - expect(project.has('dist/bar.html')).toBe(true) -}) - -afterAll(async () => { - if (browser) { - await browser.close() - } - if (server) { - server.close() - } -}) diff --git a/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCustomization.spec.js b/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCustomization.spec.js deleted file mode 100644 index 9cbf3c7482..0000000000 --- a/packages/@vue/cli-plugin-webpack-4/__tests__/pluginCustomization.spec.js +++ /dev/null @@ -1,21 +0,0 @@ -jest.setTimeout(3000000) - -const { defaultPreset } = require('@vue/cli/lib/options') -const create = require('@vue/cli-test-utils/createUpgradableProject') - -const webpack4Preset = { - ...defaultPreset, - plugins: { - ...defaultPreset.plugins, - '@vue/cli-plugin-webpack-4': {} - } -} - -test('build with customizations', async () => { - const project = await create('e2e-custom-webpack-4', webpack4Preset) - - // TODO: customizations - - const { stdout } = await project.run('vue-cli-service build') - expect(stdout).toMatch('Build complete.') -}) From 8f7ccbfd60e8dcde4f965ffa45e434d58ef7255d Mon Sep 17 00:00:00 2001 From: Haoqun Jiang <haoqunjiang@gmail.com> Date: Mon, 8 Feb 2021 18:40:40 +0800 Subject: [PATCH 09/10] feat!: use set `scriptLoading` to `defer` by default --- packages/@vue/cli-service/lib/config/app.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@vue/cli-service/lib/config/app.js b/packages/@vue/cli-service/lib/config/app.js index ad5d9aa758..b060b61fdd 100644 --- a/packages/@vue/cli-service/lib/config/app.js +++ b/packages/@vue/cli-service/lib/config/app.js @@ -83,6 +83,7 @@ module.exports = (api, options) => { const htmlOptions = { title: api.service.pkg.name, + scriptLoading: 'defer', templateParameters: (compilation, assets, assetTags, pluginOptions) => { // enhance html-webpack-plugin's built in template params let stats From a12fc3f5830f040d95076f147e8b1c27fb9575bf Mon Sep 17 00:00:00 2001 From: Haoqun Jiang <haoqunjiang@gmail.com> Date: Mon, 8 Feb 2021 18:41:02 +0800 Subject: [PATCH 10/10] docs: mention html-webpack-plugin v5 upgrade --- docs/migrations/migrate-from-v4.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migrations/migrate-from-v4.md b/docs/migrations/migrate-from-v4.md index f41a5ea13f..d4181d0d3a 100644 --- a/docs/migrations/migrate-from-v4.md +++ b/docs/migrations/migrate-from-v4.md @@ -78,7 +78,7 @@ No longer supports generating project with `node-sass`. It has been [deprecated] #### Underlying Loaders and Plugins -* `html-webpack-plugin` is upgraded from v3 to v4, see more details in the [release announcement](https://dev.to/jantimon/html-webpack-plugin-4-has-been-released-125d). +* `html-webpack-plugin` is upgraded from v3 to v5, and for webpack 4 users, v4 will be used. More details are available in the [release announcement of `html-webpack-plugin` v4](https://dev.to/jantimon/html-webpack-plugin-4-has-been-released-125d) and the [full changelog](https://github.com/jantimon/html-webpack-plugin/blob/master/CHANGELOG.md). * `sass-loader` v7 support is dropped. See the v8 breaking changes at its [changelog](https://github.com/webpack-contrib/sass-loader/blob/master/CHANGELOG.md#800-2019-08-29). * `postcss-loader` is upgraded from v3 to v4. Most notably, `PostCSS` options (`plugin` / `syntax` / `parser` / `stringifier`) are moved into the `postcssOptions` field. More details available at the [changelog](https://github.com/webpack-contrib/postcss-loader/blob/master/CHANGELOG.md#400-2020-09-07). * `copy-webpack-plugin` is upgraded from v5 to v6. If you never customized its config through `config.plugin('copy')`, there should be no user-facing breaking changes. A full list of breaking changes is available at [`copy-webpack-plugin` v6.0.0 release](https://github.com/webpack-contrib/copy-webpack-plugin/releases/tag/v6.0.0).