dev_server.js
3.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
'use strict';
const path = require('path');
const fs = require('mz/fs');
const spawn = require('cross-spawn');
const Base = require('sdk-base');
const sleep = require('mz-modules/sleep');
const awaitEvent = require('await-event');
const debug = require('debug')('egg-view-assets:dev_server');
const detectPort = require('detect-port');
const mkdirp = require('mz-modules/mkdirp');
const is = require('is-type-of');
class DevServer extends Base {
constructor(app) {
super({
initMethod: 'init',
});
this.app = app;
this.isClosed = false;
}
async init() {
const { devServer } = this.app.config.assets;
if (devServer.autoPort) {
devServer.port = await detectPort(devServer.port || 10000);
await mkdirp(path.dirname(devServer.portPath));
await fs.writeFile(devServer.portPath, devServer.port.toString());
} else {
// check whether the port is using
if (await this.checkPortExist()) {
throw new Error(`port ${this.app.config.assets.devServer.port} has been used`);
}
}
// start dev server asynchronously
this.startAsync();
await this.waitListen();
}
startAsync() {
const { devServer } = this.app.config.assets;
devServer.command = this.replacePort(devServer.command);
const [ command, ...args ] = devServer.command.split(/\s+/);
const env = Object.assign({}, process.env, devServer.env);
// env.PATH = `${this.app.config.baseDir}/node_modules/.bin:${env.PATH}`;
// replace {port}
Object.keys(env).forEach(key => {
env[key] = this.replacePort(env[key]);
});
const opt = {
// disable stdout by default
stdio: [ 'inherit', 'ignore', 'inherit' ],
env,
shell: process.platform === 'win32'
};
if (devServer.cwd) opt.cwd = devServer.cwd;
if (devServer.debug) opt.stdio[1] = 'inherit';
const proc = this.proc = spawn(command, args, opt);
proc.once('error', err => this.exit(err));
proc.once('exit', code => this.exit(code));
}
async checkPortExist() {
const { devServer } = this.app.config.assets;
const port = await detectPort(devServer.port);
debug('check %s, get result %s', devServer.port, port);
return port !== devServer.port;
}
async waitListen() {
const logger = this.app.coreLogger;
const { devServer } = this.app.config.assets;
let timeout = devServer.timeout / 1000;
let isSuccess = false;
while (timeout > 0) {
/* istanbul ignore if */
if (this.isClosed) {
logger.warn('[egg-view-assets] Closing, but devServer is not listened');
return;
}
if (await this.checkPortExist()) {
logger.warn('[egg-view-assets] Run "%s" success, listen on %s', devServer.command, devServer.port);
// 成功启动
isSuccess = true;
break;
}
timeout--;
await sleep(1000);
debug('waiting, %s remain', timeout);
}
if (isSuccess) return;
const err = new Error(`Run "${devServer.command}" failed after ${devServer.timeout / 1000}s`);
throw err;
}
async close() {
this.isClosed = true;
/* istanbul ignore if */
if (!this.proc) return;
this.app.coreLogger.warn('[egg-view-assets] dev server will be killed');
this.proc.kill();
await awaitEvent(this.proc, 'exit');
this.proc = null;
}
exit(codeOrError) {
const logger = this.app.coreLogger;
this.proc = null;
if (!(codeOrError instanceof Error)) {
const { devServer } = this.app.config.assets;
const code = codeOrError;
const message = `[egg-view-assets] Run "${devServer.command}" exit with code ${code}`;
if (!code || code === 0) {
logger.info(message);
return;
}
codeOrError = new Error(message);
}
logger.error(codeOrError);
}
replacePort(str) {
if (!is.string(str)) return str;
return str.replace('{port}', this.app.config.assets.devServer.port);
}
}
module.exports = DevServer;