zoro-cli
Version:
https://github.com/vuejs/vue-cli
261 lines (231 loc) • 7.64 kB
JavaScript
const url = require('url')
const path = require('path')
const fs = require('fs-extra')
const { importFrom } = require('zoro-cli-util/import')
const chalk = require('chalk')
const webpack = require('webpack')
const WebpackDevServer = require('webpack-dev-server')
const portfinder = require('portfinder')
const args = require('minimist')(process.argv.slice(2))
const Configurator = require('./Configurator.js')
const prepareURLs = require('./util/prepareURLs')
const prepareProxy = require('./util/prepareProxy')
const envUtil = require('./env')
const { writePortSync } = require('./port')
// const launchEditorMiddleware = require('launch-editor-middleware')
// const { openBrowser } = require('@vue/cli-shared-utils');
console.info('Starting development server...')
// 设置默认的 NODE_ENV
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = 'development'
}
const isProduction = envUtil.isProduction()
const defaults = {
mode: 'development',
host: '0.0.0.0',
port: 9000,
https: false,
}
// resolve webpack config
const configurator = new Configurator({ args })
const options = configurator.projectOptions
const webpackConfig = configurator.resolveWebpackConfig()
// load user devServer options with higher priority than devServer
// in webpck config
const projectDevServerOptions = Object.assign(
webpackConfig.devServer || {},
options.devServer,
)
// resolve server options
const useHttps = args.https || projectDevServerOptions.https || defaults.https
const protocol = useHttps ? 'https' : 'http'
const host = args.host || projectDevServerOptions.host || defaults.host
portfinder.basePort = args.port || projectDevServerOptions.port || defaults.port
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)
}
}
function forEachPlugin(category, cb) {
const pluginDir = path.join(__dirname, `plugin/${category}`)
if (fs.existsSync(pluginDir)) {
const idToPlugin = (dir, id) => ({
id: id.replace(/^\.\//, 'built-in:'),
dir,
apply: importFrom(path.join(dir, id), './index'),
})
fs.readdirSync(pluginDir)
.filter(name => name.charAt(0) !== '.')
.map(name => idToPlugin(pluginDir, name))
.forEach(({ apply }) => {
if (apply) {
cb(apply)
}
})
}
}
function applyServePlugins(obj) {
forEachPlugin('serve', apply => {
apply({ addDevClientToEntry, ...obj })
})
}
function applyServeDonePlugins(obj) {
forEachPlugin('serve-done', apply => {
apply({ options, ...obj })
})
}
portfinder
.getPortPromise()
.then(port => {
writePortSync(port)
// do not remove .port, anyproxy need it (it has been added to .gitignore)
// process.on('exit', () => fs.removeSync(portFile))
const rawPublicUrl = args.public || projectDevServerOptions.public
let publicUrl
if (rawPublicUrl) {
publicUrl = /^[a-zA-Z]+:\/\//.test(rawPublicUrl)
? rawPublicUrl
: `${protocol}://${rawPublicUrl}`
}
const urls = prepareURLs(protocol, host, port, options.baseUrl)
const proxySettings = prepareProxy(
projectDevServerOptions.proxy,
path.resolve('public'),
)
// inject dev & hot-reload middleware entries
if (!isProduction) {
const sockjsUrl = publicUrl
? // explicitly configured via devServer.public
`?${publicUrl}/sockjs-node`
: '?' +
url.format({
protocol,
port,
hostname: urls.lanUrlForConfig || 'localhost',
pathname: '/sockjs-node',
})
// console.log(sockjsUrl)
const devClients = [
// dev server client
require.resolve('webpack-dev-server/client') + sockjsUrl,
]
if (!args.nohmr) {
devClients.push(
// hmr client
require.resolve(
projectDevServerOptions.hotOnly
? 'webpack/hot/only-dev-server'
: 'webpack/hot/dev-server',
),
)
}
// inject dev/hot client
addDevClientToEntry(webpackConfig, devClients)
// load plugins
applyServePlugins({ webpackConfig })
}
// inspect webpackConfig
if (args.inspect || configurator.projectOptions.inspect) {
require('./util/inspectWebpackConfig')(webpackConfig)
}
// create compiler
const compiler = webpack(webpackConfig)
// create server
const server = new WebpackDevServer(compiler, {
clientLogLevel: 'none',
historyApiFallback: {
disableDotRule: true,
rewrites: [
{
from: /./,
to: path.posix.join(options.baseUrl, 'index.html'),
},
],
},
contentBase: path.resolve('public'),
watchContentBase: !isProduction,
hot: !isProduction && !args.nohmr,
quiet: true,
compress: isProduction,
publicPath: options.baseUrl,
overlay: isProduction // TODO disable this
? false
: { warnings: false, errors: true },
disableHostCheck: true,
...projectDevServerOptions,
https: useHttps,
// https://stackoverflow.com/questions/10175812/how-to-create-a-self-signed-certificate-with-openssl
key: fs.readFileSync(path.resolve(__dirname, './server.key')),
cert: fs.readFileSync(path.resolve(__dirname, './server.crt')),
proxy: proxySettings,
before(app) {
// allow other plugins to register middlewares, e.g. PWA
// api.service.devServerConfigFns.forEach(fn => fn(app));
// apply in project middlewares
projectDevServerOptions.before && projectDevServerOptions.before(app)
},
})
;['SIGINT', 'SIGTERM'].forEach(signal => {
process.on(signal, () => {
server.close(() => {
process.exit(0)
})
})
})
// log instructions & open browser on first compilation complete
let isFirstCompile = true
compiler.hooks.done.tap('cli serve', stats => {
if (stats.hasErrors()) {
return
}
let copied = ''
if (isFirstCompile && args.copy) {
require('clipboardy').write(urls.localUrlForBrowser)
copied = chalk.dim('(copied to clipboard)')
}
console.log()
console.log(
[
' App running at:',
` - Local: ${chalk.cyan(urls.localUrlForTerminal)} ${copied}`,
` - Network: ${chalk.cyan(urls.lanUrlForTerminal)}`,
].join('\n'),
)
applyServeDonePlugins({ port, https: useHttps, stats })
if (isFirstCompile) {
isFirstCompile = false
if (!isProduction) {
const buildCommand = 'npm run build'
console.log()
console.log(' Note that the development build is not optimized.')
console.log(
` To create a production build, run ${chalk.cyan(buildCommand)}.`,
)
} else {
console.log()
console.log(' App is served in production mode.')
console.log(' Note this is for preview or E2E testing only.')
}
if (args.open || projectDevServerOptions.open) {
// openBrowser(urls.localUrlForBrowser);
}
}
})
server.listen(port, host, err => {
if (err) {
console.error(err)
throw err
}
})
})
.catch(err => {
console.error(err)
throw err
})