UNPKG

4.11 kBJavaScriptView Raw
1exports.name = 'builtin:serve'
2
3exports.apply = api => {
4 api.hook('createCLI', ({ command, args }) => {
5 command.option('-s, --serve', 'Serve assets on a local server')
6
7 if (!args.has('s') && !args.has('serve')) return
8
9 command
10 .option('-p, --port <port>', 'Server port', { default: '4000' })
11 .option('--host <host>', 'Server host', { default: '0.0.0.0' })
12 .option('--proxy <url>', 'Proxy API requests')
13 .option('--no-hot', 'Disable hot reloading')
14 .option('-o, --open', 'Open in browser')
15
16 command.action(async () => {
17 const devServer = Object.assign({}, api.config.devServer)
18 delete devServer.hotEntries
19
20 const { host, port: _port, open } = devServer
21 const port = await require('get-port')({ port: _port })
22
23 const webpackConfig = api.createWebpackChain().toConfig()
24
25 // No need to print URLs in test mode
26 if (api.mode !== 'test') {
27 webpackConfig.plugins.push({
28 apply(compiler) {
29 // TODO: figure out why using .tap() can't catch error
30 compiler.hooks.done.tap('print-serve-urls', stats => {
31 if (stats.hasErrors() || stats.hasWarnings()) return
32
33 require('@poi/dev-utils/printServeMessage')({
34 host,
35 port,
36 open
37 })
38 })
39 }
40 })
41 }
42
43 const compiler = api.createWebpackCompiler(webpackConfig)
44
45 const devServerOptions = Object.assign(
46 {
47 quiet: true,
48 historyApiFallback: true,
49 overlay: true,
50 disableHostCheck: true,
51 publicPath: webpackConfig.output.publicPath,
52 contentBase:
53 api.config.publicFolder && api.resolveCwd(api.config.publicFolder),
54 watchContentBase: true,
55 stats: {
56 colors: true
57 }
58 },
59 devServer,
60 {
61 proxy:
62 typeof devServer.proxy === 'string'
63 ? require('@poi/dev-utils/prepareProxy')(
64 devServer.proxy,
65 api.resolveCwd(api.config.publicFolder),
66 api.cli.options.debug
67 )
68 : devServer.proxy
69 }
70 )
71
72 const existingBefore = devServerOptions.before
73 devServerOptions.before = server => {
74 server.use(
75 require('@poi/dev-utils/launchEditorEndpoint'),
76 require('launch-editor-middleware')(() =>
77 console.log(
78 `To specify an editor, sepcify the EDITOR env variable or ` +
79 `add "editor" field to your Vue project config.\n`
80 )
81 )
82 )
83 server.use(require('@poi/dev-utils/skipServiceWorker')())
84 existingBefore && existingBefore(server)
85 }
86
87 const exitingAfter = devServerOptions.after
88 devServerOptions.after = server => {
89 exitingAfter && exitingAfter(server)
90 api.hooks.invoke('onCreateServer', server)
91 }
92
93 const WebpackDevServer = require('webpack-dev-server')
94 const server = new WebpackDevServer(compiler, devServerOptions)
95
96 server.listen(port, host)
97 })
98 })
99
100 api.hook('createWebpackChain', config => {
101 if (!api.cli.options.serve) return
102
103 config.devtool('cheap-module-eval-source-map')
104
105 const { hotEntries, hot } = api.config.devServer
106
107 if (hot) {
108 for (const entry of hotEntries) {
109 if (config.entryPoints.has(entry)) {
110 config.entry(entry).prepend('#webpack-hot-client')
111 }
112 }
113
114 const { HotModuleReplacementPlugin } = require('webpack')
115 HotModuleReplacementPlugin.__expression = `require('webpack').HotModuleReplacementPlugin`
116
117 config.plugin('hot').use(HotModuleReplacementPlugin)
118 }
119
120 // Don't show bundled files in --serve
121 if (config.plugins.has('print-status')) {
122 config.plugin('print-status').tap(([options]) => [
123 Object.assign(options, {
124 printFileStats: false
125 })
126 ])
127 }
128
129 // Don't copy public folder, since we serve it instead
130 config.plugins.delete('copy-public-folder')
131 })
132}