Skip to content

Commit 81e3ef6

Browse files
authored
feat($core): leverage webpack-dev-server and sunset webpack-serve (#1195)
Breaking changes: - Plugin option`enhanceDevServer` was removed. New plugin option API: - beforeDevServer - afterDevServer
1 parent e5b07c8 commit 81e3ef6

File tree

13 files changed

+761
-713
lines changed

13 files changed

+761
-713
lines changed

packages/@vuepress/core/lib/dev.js

+53-48
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,31 @@
11
'use strict'
22

3-
module.exports = async function dev (sourceDir, cliOptions = {}) {
3+
module.exports = async (sourceDir, cliOptions = {}, ctx) => {
4+
const { server, host, port } = await prepareServer(sourceDir, cliOptions, ctx)
5+
server.listen(port, host, err => {
6+
if (err) {
7+
console.log(err)
8+
}
9+
})
10+
}
11+
12+
module.exports.prepare = prepareServer
13+
14+
async function prepareServer (sourceDir, cliOptions = {}, context) {
15+
const WebpackDevServer = require('webpack-dev-server')
416
const { path } = require('@vuepress/shared-utils')
517
const webpack = require('webpack')
618
const chokidar = require('chokidar')
7-
const serve = require('webpack-serve')
8-
const convert = require('koa-connect')
9-
const mount = require('koa-mount')
10-
const range = require('koa-range')
11-
const serveStatic = require('koa-static')
12-
const history = require('connect-history-api-fallback')
1319

1420
const prepare = require('./prepare/index')
15-
const { chalk, fs, logger } = require('@vuepress/shared-utils')
21+
const { chalk, logger } = require('@vuepress/shared-utils')
1622
const HeadPlugin = require('./webpack/HeadPlugin')
1723
const DevLogPlugin = require('./webpack/DevLogPlugin')
1824
const createClientConfig = require('./webpack/createClientConfig')
1925
const { applyUserWebpackConfig } = require('./util/index')
2026
const { frontmatterEmitter } = require('@vuepress/markdown-loader')
2127

22-
logger.wait('Extracting site metadata...')
23-
const ctx = await prepare(sourceDir, cliOptions, false /* isProd */)
28+
const ctx = context || await prepare(sourceDir, cliOptions, false /* isProd */)
2429

2530
// setup watchers to update options and dynamically generated files
2631
const update = (reason) => {
@@ -105,52 +110,52 @@ module.exports = async function dev (sourceDir, cliOptions = {}) {
105110
config = applyUserWebpackConfig(userConfig, config, false /* isServer */)
106111
}
107112

113+
const serverConfig = Object.assign({
114+
disableHostCheck: true,
115+
compress: true,
116+
clientLogLevel: 'error',
117+
hot: true,
118+
quiet: true,
119+
headers: {
120+
'access-control-allow-origin': '*'
121+
},
122+
publicPath: ctx.base,
123+
watchOptions: {
124+
ignored: /node_modules/
125+
},
126+
historyApiFallback: {
127+
rewrites: [
128+
{ from: /\.html$/, to: '/' }
129+
]
130+
},
131+
overlay: false,
132+
host,
133+
contentBase: path.resolve(sourceDir, '.vuepress/public'),
134+
before (app, server) {
135+
ctx.pluginAPI.options.beforeDevServer.syncApply(app, server)
136+
},
137+
after (app, server) {
138+
ctx.pluginAPI.options.afterDevServer.syncApply(app, server)
139+
}
140+
}, ctx.siteConfig.devServer || {})
141+
142+
WebpackDevServer.addDevServerEntrypoints(config, serverConfig)
143+
108144
const compiler = webpack(config)
145+
const server = new WebpackDevServer(compiler, serverConfig)
109146

110-
const nonExistentDir = path.resolve(__dirname, 'non-existent')
111-
await serve({
112-
// avoid project cwd from being served. Otherwise if the user has index.html
113-
// in cwd it would break the server
114-
content: [nonExistentDir],
115-
compiler,
147+
return {
148+
server,
116149
host,
117-
dev: { logLevel: 'warn' },
118-
hot: {
119-
port: port + 1,
120-
logLevel: 'error'
121-
},
122-
logLevel: 'error',
123150
port,
124-
open: cliOptions.open,
125-
add: app => {
126-
// apply plugin options to extend dev server.
127-
ctx.pluginAPI.options.enhanceDevServer.syncApply(app)
128-
129-
const userPublic = path.resolve(sourceDir, '.vuepress/public')
130-
131-
// enable range request
132-
app.use(range)
133-
134-
// respect base when serving static files...
135-
if (fs.existsSync(userPublic)) {
136-
app.use(mount(ctx.base, serveStatic(userPublic)))
137-
}
138-
139-
app.use(convert(history({
140-
rewrites: [
141-
{ from: /\.html$/, to: '/' }
142-
]
143-
})))
144-
}
145-
})
151+
ctx
152+
}
146153
}
147154

148155
function resolveHost (host) {
149-
// webpack-serve hot updates doesn't work properly over 0.0.0.0 on Windows,
150-
// but localhost does not allow visiting over network :/
151-
const defaultHost = process.platform === 'win32' ? 'localhost' : '0.0.0.0'
156+
const defaultHost = 'localhost'
152157
host = host || defaultHost
153-
const displayHost = host === defaultHost && process.platform !== 'win32'
158+
const displayHost = host === defaultHost
154159
? 'localhost'
155160
: host
156161
return {

packages/@vuepress/core/lib/plugin-api/constants.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ const PLUGIN_OPTION_META_MAP = {
88
GENERATED: { name: 'generated', types: [Function], async: true },
99
// options
1010
CHAIN_WEBPACK: { name: 'chainWebpack', types: [Function] },
11-
ENHANCE_DEV_SERVER: { name: 'enhanceDevServer', types: [Function] },
1211
ENHANCE_APP_FILES: { name: 'enhanceAppFiles', types: [String, Object, Array, Function] },
1312
OUT_FILES: { name: 'outFiles', types: [Object] },
1413
EXTEND_PAGE_DATA: { name: 'extendPageData', types: [Function] },
@@ -20,7 +19,9 @@ const PLUGIN_OPTION_META_MAP = {
2019
GLOBAL_UI_COMPONENTS: { name: 'globalUIComponents', types: [String, Array] },
2120
DEFINE: { name: 'define', types: [Function, Object] },
2221
ALIAS: { name: 'alias', types: [Function, Object] },
23-
EXTEND_CLI: { name: 'extendCli', types: [Function] }
22+
EXTEND_CLI: { name: 'extendCli', types: [Function] },
23+
BEFORE_DEV_SERVER: { name: 'beforeDevServer', types: [Function] },
24+
AFTER_DEV_SERVER: { name: 'afterDevServer', types: [Function] }
2425
}
2526

2627
const PLUGIN_OPTION_MAP = {}

packages/@vuepress/core/lib/plugin-api/index.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,6 @@ module.exports = class PluginAPI {
195195

196196
// options
197197
chainWebpack,
198-
enhanceDevServer,
199198
extendMarkdown,
200199
chainMarkdown,
201200
enhanceAppFiles,
@@ -207,7 +206,9 @@ module.exports = class PluginAPI {
207206
globalUIComponents,
208207
define,
209208
alias,
210-
extendCli
209+
extendCli,
210+
beforeDevServer,
211+
afterDevServer
211212
}) {
212213
const isInternalPlugin = pluginName.startsWith('@vuepress/internal-')
213214
logger[isInternalPlugin ? 'debug' : 'tip'](pluginLog(pluginName, shortcut))
@@ -218,7 +219,6 @@ module.exports = class PluginAPI {
218219
.registerOption(PLUGIN_OPTION_MAP.UPDATED.key, updated, pluginName)
219220
.registerOption(PLUGIN_OPTION_MAP.GENERATED.key, generated, pluginName)
220221
.registerOption(PLUGIN_OPTION_MAP.CHAIN_WEBPACK.key, chainWebpack, pluginName)
221-
.registerOption(PLUGIN_OPTION_MAP.ENHANCE_DEV_SERVER.key, enhanceDevServer, pluginName)
222222
.registerOption(PLUGIN_OPTION_MAP.EXTEND_MARKDOWN.key, extendMarkdown, pluginName)
223223
.registerOption(PLUGIN_OPTION_MAP.CHAIN_MARKDOWN.key, chainMarkdown, pluginName)
224224
.registerOption(PLUGIN_OPTION_MAP.EXTEND_PAGE_DATA.key, extendPageData, pluginName)
@@ -231,6 +231,8 @@ module.exports = class PluginAPI {
231231
.registerOption(PLUGIN_OPTION_MAP.DEFINE.key, define, pluginName)
232232
.registerOption(PLUGIN_OPTION_MAP.ALIAS.key, alias, pluginName)
233233
.registerOption(PLUGIN_OPTION_MAP.EXTEND_CLI.key, extendCli, pluginName)
234+
.registerOption(PLUGIN_OPTION_MAP.BEFORE_DEV_SERVER.key, beforeDevServer, pluginName)
235+
.registerOption(PLUGIN_OPTION_MAP.AFTER_DEV_SERVER.key, afterDevServer, pluginName)
234236
}
235237
}
236238

packages/@vuepress/core/lib/prepare/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
*/
66

77
const AppContext = require('./AppContext')
8+
const { logger } = require('@vuepress/shared-utils')
89

910
/**
1011
* Expose prepare.
1112
*/
1213

1314
module.exports = async function prepare (sourceDir, cliOptions, isProd) {
15+
logger.wait('Extracting site metadata...')
1416
const appContext = AppContext.getInstance(sourceDir, cliOptions, isProd)
1517
await appContext.process()
1618
return appContext

packages/@vuepress/core/lib/webpack/createClientConfig.js

+4
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ module.exports = function createClientConfig (ctx) {
5454
mergeLonghand: false
5555
}
5656
}])
57+
} else {
58+
config
59+
.plugin('hmr')
60+
.use(require('webpack/lib/HotModuleReplacementPlugin'))
5761
}
5862

5963
if (!env.isDebug) {

packages/@vuepress/core/package.json

+1-5
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,6 @@
4545
"css-loader": "^0.28.11",
4646
"file-loader": "^1.1.11",
4747
"js-yaml": "^3.11.0",
48-
"koa-connect": "^2.0.1",
49-
"koa-mount": "^3.0.0",
50-
"koa-range": "^0.3.0",
51-
"koa-static": "^4.0.2",
5248
"lru-cache": "^5.1.1",
5349
"mini-css-extract-plugin": "0.4.4",
5450
"optimize-css-assets-webpack-plugin": "^4.0.0",
@@ -65,7 +61,7 @@
6561
"webpack": "^4.8.1",
6662
"webpack-chain": "^4.6.0",
6763
"webpack-merge": "^4.1.2",
68-
"webpack-serve": "^1.0.2",
64+
"webpack-dev-server": "^3.1.14",
6965
"webpackbar": "^2.6.1"
7066
},
7167
"engines": {

packages/docs/docs/plugin/option-api.md

+17-23
Original file line numberDiff line numberDiff line change
@@ -132,42 +132,36 @@ module.exports = (options, context) => ({
132132
})
133133
```
134134

135-
## enhanceDevServer
135+
## beforeDevServer
136136

137137
- Type: `Function`
138138
- Default: undefined
139139

140-
Enhance the underlying [Koa](https://github.com/koajs/koa) app.
140+
Equivalent to [before](https://webpack.js.org/configuration/dev-server/#devserver-before) in [webpack-dev-server](https://github.com/webpack/webpack-dev-server). you can use it to define custom handlers before all middleware is executed:
141141

142-
``` js
142+
```js
143143
module.exports = {
144-
enhanceDevServer (app) {
145-
// ...
144+
// ...
145+
beforeDevServer(app, server) {
146+
app.get('/path/to/your/custom', function(req, res) {
147+
res.json({ custom: 'response' })
148+
})
146149
}
147150
}
148151
```
149152

150-
A simple plugin to create a sub public directory is as follows:
153+
## afterDevServer
151154

152-
```js
153-
const path = require('path')
155+
- Type: `Function`
156+
- Default: undefined
154157

155-
module.exports = (options, context) => {
156-
const imagesAssetsPath = path.resolve(context.sourceDir, '.vuepress/images')
158+
Equivalent to [after](https://webpack.js.org/configuration/dev-server/#devserver-after) in [webpack-dev-server](https://github.com/webpack/webpack-dev-server). you can use it to execute custom middleware after all other middleware:
157159

158-
return {
159-
// For development
160-
enhanceDevServer (app) {
161-
const mount = require('koa-mount')
162-
const serveStatic = require('koa-static')
163-
app.use(mount(path.join(context.base, 'images'), serveStatic(imagesAssetsPath)))
164-
},
165-
166-
// For production
167-
async generated () {
168-
const { fs } = require('@vuepress/shared-utils')
169-
await fs.copy(imagesAssetsPath, path.resolve(context.outDir, 'images'))
170-
}
160+
```js
161+
module.exports = {
162+
// ...
163+
afterDevServer(app, server) {
164+
// hacking now ...
171165
}
172166
}
173167
```

packages/docs/docs/zh/plugin/option-api.md

+17-23
Original file line numberDiff line numberDiff line change
@@ -136,42 +136,36 @@ module.exports = (options, context) => ({
136136
})
137137
```
138138

139-
## enhanceDevServer
139+
## beforeDevServer
140140

141141
- 类型: `Function`
142142
- 默认值: undefined
143143

144-
拓展 devServer 下层的 [Koa](https://github.com/koajs/koa) app
144+
等同于 [webpack-dev-server](https://github.com/webpack/webpack-dev-server) 中的 [before](https://webpack.js.org/configuration/dev-server/#devserver-before) 选项,你可以使用它来自定义你的 devServer,如
145145

146-
``` js
146+
```js
147147
module.exports = {
148-
enhanceDevServer (app) {
149-
// ...
148+
// ...
149+
beforeDevServer(app, server) {
150+
app.get('/path/to/your/custom', function(req, res) {
151+
res.json({ custom: 'response' })
152+
})
150153
}
151154
}
152155
```
153156

154-
一个简单的创建子 public 目录的插件如下:
157+
## afterDevServer
155158

156-
```js
157-
const path = require('path')
159+
- 类型: `Function`
160+
- 默认值: undefined
158161

159-
module.exports = (options, context) => {
160-
const imagesAssetsPath = path.resolve(context.sourceDir, '.vuepress/images')
162+
等同于 [webpack-dev-server](https://github.com/webpack/webpack-dev-server) 中的 [after](https://webpack.js.org/configuration/dev-server/#devserver-after),你可以用其在所有中间件的最后去执行一些自定义的中间件:
161163

162-
return {
163-
// For development
164-
enhanceDevServer (app) {
165-
const mount = require('koa-mount')
166-
const serveStatic = require('koa-static')
167-
app.use(mount(path.join(context.base, 'images'), serveStatic(imagesAssetsPath)))
168-
},
169-
170-
// For production
171-
async generated () {
172-
const { fs } = require('@vuepress/shared-utils')
173-
await fs.copy(imagesAssetsPath, path.resolve(context.outDir, 'images'))
174-
}
164+
```js
165+
module.exports = {
166+
// ...
167+
afterDevServer(app, server) {
168+
// hacking now ...
175169
}
176170
}
177171
```
File renamed without changes.

packages/vuepress/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('@vuepress/core')

packages/vuepress/lib/handleUnknownCommand.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ module.exports = async function (cli, options) {
3939

4040
if (sourceDir) {
4141
context = await prepare(sourceDir, options)
42-
context.pluginAPI.options.extendCli.apply(cli)
42+
context.pluginAPI.options.extendCli.apply(cli, context)
4343
}
4444

4545
logger.setOptions({ logLevel: 3 })
@@ -107,7 +107,7 @@ function registerUnknownCommands (cli, options) {
107107
...options,
108108
...commandoptions
109109
}, false /* isProd */)
110-
await context.pluginAPI.options.extendCli.apply(subCli)
110+
await context.pluginAPI.options.extendCli.apply(subCli, context)
111111
},
112112
async afterParse (subCli) {
113113
if (!subCli.matchedCommand) {

packages/vuepress/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "vuepress",
33
"version": "1.0.0-alpha.32",
44
"description": "Minimalistic doc generator with Vue component based layout system",
5-
"main": "vuepress.js",
5+
"main": "index.js",
66
"repository": {
77
"type": "git",
88
"url": "git+https://github.com/vuejs/vuepress.git"
@@ -13,7 +13,7 @@
1313
"generator"
1414
],
1515
"bin": {
16-
"vuepress": "vuepress.js"
16+
"vuepress": "cli.js"
1717
},
1818
"author": "Evan You",
1919
"maintainers": [

0 commit comments

Comments
 (0)