Files
Barthélémy LedouxandULIVZ 2348e75fb1 fix(dev): Prevent files at node_modules from being watched (close: #855) (#856)
When running vuepress dev, chorkidar is set to observe all
addition/removal of md files inside the entity folder.

`node_modules` is one of these folders and can contain tons of
subfolders. It mostly never contains any md files anyway, and
even rarely has any hot additions of md files.

Another perk of this perf fix is this: If for some reason a developer
decided to publish a folder with a weird name in its module. It will not
prevent `vuepress dev` from loading.
2018-09-20 13:44:59 +08:00

150 lines
4.2 KiB
JavaScript

module.exports = async function dev (sourceDir, cliOptions = {}) {
const fs = require('fs')
const path = require('path')
const chalk = require('chalk')
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')
const logger = require('./util/logger')
const HeadPlugin = require('./webpack/HeadPlugin')
const DevLogPlugin = require('./webpack/DevLogPlugin')
const createClientConfig = require('./webpack/createClientConfig')
const { applyUserWebpackConfig } = require('./util')
const { frontmatterEmitter } = require('./webpack/markdownLoader')
logger.wait('\nExtracting site metadata...')
const options = await prepare(sourceDir)
// setup watchers to update options and dynamically generated files
const update = () => {
prepare(sourceDir).catch(err => {
console.error(logger.error(chalk.red(err.stack), false))
})
}
// watch add/remove of files
const pagesWatcher = chokidar.watch([
'**/*.md',
'.vuepress/components/**/*.vue'
], {
cwd: sourceDir,
ignored: ['.vuepress/**/*.md', 'node_modules'],
ignoreInitial: true
})
pagesWatcher.on('add', update)
pagesWatcher.on('unlink', update)
pagesWatcher.on('addDir', update)
pagesWatcher.on('unlinkDir', update)
// watch config file
const configWatcher = chokidar.watch([
'.vuepress/config.js',
'.vuepress/config.yml',
'.vuepress/config.toml'
], {
cwd: sourceDir,
ignoreInitial: true
})
configWatcher.on('change', update)
// also listen for frontmatter changes from markdown files
frontmatterEmitter.on('update', update)
// resolve webpack config
let config = createClientConfig(options, cliOptions)
config
.plugin('html')
// using a fork of html-webpack-plugin to avoid it requiring webpack
// internals from an incompatible version.
.use(require('vuepress-html-webpack-plugin'), [{
template: path.resolve(__dirname, 'app/index.dev.html')
}])
config
.plugin('site-data')
.use(HeadPlugin, [{
tags: options.siteConfig.head || []
}])
const port = await resolvePort(cliOptions.port || options.siteConfig.port)
const { host, displayHost } = await resolveHost(cliOptions.host || options.siteConfig.host)
config
.plugin('vuepress-log')
.use(DevLogPlugin, [{
port,
displayHost,
publicPath: options.publicPath
}])
config = config.toConfig()
const userConfig = options.siteConfig.configureWebpack
if (userConfig) {
config = applyUserWebpackConfig(userConfig, config, false /* isServer */)
}
const compiler = webpack(config)
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,
host,
dev: { logLevel: 'warn' },
hot: {
port: port + 1,
logLevel: 'error'
},
logLevel: 'error',
port,
add: 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(options.publicPath, serveStatic(userPublic)))
}
app.use(convert(history({
rewrites: [
{ from: /\.html$/, to: '/' }
]
})))
}
})
}
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'
host = host || defaultHost
const displayHost = host === defaultHost && process.platform !== 'win32'
? 'localhost'
: host
return {
displayHost,
host
}
}
async function resolvePort (port) {
const portfinder = require('portfinder')
portfinder.basePort = parseInt(port) || 8080
port = await portfinder.getPortPromise()
return port
}