1 | 'use strict';
|
2 |
|
3 | const { debug, print, strong } = require('./cnsl');
|
4 | const { fork } = require('child_process');
|
5 | const chalk = require('chalk');
|
6 | const merge = require('lodash/merge');
|
7 | const path = require('path');
|
8 | const portscanner = require('portscanner');
|
9 | const series = require('async/series');
|
10 |
|
11 | let appServer = {};
|
12 | let checkingAppServerPort = false;
|
13 | let isReloading = false;
|
14 | let server, reloadServer;
|
15 |
|
16 | module.exports = {
|
17 | |
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 | start(serve, reload, options, fn) {
|
33 | const { ReloadServerFactory, ServerFactory } = require(options.buddyServerPath);
|
34 | const tasks = [];
|
35 |
|
36 | if (reload) {
|
37 | tasks.push(done => {
|
38 | reloadServer = ReloadServerFactory();
|
39 | reloadServer.on('error', err => {
|
40 | if (!err.message.includes('ECONNRESET')) throw err;
|
41 | });
|
42 | print(`live-reloading on port: ${reloadServer.port}`, 0);
|
43 | reloadServer.start(err => {
|
44 | if (err) {
|
45 |
|
46 | }
|
47 | isReloading = true;
|
48 | done();
|
49 | });
|
50 | });
|
51 | }
|
52 |
|
53 | if (serve) {
|
54 | const filepath = path.relative(process.cwd(), options.file || options.directory) || path.dirname(process.cwd());
|
55 |
|
56 | tasks.push(done => {
|
57 | const file = options.file ? path.resolve(options.file) : '';
|
58 | const started = err => {
|
59 | if (err) return fn(err);
|
60 | done();
|
61 | };
|
62 |
|
63 | print(`serving ${strong(filepath)} at ${chalk.underline('http://localhost:' + options.port)}\n`, 0);
|
64 |
|
65 |
|
66 | if (file) {
|
67 | const env = merge({}, process.env, options.env);
|
68 |
|
69 | env.PORT = options.port;
|
70 | appServer = {
|
71 | file,
|
72 | port: options.port,
|
73 | options: {
|
74 | cwd: process.cwd(),
|
75 | env,
|
76 | execArgv: options.flags ? Array.isArray(options.flags) ? options.flags : [options.flags] : []
|
77 | }
|
78 | };
|
79 | startAppServer(started);
|
80 |
|
81 |
|
82 | } else {
|
83 | server = ServerFactory(options.directory, options.port, options.headers, options.extraDirectories);
|
84 | server.start(started);
|
85 | }
|
86 | });
|
87 | }
|
88 |
|
89 | series(tasks, fn);
|
90 | },
|
91 |
|
92 | |
93 |
|
94 |
|
95 |
|
96 | restart(fn) {
|
97 | if (server) {
|
98 | checkingAppServerPort = false;
|
99 | server.removeAllListeners();
|
100 | server.on('close', () => {
|
101 | server = null;
|
102 | startAppServer(fn);
|
103 | });
|
104 | server.kill('SIGKILL');
|
105 | } else {
|
106 | startAppServer(fn);
|
107 | }
|
108 | },
|
109 |
|
110 | |
111 |
|
112 |
|
113 |
|
114 | refresh(file) {
|
115 | let msg;
|
116 |
|
117 | if (isReloading && reloadServer) {
|
118 | msg = {
|
119 | command: 'reload',
|
120 | path: file,
|
121 | liveCSS: true
|
122 | };
|
123 | debug(`sending ${strong('reload')} command to live-reload clients`, 4);
|
124 |
|
125 | reloadServer.activeConnections().forEach(connection => {
|
126 | connection.send(msg);
|
127 | });
|
128 | }
|
129 | },
|
130 |
|
131 | |
132 |
|
133 |
|
134 | stop() {
|
135 | try {
|
136 | if (server) server[appServer ? 'kill' : 'close']();
|
137 | if (reloadServer) reloadServer.close();
|
138 | } catch (err) {
|
139 |
|
140 | }
|
141 | server = null;
|
142 | reloadServer = null;
|
143 | isReloading = false;
|
144 | }
|
145 | };
|
146 |
|
147 |
|
148 |
|
149 |
|
150 |
|
151 | function startAppServer(fn) {
|
152 | if (!checkingAppServerPort) {
|
153 | server = fork(appServer.file, [], appServer.options);
|
154 | server.on('exit', code => {
|
155 | checkingAppServerPort = false;
|
156 | server.removeAllListeners();
|
157 | server = null;
|
158 | fn(Error('failed to start server'));
|
159 | });
|
160 |
|
161 | checkingAppServerPort = true;
|
162 | waitForPortOpen(appServer.port, fn);
|
163 | }
|
164 | }
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 | function waitForPortOpen(port, fn) {
|
172 | function check() {
|
173 | if (checkingAppServerPort) {
|
174 | portscanner.checkPortStatus(port, '127.0.0.1', (err, status) => {
|
175 | if (err) {
|
176 |
|
177 | }
|
178 | if (!status || status !== 'open') {
|
179 | setTimeout(check, 20);
|
180 | } else {
|
181 | checkingAppServerPort = false;
|
182 | fn();
|
183 | }
|
184 | });
|
185 | }
|
186 | }
|
187 |
|
188 | check();
|
189 | }
|