Skip to content

Commit

Permalink
feat($core): leverage webpack-dev-server and sunset webpack-serve (#1195
Browse files Browse the repository at this point in the history
)

Breaking changes:

- Plugin option`enhanceDevServer` was removed.

New plugin option API:

- beforeDevServer
- afterDevServer
  • Loading branch information
ulivz authored Jan 27, 2019
1 parent e5b07c8 commit 81e3ef6
Show file tree
Hide file tree
Showing 13 changed files with 761 additions and 713 deletions.
101 changes: 53 additions & 48 deletions packages/@vuepress/core/lib/dev.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
'use strict'

module.exports = async function dev (sourceDir, cliOptions = {}) {
module.exports = async (sourceDir, cliOptions = {}, ctx) => {
const { server, host, port } = await prepareServer(sourceDir, cliOptions, ctx)
server.listen(port, host, err => {
if (err) {
console.log(err)
}
})
}

module.exports.prepare = prepareServer

async function prepareServer (sourceDir, cliOptions = {}, context) {
const WebpackDevServer = require('webpack-dev-server')
const { path } = require('@vuepress/shared-utils')
const webpack = require('webpack')
const chokidar = require('chokidar')
const serve = require('webpack-serve')
const convert = require('koa-connect')
const mount = require('koa-mount')
const range = require('koa-range')
const serveStatic = require('koa-static')
const history = require('connect-history-api-fallback')

const prepare = require('./prepare/index')
const { chalk, fs, logger } = require('@vuepress/shared-utils')
const { chalk, logger } = require('@vuepress/shared-utils')
const HeadPlugin = require('./webpack/HeadPlugin')
const DevLogPlugin = require('./webpack/DevLogPlugin')
const createClientConfig = require('./webpack/createClientConfig')
const { applyUserWebpackConfig } = require('./util/index')
const { frontmatterEmitter } = require('@vuepress/markdown-loader')

logger.wait('Extracting site metadata...')
const ctx = await prepare(sourceDir, cliOptions, false /* isProd */)
const ctx = context || await prepare(sourceDir, cliOptions, false /* isProd */)

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

const serverConfig = Object.assign({
disableHostCheck: true,
compress: true,
clientLogLevel: 'error',
hot: true,
quiet: true,
headers: {
'access-control-allow-origin': '*'
},
publicPath: ctx.base,
watchOptions: {
ignored: /node_modules/
},
historyApiFallback: {
rewrites: [
{ from: /\.html$/, to: '/' }
]
},
overlay: false,
host,
contentBase: path.resolve(sourceDir, '.vuepress/public'),
before (app, server) {
ctx.pluginAPI.options.beforeDevServer.syncApply(app, server)
},
after (app, server) {
ctx.pluginAPI.options.afterDevServer.syncApply(app, server)
}
}, ctx.siteConfig.devServer || {})

WebpackDevServer.addDevServerEntrypoints(config, serverConfig)

const compiler = webpack(config)
const server = new WebpackDevServer(compiler, serverConfig)

const nonExistentDir = path.resolve(__dirname, 'non-existent')
await serve({
// avoid project cwd from being served. Otherwise if the user has index.html
// in cwd it would break the server
content: [nonExistentDir],
compiler,
return {
server,
host,
dev: { logLevel: 'warn' },
hot: {
port: port + 1,
logLevel: 'error'
},
logLevel: 'error',
port,
open: cliOptions.open,
add: app => {
// apply plugin options to extend dev server.
ctx.pluginAPI.options.enhanceDevServer.syncApply(app)

const userPublic = path.resolve(sourceDir, '.vuepress/public')

// enable range request
app.use(range)

// respect base when serving static files...
if (fs.existsSync(userPublic)) {
app.use(mount(ctx.base, serveStatic(userPublic)))
}

app.use(convert(history({
rewrites: [
{ from: /\.html$/, to: '/' }
]
})))
}
})
ctx
}
}

function resolveHost (host) {
// webpack-serve hot updates doesn't work properly over 0.0.0.0 on Windows,
// but localhost does not allow visiting over network :/
const defaultHost = process.platform === 'win32' ? 'localhost' : '0.0.0.0'
const defaultHost = 'localhost'
host = host || defaultHost
const displayHost = host === defaultHost && process.platform !== 'win32'
const displayHost = host === defaultHost
? 'localhost'
: host
return {
Expand Down
5 changes: 3 additions & 2 deletions packages/@vuepress/core/lib/plugin-api/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ const PLUGIN_OPTION_META_MAP = {
GENERATED: { name: 'generated', types: [Function], async: true },
// options
CHAIN_WEBPACK: { name: 'chainWebpack', types: [Function] },
ENHANCE_DEV_SERVER: { name: 'enhanceDevServer', types: [Function] },
ENHANCE_APP_FILES: { name: 'enhanceAppFiles', types: [String, Object, Array, Function] },
OUT_FILES: { name: 'outFiles', types: [Object] },
EXTEND_PAGE_DATA: { name: 'extendPageData', types: [Function] },
Expand All @@ -20,7 +19,9 @@ const PLUGIN_OPTION_META_MAP = {
GLOBAL_UI_COMPONENTS: { name: 'globalUIComponents', types: [String, Array] },
DEFINE: { name: 'define', types: [Function, Object] },
ALIAS: { name: 'alias', types: [Function, Object] },
EXTEND_CLI: { name: 'extendCli', types: [Function] }
EXTEND_CLI: { name: 'extendCli', types: [Function] },
BEFORE_DEV_SERVER: { name: 'beforeDevServer', types: [Function] },
AFTER_DEV_SERVER: { name: 'afterDevServer', types: [Function] }
}

const PLUGIN_OPTION_MAP = {}
Expand Down
8 changes: 5 additions & 3 deletions packages/@vuepress/core/lib/plugin-api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,6 @@ module.exports = class PluginAPI {

// options
chainWebpack,
enhanceDevServer,
extendMarkdown,
chainMarkdown,
enhanceAppFiles,
Expand All @@ -207,7 +206,9 @@ module.exports = class PluginAPI {
globalUIComponents,
define,
alias,
extendCli
extendCli,
beforeDevServer,
afterDevServer
}) {
const isInternalPlugin = pluginName.startsWith('@vuepress/internal-')
logger[isInternalPlugin ? 'debug' : 'tip'](pluginLog(pluginName, shortcut))
Expand All @@ -218,7 +219,6 @@ module.exports = class PluginAPI {
.registerOption(PLUGIN_OPTION_MAP.UPDATED.key, updated, pluginName)
.registerOption(PLUGIN_OPTION_MAP.GENERATED.key, generated, pluginName)
.registerOption(PLUGIN_OPTION_MAP.CHAIN_WEBPACK.key, chainWebpack, pluginName)
.registerOption(PLUGIN_OPTION_MAP.ENHANCE_DEV_SERVER.key, enhanceDevServer, pluginName)
.registerOption(PLUGIN_OPTION_MAP.EXTEND_MARKDOWN.key, extendMarkdown, pluginName)
.registerOption(PLUGIN_OPTION_MAP.CHAIN_MARKDOWN.key, chainMarkdown, pluginName)
.registerOption(PLUGIN_OPTION_MAP.EXTEND_PAGE_DATA.key, extendPageData, pluginName)
Expand All @@ -231,6 +231,8 @@ module.exports = class PluginAPI {
.registerOption(PLUGIN_OPTION_MAP.DEFINE.key, define, pluginName)
.registerOption(PLUGIN_OPTION_MAP.ALIAS.key, alias, pluginName)
.registerOption(PLUGIN_OPTION_MAP.EXTEND_CLI.key, extendCli, pluginName)
.registerOption(PLUGIN_OPTION_MAP.BEFORE_DEV_SERVER.key, beforeDevServer, pluginName)
.registerOption(PLUGIN_OPTION_MAP.AFTER_DEV_SERVER.key, afterDevServer, pluginName)
}
}

Expand Down
2 changes: 2 additions & 0 deletions packages/@vuepress/core/lib/prepare/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
*/

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

/**
* Expose prepare.
*/

module.exports = async function prepare (sourceDir, cliOptions, isProd) {
logger.wait('Extracting site metadata...')
const appContext = AppContext.getInstance(sourceDir, cliOptions, isProd)
await appContext.process()
return appContext
Expand Down
4 changes: 4 additions & 0 deletions packages/@vuepress/core/lib/webpack/createClientConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ module.exports = function createClientConfig (ctx) {
mergeLonghand: false
}
}])
} else {
config
.plugin('hmr')
.use(require('webpack/lib/HotModuleReplacementPlugin'))
}

if (!env.isDebug) {
Expand Down
6 changes: 1 addition & 5 deletions packages/@vuepress/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,6 @@
"css-loader": "^0.28.11",
"file-loader": "^1.1.11",
"js-yaml": "^3.11.0",
"koa-connect": "^2.0.1",
"koa-mount": "^3.0.0",
"koa-range": "^0.3.0",
"koa-static": "^4.0.2",
"lru-cache": "^5.1.1",
"mini-css-extract-plugin": "0.4.4",
"optimize-css-assets-webpack-plugin": "^4.0.0",
Expand All @@ -65,7 +61,7 @@
"webpack": "^4.8.1",
"webpack-chain": "^4.6.0",
"webpack-merge": "^4.1.2",
"webpack-serve": "^1.0.2",
"webpack-dev-server": "^3.1.14",
"webpackbar": "^2.6.1"
},
"engines": {
Expand Down
40 changes: 17 additions & 23 deletions packages/docs/docs/plugin/option-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,42 +132,36 @@ module.exports = (options, context) => ({
})
```

## enhanceDevServer
## beforeDevServer

- Type: `Function`
- Default: undefined

Enhance the underlying [Koa](https://github.com/koajs/koa) app.
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:

``` js
```js
module.exports = {
enhanceDevServer (app) {
// ...
// ...
beforeDevServer(app, server) {
app.get('/path/to/your/custom', function(req, res) {
res.json({ custom: 'response' })
})
}
}
```

A simple plugin to create a sub public directory is as follows:
## afterDevServer

```js
const path = require('path')
- Type: `Function`
- Default: undefined

module.exports = (options, context) => {
const imagesAssetsPath = path.resolve(context.sourceDir, '.vuepress/images')
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:

return {
// For development
enhanceDevServer (app) {
const mount = require('koa-mount')
const serveStatic = require('koa-static')
app.use(mount(path.join(context.base, 'images'), serveStatic(imagesAssetsPath)))
},

// For production
async generated () {
const { fs } = require('@vuepress/shared-utils')
await fs.copy(imagesAssetsPath, path.resolve(context.outDir, 'images'))
}
```js
module.exports = {
// ...
afterDevServer(app, server) {
// hacking now ...
}
}
```
Expand Down
40 changes: 17 additions & 23 deletions packages/docs/docs/zh/plugin/option-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,42 +136,36 @@ module.exports = (options, context) => ({
})
```

## enhanceDevServer
## beforeDevServer

- 类型: `Function`
- 默认值: undefined

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

``` js
```js
module.exports = {
enhanceDevServer (app) {
// ...
// ...
beforeDevServer(app, server) {
app.get('/path/to/your/custom', function(req, res) {
res.json({ custom: 'response' })
})
}
}
```

一个简单的创建子 public 目录的插件如下:
## afterDevServer

```js
const path = require('path')
- 类型: `Function`
- 默认值: undefined

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

return {
// For development
enhanceDevServer (app) {
const mount = require('koa-mount')
const serveStatic = require('koa-static')
app.use(mount(path.join(context.base, 'images'), serveStatic(imagesAssetsPath)))
},

// For production
async generated () {
const { fs } = require('@vuepress/shared-utils')
await fs.copy(imagesAssetsPath, path.resolve(context.outDir, 'images'))
}
```js
module.exports = {
// ...
afterDevServer(app, server) {
// hacking now ...
}
}
```
Expand Down
File renamed without changes.
1 change: 1 addition & 0 deletions packages/vuepress/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('@vuepress/core')
4 changes: 2 additions & 2 deletions packages/vuepress/lib/handleUnknownCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ module.exports = async function (cli, options) {

if (sourceDir) {
context = await prepare(sourceDir, options)
context.pluginAPI.options.extendCli.apply(cli)
context.pluginAPI.options.extendCli.apply(cli, context)
}

logger.setOptions({ logLevel: 3 })
Expand Down Expand Up @@ -107,7 +107,7 @@ function registerUnknownCommands (cli, options) {
...options,
...commandoptions
}, false /* isProd */)
await context.pluginAPI.options.extendCli.apply(subCli)
await context.pluginAPI.options.extendCli.apply(subCli, context)
},
async afterParse (subCli) {
if (!subCli.matchedCommand) {
Expand Down
4 changes: 2 additions & 2 deletions packages/vuepress/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "vuepress",
"version": "1.0.0-alpha.32",
"description": "Minimalistic doc generator with Vue component based layout system",
"main": "vuepress.js",
"main": "index.js",

This comment has been minimized.

Copy link
@x8x

x8x Jan 29, 2019

Contributor

index.js shouldn't be cli.js ?

This comment has been minimized.

Copy link
@ulivz

ulivz Jan 29, 2019

Author Member

noop, index.js should export the Node.js API.

This comment has been minimized.

Copy link
@x8x

x8x Jan 30, 2019

Contributor

This used to work until this change ( yes, I'm running vuepress from a different folder ):
node ../vuepress/packages/vuepress build docs
after this change I have to use:
node ../vuepress/packages/vuepress/cli.js build docs
Forgive me if it is not meant to be used like this! :-)

This comment has been minimized.

Copy link
@ulivz

ulivz Jan 30, 2019

Author Member

Yes, for now you should use like this.

BTW, I recommend that you alias it or npm/yarn link it.

This comment has been minimized.

Copy link
@x8x

x8x Jan 30, 2019

Contributor

Alright, thank you @ulivz ! 👍

"repository": {
"type": "git",
"url": "git+https://github.com/vuejs/vuepress.git"
Expand All @@ -13,7 +13,7 @@
"generator"
],
"bin": {
"vuepress": "vuepress.js"
"vuepress": "cli.js"
},
"author": "Evan You",
"maintainers": [
Expand Down
Loading

0 comments on commit 81e3ef6

Please sign in to comment.