1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 | process.env.NODE_ENV = 'development';
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | require('dotenv').config({silent: true});
|
19 |
|
20 | var chalk = require('chalk');
|
21 | var webpack = require('webpack');
|
22 | var WebpackDevServer = require('webpack-dev-server');
|
23 | var historyApiFallback = require('connect-history-api-fallback');
|
24 | var httpProxyMiddleware = require('http-proxy-middleware');
|
25 | var detect = require('detect-port');
|
26 | var clearConsole = require('react-dev-utils/clearConsole');
|
27 | var checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
|
28 | var formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
|
29 | var getProcessForPort = require('react-dev-utils/getProcessForPort');
|
30 | var openBrowser = require('react-dev-utils/openBrowser');
|
31 | var prompt = require('react-dev-utils/prompt');
|
32 | var fs = require('fs');
|
33 | var config = require('../config/webpack.config.dev');
|
34 | var paths = require('../config/paths');
|
35 |
|
36 | var useYarn = fs.existsSync(paths.yarnLockFile);
|
37 | var cli = useYarn ? 'yarn' : 'npm';
|
38 | var isInteractive = process.stdout.isTTY;
|
39 |
|
40 |
|
41 | if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
|
42 | process.exit(1);
|
43 | }
|
44 |
|
45 |
|
46 | var DEFAULT_PORT = process.env.PORT || 3000;
|
47 | var compiler;
|
48 | var handleCompile;
|
49 |
|
50 |
|
51 |
|
52 | var isSmokeTest = process.argv.some(arg => arg.indexOf('--smoke-test') > -1);
|
53 | if (isSmokeTest) {
|
54 | handleCompile = function (err, stats) {
|
55 | if (err || stats.hasErrors() || stats.hasWarnings()) {
|
56 | process.exit(1);
|
57 | } else {
|
58 | process.exit(0);
|
59 | }
|
60 | };
|
61 | }
|
62 |
|
63 | function setupCompiler(host, port, protocol) {
|
64 |
|
65 |
|
66 | compiler = webpack(config, handleCompile);
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 | compiler.plugin('invalid', function() {
|
73 | if (isInteractive) {
|
74 | clearConsole();
|
75 | }
|
76 | console.log('Compiling...');
|
77 | });
|
78 |
|
79 | var isFirstCompile = true;
|
80 |
|
81 |
|
82 |
|
83 | compiler.plugin('done', function(stats) {
|
84 | if (isInteractive) {
|
85 | clearConsole();
|
86 | }
|
87 |
|
88 |
|
89 |
|
90 |
|
91 | var messages = formatWebpackMessages(stats.toJson({}, true));
|
92 | var isSuccessful = !messages.errors.length && !messages.warnings.length;
|
93 | var showInstructions = isSuccessful && (isInteractive || isFirstCompile);
|
94 |
|
95 | if (isSuccessful) {
|
96 | console.log(chalk.green('Compiled successfully!'));
|
97 | }
|
98 |
|
99 | if (showInstructions) {
|
100 | console.log();
|
101 | console.log('The app is running at:');
|
102 | console.log();
|
103 | console.log(' ' + chalk.cyan(protocol + '://' + host + ':' + port + '/'));
|
104 | console.log();
|
105 | console.log('Note that the development build is not optimized.');
|
106 | console.log('To create a production build, use ' + chalk.cyan(cli + ' run build') + '.');
|
107 | console.log();
|
108 | isFirstCompile = false;
|
109 | }
|
110 |
|
111 |
|
112 | if (messages.errors.length) {
|
113 | console.log(chalk.red('Failed to compile.'));
|
114 | console.log();
|
115 | messages.errors.forEach(message => {
|
116 | console.log(message);
|
117 | console.log();
|
118 | });
|
119 | return;
|
120 | }
|
121 |
|
122 |
|
123 | if (messages.warnings.length) {
|
124 | console.log(chalk.yellow('Compiled with warnings.'));
|
125 | console.log();
|
126 | messages.warnings.forEach(message => {
|
127 | console.log(message);
|
128 | console.log();
|
129 | });
|
130 |
|
131 | console.log('You may use special comments to disable some warnings.');
|
132 | console.log('Use ' + chalk.yellow('// eslint-disable-next-line') + ' to ignore the next line.');
|
133 | console.log('Use ' + chalk.yellow('/* eslint-disable */') + ' to ignore all warnings in a file.');
|
134 |
|
135 | console.log('Use ' + chalk.yellow('tslint:disable-line') + ' to disable this line.');
|
136 | console.log('Use ' + chalk.yellow('tslint:disable-next-line') + ' to ignore the rules on next line.');
|
137 | console.log('Use ' + chalk.yellow('tslint:disable ') + ' to disable linting for rest of file.');
|
138 | console.log('Use ' + chalk.yellow('tslint:enable ') + ' to enable linting for rest of file.');
|
139 | }
|
140 | });
|
141 | }
|
142 |
|
143 |
|
144 |
|
145 | function onProxyError(proxy) {
|
146 | return function(err, req, res){
|
147 | var host = req.headers && req.headers.host;
|
148 | console.log(
|
149 | chalk.red('Proxy error:') + ' Could not proxy request ' + chalk.cyan(req.url) +
|
150 | ' from ' + chalk.cyan(host) + ' to ' + chalk.cyan(proxy) + '.'
|
151 | );
|
152 | console.log(
|
153 | 'See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (' +
|
154 | chalk.cyan(err.code) + ').'
|
155 | );
|
156 | console.log();
|
157 |
|
158 |
|
159 |
|
160 | if (res.writeHead && !res.headersSent) {
|
161 | res.writeHead(500);
|
162 | }
|
163 | res.end('Proxy error: Could not proxy request ' + req.url + ' from ' +
|
164 | host + ' to ' + proxy + ' (' + err.code + ').'
|
165 | );
|
166 | }
|
167 | }
|
168 |
|
169 | function addMiddleware(devServer) {
|
170 |
|
171 |
|
172 | var proxy = require(paths.appPackageJson).proxy;
|
173 | devServer.use(historyApiFallback({
|
174 |
|
175 |
|
176 | disableDotRule: true,
|
177 |
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 | htmlAcceptHeaders: proxy ?
|
185 | ['text/html'] :
|
186 | ['text/html', '*/*']
|
187 | }));
|
188 | if (proxy) {
|
189 | if (typeof proxy !== 'string') {
|
190 | console.log(chalk.red('When specified, "proxy" in package.json must be a string.'));
|
191 | console.log(chalk.red('Instead, the type of "proxy" was "' + typeof proxy + '".'));
|
192 | console.log(chalk.red('Either remove "proxy" from package.json, or make it a string.'));
|
193 | process.exit(1);
|
194 | }
|
195 |
|
196 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 |
|
202 | var mayProxy = /^(?!\/(index\.html$|.*\.hot-update\.json$|sockjs-node\/)).*$/;
|
203 |
|
204 |
|
205 |
|
206 | var hpm = httpProxyMiddleware(pathname => mayProxy.test(pathname), {
|
207 | target: proxy,
|
208 | logLevel: 'silent',
|
209 | onProxyReq: function(proxyReq, req, res) {
|
210 |
|
211 |
|
212 |
|
213 | if (proxyReq.getHeader('origin')) {
|
214 | proxyReq.setHeader('origin', proxy);
|
215 | }
|
216 | },
|
217 | onError: onProxyError(proxy),
|
218 | secure: false,
|
219 | changeOrigin: true,
|
220 | ws: true
|
221 | });
|
222 | devServer.use(mayProxy, hpm);
|
223 |
|
224 |
|
225 |
|
226 |
|
227 | devServer.listeningApp.on('upgrade', hpm.upgrade);
|
228 | }
|
229 |
|
230 |
|
231 |
|
232 | devServer.use(devServer.middleware);
|
233 | }
|
234 |
|
235 | function runDevServer(host, port, protocol) {
|
236 | var devServer = new WebpackDevServer(compiler, {
|
237 |
|
238 | compress: true,
|
239 |
|
240 |
|
241 | clientLogLevel: 'none',
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 |
|
251 |
|
252 |
|
253 |
|
254 |
|
255 |
|
256 | contentBase: paths.appPublic,
|
257 |
|
258 |
|
259 |
|
260 |
|
261 |
|
262 | hot: true,
|
263 |
|
264 |
|
265 | publicPath: config.output.publicPath,
|
266 |
|
267 |
|
268 | quiet: true,
|
269 |
|
270 |
|
271 | watchOptions: {
|
272 | ignored: /node_modules/
|
273 | },
|
274 |
|
275 | https: protocol === "https",
|
276 | host: host
|
277 | });
|
278 |
|
279 |
|
280 | addMiddleware(devServer);
|
281 |
|
282 |
|
283 | devServer.listen(port, (err, result) => {
|
284 | if (err) {
|
285 | return console.log(err);
|
286 | }
|
287 |
|
288 | if (isInteractive) {
|
289 | clearConsole();
|
290 | }
|
291 | console.log(chalk.cyan('Starting the development server...'));
|
292 | console.log();
|
293 |
|
294 | openBrowser(protocol + '://' + host + ':' + port + '/');
|
295 | });
|
296 | }
|
297 |
|
298 | function run(port) {
|
299 | var protocol = process.env.HTTPS === 'true' ? "https" : "http";
|
300 | var host = process.env.HOST || 'localhost';
|
301 | setupCompiler(host, port, protocol);
|
302 | runDevServer(host, port, protocol);
|
303 | }
|
304 |
|
305 |
|
306 |
|
307 | detect(DEFAULT_PORT).then(port => {
|
308 | if (port === DEFAULT_PORT) {
|
309 | run(port);
|
310 | return;
|
311 | }
|
312 |
|
313 | if (isInteractive) {
|
314 | clearConsole();
|
315 | var existingProcess = getProcessForPort(DEFAULT_PORT);
|
316 | var question =
|
317 | chalk.yellow('Something is already running on port ' + DEFAULT_PORT + '.' +
|
318 | ((existingProcess) ? ' Probably:\n ' + existingProcess : '')) +
|
319 | '\n\nWould you like to run the app on another port instead?';
|
320 |
|
321 | prompt(question, true).then(shouldChangePort => {
|
322 | if (shouldChangePort) {
|
323 | run(port);
|
324 | }
|
325 | });
|
326 | } else {
|
327 | console.log(chalk.red('Something is already running on port ' + DEFAULT_PORT + '.'));
|
328 | }
|
329 | });
|