Skip to content

Commit

Permalink
feat!: upgrade to webpack-dev-server v4 (#6669)
Browse files Browse the repository at this point in the history
  • Loading branch information
haoqunjiang authored Sep 15, 2021
1 parent 662153b commit 967f948
Show file tree
Hide file tree
Showing 8 changed files with 353 additions and 121 deletions.
12 changes: 11 additions & 1 deletion docs/migrations/migrate-from-v4.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,17 @@ Besides the internal changes that are only noticeable for custom configurations,
1. Named exports from JSON modules are no longer supported. Instead of `import { version } from './package.json'; console.log(version);` use `import package from './package.json'; console.log(package.version);`
2. Webpack 5 does no longer include polyfills for Node.js modules by default. You shall see an informative error message if your code relies on any of these modules. A detailed list of previously polyfilled modules is also available [here](https://github.com/webpack/webpack/pull/8460/commits/a68426e9255edcce7822480b78416837617ab065).

#### Changes to the `build` command and modern mode
#### Dev Server

`webpack-dev-server` has been updated from v3 to v4. So there are breaking changes with regard to the `devServer` option in `vue.config.js`. Please check out the [`webpack-dev-server` migration guide](https://github.com/webpack/webpack-dev-server/blob/master/migration-v4.md) for more details.

Most notably:

* The `disableHostCheck` option was removed in favor `allowedHosts: 'all'`;
* `public`, `sockHost`, `sockPath`, and `sockPort` options were removed in favor `client.webSocketURL` option.
* IE9 support of the dev server is not enabled by default. If you need to develop under IE9, please manually set the `devServer.webSocketServer` option to `sockjs`.

#### The `build` Command and Modern Mode

Starting with v5.0.0-beta.0, running `vue-cli-service build` will automatically generate different bundles based on your browserslist configurations.
The `--modern` flag is no longer needed because it is turned on by default.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,8 @@ test(`should use formatter 'codeframe'`, async () => {
} else if (data.match(/semi/)) {
// check the format of output
// https://eslint.org/docs/user-guide/formatters/#codeframe
expect(data).toMatch(`error: Missing semicolon (semi) at src${path.sep}main.js`)
expect(data).toMatch(`error`)
expect(data).toMatch(`Missing semicolon (semi) at src${path.sep}main.js`)

server.stdin.write('close')
done()
Expand Down
4 changes: 2 additions & 2 deletions packages/@vue/cli-service/__tests__/multiPage.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ test('serve w/ multi page', async () => {
async ({ page, url, helpers }) => {
expect(await helpers.getText('h1')).toMatch(`Welcome to Your Vue.js App`)

await page.goto(`${url}/foo.html`)
await page.goto(`${url}foo.html`)
expect(await helpers.getText('h1')).toMatch(`Foo`)

await page.goto(`${url}/bar.html`)
await page.goto(`${url}bar.html`)
expect(await helpers.getText('h1')).toMatch(`Welcome to Your Vue.js App`)

await page.goto(`${url}foo`)
Expand Down
2 changes: 1 addition & 1 deletion packages/@vue/cli-service/__tests__/serve.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ test('use a single websocket connection for HMR', async () => {
const msg = `Welcome to Your Vue.js App`
expect(await helpers.getText('h1')).toMatch(msg)

expect(requestUrls.filter(url => url.includes('sockjs-node')).length).toBe(1)
expect(requestUrls.filter(url => url.includes('ws://')).length).toBe(1)
}
)
})
172 changes: 82 additions & 90 deletions packages/@vue/cli-service/lib/commands/serve.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ const {
error,
hasProjectYarn,
hasProjectPnpm,
openBrowser,
IpcMessenger
} = require('@vue/cli-shared-utils')

Expand Down Expand Up @@ -37,7 +36,6 @@ module.exports = (api, options) => {
const isInContainer = checkInContainer()
const isProduction = process.env.NODE_ENV === 'production'

const url = require('url')
const { chalk } = require('@vue/cli-shared-utils')
const webpack = require('webpack')
const WebpackDevServer = require('webpack-dev-server')
Expand All @@ -56,20 +54,17 @@ module.exports = (api, options) => {
.devtool('eval-cheap-module-source-map')
}

webpackConfig
.plugin('hmr')
.use(require('webpack/lib/HotModuleReplacementPlugin'))

// https://github.com/webpack/webpack/issues/6642
// https://github.com/vuejs/vue-cli/issues/3539
webpackConfig
.output
.globalObject(`(typeof self !== 'undefined' ? self : this)`)

if (!process.env.VUE_CLI_TEST && options.devServer.progress !== false) {
// the default progress plugin won't show progress due to infrastructreLogging.level
webpackConfig
.plugin('progress')
.use(webpack.ProgressPlugin)
.use(require('progress-webpack-plugin'))
}
}
})
Expand All @@ -90,7 +85,7 @@ module.exports = (api, options) => {
// expose advanced stats
if (args.dashboard) {
const DashboardPlugin = require('../webpack/DashboardPlugin')
;(webpackConfig.plugins = webpackConfig.plugins || []).push(new DashboardPlugin({
webpackConfig.plugins.push(new DashboardPlugin({
type: 'serve'
}))
}
Expand Down Expand Up @@ -131,38 +126,50 @@ module.exports = (api, options) => {
)

// inject dev & hot-reload middleware entries
let webSocketURL
if (!isProduction) {
const sockPath = projectDevServerOptions.sockPath || '/sockjs-node'
const sockjsUrl = publicUrl
if (publicHost) {
// explicitly configured via devServer.public
? `?${publicUrl}&sockPath=${sockPath}`
: isInContainer
// can't infer public network url if inside a container...
// use client-side inference (note this would break with non-root publicPath)
? ``
// otherwise infer the url
: `?` + url.format({
protocol,
port,
hostname: urls.lanUrlForConfig || 'localhost'
}) + `&sockPath=${sockPath}`
const devClients = [
// dev server client
require.resolve(`webpack-dev-server/client`) + sockjsUrl,
// hmr client
require.resolve(projectDevServerOptions.hotOnly
? 'webpack/hot/only-dev-server'
: 'webpack/hot/dev-server')
// TODO custom overlay client
// `@vue/cli-overlay/dist/client`
]
webSocketURL = {
protocol: protocol === 'https' ? 'wss' : 'ws',
hostname: publicHost,
port
}
} else if (isInContainer) {
// can't infer public network url if inside a container
// infer it from the browser instead
webSocketURL = 'auto://0.0.0.0:0/ws'
} else {
// otherwise infer the url from the config
webSocketURL = {
protocol: protocol === 'https' ? 'wss' : 'ws',
hostname: urls.lanUrlForConfig || 'localhost',
port
}
}

if (process.env.APPVEYOR) {
devClients.push(`webpack/hot/poll?500`)
webpackConfig.plugins.push(
new webpack.EntryPlugin(__dirname, 'webpack/hot/poll?500', { name: undefined })
)
}
// inject dev/hot client
addDevClientToEntry(webpackConfig, devClients)
}

const { projectTargets } = require('../util/targets')
const supportsIE = !!projectTargets
if (supportsIE) {
webpackConfig.plugins.push(
// must use undefined as name,
// to avoid dev server establishing an extra ws connection for the new entry
new webpack.EntryPlugin(__dirname, 'whatwg-fetch', { name: undefined })
)
}

// fixme: temporary fix to suppress dev server logging
// should be more robust to show necessary info but not duplicate errors
webpackConfig.infrastructureLogging = { ...webpackConfig.infrastructureLogging, level: 'none' }
webpackConfig.stats = 'errors-only'

// create compiler
const compiler = webpack(webpackConfig)

Expand All @@ -173,9 +180,7 @@ module.exports = (api, options) => {
})

// create server
const server = new WebpackDevServer(compiler, Object.assign({
logLevel: 'silent',
clientLogLevel: 'silent',
const server = new WebpackDevServer(Object.assign({
historyApiFallback: {
disableDotRule: true,
htmlAcceptHeaders: [
Expand All @@ -184,47 +189,58 @@ module.exports = (api, options) => {
],
rewrites: genHistoryApiFallbackRewrites(options.publicPath, options.pages)
},
contentBase: api.resolve('public'),
watchContentBase: !isProduction,
hot: !isProduction,
injectClient: false,
compress: isProduction,
publicPath: options.publicPath,
overlay: isProduction // TODO disable this
? false
: { warnings: false, errors: true }
hot: !isProduction
}, projectDevServerOptions, {
host,
port,
https: useHttps,
proxy: proxySettings,
public: publicHost,

static: {
directory: api.resolve('public'),
publicPath: options.publicPath,
watch: !isProduction,

...projectDevServerOptions.static
},

client: {
webSocketURL,

logging: 'none',
overlay: isProduction // TODO disable this

This comment has been minimized.

Copy link
@Mister-Hope

Mister-Hope Sep 23, 2021

PS F:\template\vue> yarn serve
yarn run v1.22.11
$ vue-cli-service serve
 INFO  Starting development server...
[1%] setup (initialize)
 ERROR  ValidationError: Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.  
         - options has an unknown property 'overlay'. These properties are valid:
           object { allowedHosts?, bonjour?, client?, compress?, devMiddleware?, headers?, historyApiFallback?, host?, hot?, http2?, https?, ipc?, liveReload?, magicHtml?, onAfterSetupMiddleware?, onBeforeSetupMiddleware?, onListening?, open?, port?, proxy?, setupExitSignals?, static?, watchFiles?, webSocketServer? }
ValidationError: Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
 - options has an unknown property 'overlay'. These properties are valid:
   object { allowedHosts?, bonjour?, client?, compress?, devMiddleware?, headers?, historyApiFallback?, host?, hot?, http2?, https?, ipc?, liveReload?, magicHtml?, onAfterSetupMiddleware?, onBeforeSetupMiddleware?, onListening?, open?, port?, proxy?, setupExitSignals?, static?, watchFiles?, webSocketServer? }
    at validate (F:\template\vue\node_modules\schema-utils\dist\validate.js:105:11)
    at new Server (F:\template\vue\node_modules\webpack-dev-server\lib\Server.js:31:5)
    at serve (F:\template\vue\node_modules\@vue\cli-service\lib\commands\serve.js:183:20)
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
PS F:\template\vue> 

I personally do not think this can be // TODO. This will stop a project running dev server.

? false
: { warnings: false, errors: true },
progress: !process.env.VUE_CLI_TEST,

...projectDevServerOptions.client
},

open: args.open || projectDevServerOptions.open,
setupExitSignals: true,

// eslint-disable-next-line no-shadow
before (app, server) {
onBeforeSetupMiddleware (server) {
// launch editor support.
// this works with vue-devtools & @vue/cli-overlay
app.use('/__open-in-editor', launchEditorMiddleware(() => console.log(
server.app.use('/__open-in-editor', launchEditorMiddleware(() => console.log(
`To specify an editor, specify the EDITOR env variable or ` +
`add "editor" field to your Vue project config.\n`
)))

// allow other plugins to register middlewares, e.g. PWA
api.service.devServerConfigFns.forEach(fn => fn(app, server))
// apply in project middlewares
projectDevServerOptions.before && projectDevServerOptions.before(app, server)
},
// avoid opening browser
open: false
}))
// todo: migrate to the new API interface
api.service.devServerConfigFns.forEach(fn => fn(server.app, server))

;['SIGINT', 'SIGTERM'].forEach(signal => {
process.on(signal, () => {
server.close(() => {
process.exit(0)
})
})
})
if (projectDevServerOptions.onBeforeSetupMiddleware) {
projectDevServerOptions.onBeforeSetupMiddleware(server)
}
}
}), compiler)

if (args.stdin) {
process.stdin.on('end', () => {
server.close(() => {
server.stopCallback(() => {
process.exit(0)
})
})
Expand All @@ -238,7 +254,7 @@ module.exports = (api, options) => {
process.stdin.on('data', data => {
if (data.toString() === 'close') {
console.log('got close signal!')
server.close(() => {
server.stopCallback(() => {
process.exit(0)
})
}
Expand Down Expand Up @@ -301,13 +317,6 @@ module.exports = (api, options) => {
}
console.log()

if (args.open || projectDevServerOptions.open) {
const pageUri = (projectDevServerOptions.openPage && typeof projectDevServerOptions.openPage === 'string')
? projectDevServerOptions.openPage
: ''
openBrowser(localUrlForBrowser + pageUri)
}

// Send final app URL
if (args.dashboard) {
const ipc = new IpcMessenger()
Expand All @@ -330,28 +339,11 @@ module.exports = (api, options) => {
}
})

server.listen(port, host, err => {
if (err) {
reject(err)
}
})
server.start().catch(err => reject(err))
})
})
}

function addDevClientToEntry (config, devClient) {
const { entry } = config
if (typeof entry === 'object' && !Array.isArray(entry)) {
Object.keys(entry).forEach((key) => {
entry[key] = devClient.concat(entry[key])
})
} else if (typeof entry === 'function') {
config.entry = entry(devClient)
} else {
config.entry = devClient.concat(entry)
}
}

// https://stackoverflow.com/a/20012536
function checkInContainer () {
if ('CODESANDBOX_SSE' in process.env) {
Expand Down
8 changes: 5 additions & 3 deletions packages/@vue/cli-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"@soda/friendly-errors-webpack-plugin": "^1.8.0",
"@soda/get-current-script": "^1.0.2",
"@types/minimist": "^1.2.0",
"@types/webpack-dev-server": "^3.11.0",
"@types/webpack-dev-server": "^4.1.0",
"@vue/cli-overlay": "^5.0.0-beta.3",
"@vue/cli-plugin-router": "^5.0.0-beta.3",
"@vue/cli-plugin-vuex": "^5.0.0-beta.3",
Expand Down Expand Up @@ -68,6 +68,7 @@
"portfinder": "^1.0.26",
"postcss": "^8.2.6",
"postcss-loader": "^6.1.1",
"progress-webpack-plugin": "^1.0.12",
"ssri": "^8.0.1",
"terser-webpack-plugin": "^5.1.1",
"thread-loader": "^3.0.0",
Expand All @@ -77,9 +78,10 @@
"webpack": "^5.22.0",
"webpack-bundle-analyzer": "^4.4.0",
"webpack-chain": "^6.5.1",
"webpack-dev-server": "^3.11.2",
"webpack-dev-server": "^4.1.0",
"webpack-merge": "^5.7.3",
"webpack-virtual-modules": "^0.4.2"
"webpack-virtual-modules": "^0.4.2",
"whatwg-fetch": "^3.6.2"
},
"peerDependencies": {
"@vue/compiler-sfc": "^3.0.0-beta.14",
Expand Down
9 changes: 8 additions & 1 deletion packages/@vue/cli-test-utils/launchPuppeteer.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@ module.exports = async function launchPuppeteer (url) {
interceptedRequest.continue()
})

await page.goto(url)
const f12 = await page.target().createCDPSession()
await f12.send('Network.enable')
await f12.send('Page.enable')

f12.on('Network.webSocketCreated', ({ url: wsUrl }) => {
requestUrls.push(wsUrl)
})

await page.goto(url)
return { browser, page, logs, requestUrls }
}
Loading

0 comments on commit 967f948

Please sign in to comment.