UNPKG

3.83 kBJavaScriptView Raw
1'use strict'
2
3process.env.BABEL_ENV = 'development'
4process.env.NODE_ENV = 'development'
5
6process.on('unhandledRejection', err => {
7 throw err
8})
9
10const config = require('../config')
11const { getFreePort } = require('../libs/utils')
12const getEntry = require('../libs/entry')
13const maraConf = require(config.paths.marauder)
14const clearConsole = require('react-dev-utils/clearConsole')
15
16// 是否为交互模式
17const isInteractive = process.stdout.isTTY
18
19const webpack = require('webpack')
20const getWebpackConfig = require('../webpack/webpack.dev.conf')
21const prehandleConfig = require('../libs/prehandleConfig')
22const progressHandler = require('../libs/buildProgress')
23const DEFAULT_PORT = parseInt(process.env.PORT, 10) || config.dev.port
24const PROTOCOL = maraConf.https === true ? 'https' : 'http'
25
26// Define HTTP proxies to your custom API backend
27// https://github.com/chimurai/http-proxy-middleware
28const proxyTable = config.dev.proxyTable
29
30function getCompiler(webpackConf, { entry, port } = {}) {
31 const openBrowser = require('react-dev-utils/openBrowser')
32 const hostUri = getServerHostUri(webpackConf.devServer.host, port)
33 let isFirstCompile = true
34
35 // 为每一个入口文件添加 webpack-dev-server 客户端
36 addHotDevClient(webpackConf.entry)
37
38 const compiler = webpack(webpackConf)
39
40 compiler.apply(
41 new webpack.ProgressPlugin((...args) => {
42 if (isFirstCompile) progressHandler.apply(null, args)
43 })
44 )
45
46 compiler.plugin('after-emit', (compilation, callback) => {
47 if (isFirstCompile) {
48 // 交互模式下清除 console
49 isInteractive && clearConsole()
50 }
51 callback()
52 })
53
54 compiler.plugin('done', stats => {
55 const messages = stats.toJson({}, true)
56
57 // If errors exist, only show errors.
58 if (messages.errors.length) return
59
60 if (isFirstCompile) {
61 console.log(`> Listening at ${hostUri}\n`)
62 openBrowser(getServerURL(hostUri, entry))
63 isFirstCompile = false
64 }
65 })
66
67 return compiler
68}
69
70function addHotDevClient(entryConf) {
71 Object.keys(entryConf).forEach(entry => {
72 // client 在业务模块之前引入,以捕获初始化错误
73 entryConf[entry] = [
74 require.resolve('react-dev-utils/webpackHotDevClient')
75 ].concat(entryConf[entry])
76 })
77}
78
79function createDevServer(webpackConf, opt) {
80 const DevServer = require('webpack-dev-server')
81 const serverConf = webpackConf.devServer
82 const compiler = getCompiler(webpackConf, opt)
83
84 serverConf.https = PROTOCOL === 'https'
85 // 安全原因,一般禁用 HostCheck
86 // https://github.com/webpack/webpack-dev-server/issues/887
87 serverConf.disableHostCheck = !proxyTable
88
89 return new DevServer(compiler, serverConf)
90}
91
92function getServerHostUri(host, port) {
93 return `${PROTOCOL}://${host || 'localhost'}:${port}`
94}
95
96function getServerURL(hostUri, entry) {
97 let publicDevPath = config.dev.assetsPublicPath
98
99 // 以绝对路径 / 开头时,加入 url 中在浏览器打开
100 // 以非 / 开头时,回退为 /,避免浏览器路径错乱
101 publicDevPath = publicDevPath.startsWith('/') ? publicDevPath : '/'
102
103 return `${hostUri + publicDevPath + entry}.html`
104}
105
106async function server(entryInput) {
107 console.log('> Starting development server...')
108
109 const webpackConf = prehandleConfig('dev', getWebpackConfig(entryInput))
110 const port = await getFreePort(DEFAULT_PORT)
111 const devServer = createDevServer(webpackConf, {
112 entry: entryInput.entry,
113 port
114 })
115 // Ctrl + C 触发
116 ;['SIGINT', 'SIGTERM'].forEach(sig => {
117 process.on(sig, () => {
118 devServer.close()
119 process.exit()
120 })
121 })
122
123 // 指定 listen host 0.0.0.0 允许来自 ip 或 localhost 的访问
124 return devServer.listen(port, '0.0.0.0', err => {
125 if (err) return console.log(err)
126 })
127}
128
129module.exports = args => {
130 return getEntry(args).then(server)
131}