source: frontend/node_modules/react-dev-utils/WebpackDevServerUtils.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 13.1 KB
Line 
1/**
2 * Copyright (c) 2015-present, Facebook, Inc.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7'use strict';
8
9const address = require('address');
10const fs = require('fs');
11const path = require('path');
12const url = require('url');
13const chalk = require('chalk');
14const detect = require('detect-port-alt');
15const isRoot = require('is-root');
16const prompts = require('prompts');
17const clearConsole = require('./clearConsole');
18const formatWebpackMessages = require('./formatWebpackMessages');
19const getProcessForPort = require('./getProcessForPort');
20const forkTsCheckerWebpackPlugin = require('./ForkTsCheckerWebpackPlugin');
21
22const isInteractive = process.stdout.isTTY;
23
24function prepareUrls(protocol, host, port, pathname = '/') {
25 const formatUrl = hostname =>
26 url.format({
27 protocol,
28 hostname,
29 port,
30 pathname,
31 });
32 const prettyPrintUrl = hostname =>
33 url.format({
34 protocol,
35 hostname,
36 port: chalk.bold(port),
37 pathname,
38 });
39
40 const isUnspecifiedHost = host === '0.0.0.0' || host === '::';
41 let prettyHost, lanUrlForConfig, lanUrlForTerminal;
42 if (isUnspecifiedHost) {
43 prettyHost = 'localhost';
44 try {
45 // This can only return an IPv4 address
46 lanUrlForConfig = address.ip();
47 if (lanUrlForConfig) {
48 // Check if the address is a private ip
49 // https://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces
50 if (
51 /^10[.]|^172[.](1[6-9]|2[0-9]|3[0-1])[.]|^192[.]168[.]/.test(
52 lanUrlForConfig
53 )
54 ) {
55 // Address is private, format it for later use
56 lanUrlForTerminal = prettyPrintUrl(lanUrlForConfig);
57 } else {
58 // Address is not private, so we will discard it
59 lanUrlForConfig = undefined;
60 }
61 }
62 } catch (_e) {
63 // ignored
64 }
65 } else {
66 prettyHost = host;
67 }
68 const localUrlForTerminal = prettyPrintUrl(prettyHost);
69 const localUrlForBrowser = formatUrl(prettyHost);
70 return {
71 lanUrlForConfig,
72 lanUrlForTerminal,
73 localUrlForTerminal,
74 localUrlForBrowser,
75 };
76}
77
78function printInstructions(appName, urls, useYarn) {
79 console.log();
80 console.log(`You can now view ${chalk.bold(appName)} in the browser.`);
81 console.log();
82
83 if (urls.lanUrlForTerminal) {
84 console.log(
85 ` ${chalk.bold('Local:')} ${urls.localUrlForTerminal}`
86 );
87 console.log(
88 ` ${chalk.bold('On Your Network:')} ${urls.lanUrlForTerminal}`
89 );
90 } else {
91 console.log(` ${urls.localUrlForTerminal}`);
92 }
93
94 console.log();
95 console.log('Note that the development build is not optimized.');
96 console.log(
97 `To create a production build, use ` +
98 `${chalk.cyan(`${useYarn ? 'yarn' : 'npm run'} build`)}.`
99 );
100 console.log();
101}
102
103function createCompiler({
104 appName,
105 config,
106 urls,
107 useYarn,
108 useTypeScript,
109 webpack,
110}) {
111 // "Compiler" is a low-level interface to webpack.
112 // It lets us listen to some events and provide our own custom messages.
113 let compiler;
114 try {
115 compiler = webpack(config);
116 } catch (err) {
117 console.log(chalk.red('Failed to compile.'));
118 console.log();
119 console.log(err.message || err);
120 console.log();
121 process.exit(1);
122 }
123
124 // "invalid" event fires when you have changed a file, and webpack is
125 // recompiling a bundle. WebpackDevServer takes care to pause serving the
126 // bundle, so if you refresh, it'll wait instead of serving the old one.
127 // "invalid" is short for "bundle invalidated", it doesn't imply any errors.
128 compiler.hooks.invalid.tap('invalid', () => {
129 if (isInteractive) {
130 clearConsole();
131 }
132 console.log('Compiling...');
133 });
134
135 let isFirstCompile = true;
136 let tsMessagesPromise;
137
138 if (useTypeScript) {
139 forkTsCheckerWebpackPlugin
140 .getCompilerHooks(compiler)
141 .waiting.tap('awaitingTypeScriptCheck', () => {
142 console.log(
143 chalk.yellow(
144 'Files successfully emitted, waiting for typecheck results...'
145 )
146 );
147 });
148 }
149
150 // "done" event fires when webpack has finished recompiling the bundle.
151 // Whether or not you have warnings or errors, you will get this event.
152 compiler.hooks.done.tap('done', async stats => {
153 if (isInteractive) {
154 clearConsole();
155 }
156
157 // We have switched off the default webpack output in WebpackDevServer
158 // options so we are going to "massage" the warnings and errors and present
159 // them in a readable focused way.
160 // We only construct the warnings and errors for speed:
161 // https://github.com/facebook/create-react-app/issues/4492#issuecomment-421959548
162 const statsData = stats.toJson({
163 all: false,
164 warnings: true,
165 errors: true,
166 });
167
168 const messages = formatWebpackMessages(statsData);
169 const isSuccessful = !messages.errors.length && !messages.warnings.length;
170 if (isSuccessful) {
171 console.log(chalk.green('Compiled successfully!'));
172 }
173 if (isSuccessful && (isInteractive || isFirstCompile)) {
174 printInstructions(appName, urls, useYarn);
175 }
176 isFirstCompile = false;
177
178 // If errors exist, only show errors.
179 if (messages.errors.length) {
180 // Only keep the first error. Others are often indicative
181 // of the same problem, but confuse the reader with noise.
182 if (messages.errors.length > 1) {
183 messages.errors.length = 1;
184 }
185 console.log(chalk.red('Failed to compile.\n'));
186 console.log(messages.errors.join('\n\n'));
187 return;
188 }
189
190 // Show warnings if no errors were found.
191 if (messages.warnings.length) {
192 console.log(chalk.yellow('Compiled with warnings.\n'));
193 console.log(messages.warnings.join('\n\n'));
194
195 // Teach some ESLint tricks.
196 console.log(
197 '\nSearch for the ' +
198 chalk.underline(chalk.yellow('keywords')) +
199 ' to learn more about each warning.'
200 );
201 console.log(
202 'To ignore, add ' +
203 chalk.cyan('// eslint-disable-next-line') +
204 ' to the line before.\n'
205 );
206 }
207 });
208
209 // You can safely remove this after ejecting.
210 // We only use this block for testing of Create React App itself:
211 const isSmokeTest = process.argv.some(
212 arg => arg.indexOf('--smoke-test') > -1
213 );
214 if (isSmokeTest) {
215 compiler.hooks.failed.tap('smokeTest', async () => {
216 await tsMessagesPromise;
217 process.exit(1);
218 });
219 compiler.hooks.done.tap('smokeTest', async stats => {
220 await tsMessagesPromise;
221 if (stats.hasErrors() || stats.hasWarnings()) {
222 process.exit(1);
223 } else {
224 process.exit(0);
225 }
226 });
227 }
228
229 return compiler;
230}
231
232function resolveLoopback(proxy) {
233 const o = url.parse(proxy);
234 o.host = undefined;
235 if (o.hostname !== 'localhost') {
236 return proxy;
237 }
238 // Unfortunately, many languages (unlike node) do not yet support IPv6.
239 // This means even though localhost resolves to ::1, the application
240 // must fall back to IPv4 (on 127.0.0.1).
241 // We can re-enable this in a few years.
242 /*try {
243 o.hostname = address.ipv6() ? '::1' : '127.0.0.1';
244 } catch (_ignored) {
245 o.hostname = '127.0.0.1';
246 }*/
247
248 try {
249 // Check if we're on a network; if we are, chances are we can resolve
250 // localhost. Otherwise, we can just be safe and assume localhost is
251 // IPv4 for maximum compatibility.
252 if (!address.ip()) {
253 o.hostname = '127.0.0.1';
254 }
255 } catch (_ignored) {
256 o.hostname = '127.0.0.1';
257 }
258 return url.format(o);
259}
260
261// We need to provide a custom onError function for httpProxyMiddleware.
262// It allows us to log custom error messages on the console.
263function onProxyError(proxy) {
264 return (err, req, res) => {
265 const host = req.headers && req.headers.host;
266 console.log(
267 chalk.red('Proxy error:') +
268 ' Could not proxy request ' +
269 chalk.cyan(req.url) +
270 ' from ' +
271 chalk.cyan(host) +
272 ' to ' +
273 chalk.cyan(proxy) +
274 '.'
275 );
276 console.log(
277 'See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (' +
278 chalk.cyan(err.code) +
279 ').'
280 );
281 console.log();
282
283 // And immediately send the proper error response to the client.
284 // Otherwise, the request will eventually timeout with ERR_EMPTY_RESPONSE on the client side.
285 if (res.writeHead && !res.headersSent) {
286 res.writeHead(500);
287 }
288 res.end(
289 'Proxy error: Could not proxy request ' +
290 req.url +
291 ' from ' +
292 host +
293 ' to ' +
294 proxy +
295 ' (' +
296 err.code +
297 ').'
298 );
299 };
300}
301
302function prepareProxy(proxy, appPublicFolder, servedPathname) {
303 // `proxy` lets you specify alternate servers for specific requests.
304 if (!proxy) {
305 return undefined;
306 }
307 if (typeof proxy !== 'string') {
308 console.log(
309 chalk.red('When specified, "proxy" in package.json must be a string.')
310 );
311 console.log(
312 chalk.red('Instead, the type of "proxy" was "' + typeof proxy + '".')
313 );
314 console.log(
315 chalk.red('Either remove "proxy" from package.json, or make it a string.')
316 );
317 process.exit(1);
318 }
319
320 // If proxy is specified, let it handle any request except for
321 // files in the public folder and requests to the WebpackDevServer socket endpoint.
322 // https://github.com/facebook/create-react-app/issues/6720
323 const sockPath = process.env.WDS_SOCKET_PATH || '/ws';
324 const isDefaultSockHost = !process.env.WDS_SOCKET_HOST;
325 function mayProxy(pathname) {
326 const maybePublicPath = path.resolve(
327 appPublicFolder,
328 pathname.replace(new RegExp('^' + servedPathname), '')
329 );
330 const isPublicFileRequest = fs.existsSync(maybePublicPath);
331 // used by webpackHotDevClient
332 const isWdsEndpointRequest =
333 isDefaultSockHost && pathname.startsWith(sockPath);
334 return !(isPublicFileRequest || isWdsEndpointRequest);
335 }
336
337 if (!/^http(s)?:\/\//.test(proxy)) {
338 console.log(
339 chalk.red(
340 'When "proxy" is specified in package.json it must start with either http:// or https://'
341 )
342 );
343 process.exit(1);
344 }
345
346 let target;
347 if (process.platform === 'win32') {
348 target = resolveLoopback(proxy);
349 } else {
350 target = proxy;
351 }
352 return [
353 {
354 target,
355 logLevel: 'silent',
356 // For single page apps, we generally want to fallback to /index.html.
357 // However we also want to respect `proxy` for API calls.
358 // So if `proxy` is specified as a string, we need to decide which fallback to use.
359 // We use a heuristic: We want to proxy all the requests that are not meant
360 // for static assets and as all the requests for static assets will be using
361 // `GET` method, we can proxy all non-`GET` requests.
362 // For `GET` requests, if request `accept`s text/html, we pick /index.html.
363 // Modern browsers include text/html into `accept` header when navigating.
364 // However API calls like `fetch()` won’t generally accept text/html.
365 // If this heuristic doesn’t work well for you, use `src/setupProxy.js`.
366 context: function (pathname, req) {
367 return (
368 req.method !== 'GET' ||
369 (mayProxy(pathname) &&
370 req.headers.accept &&
371 req.headers.accept.indexOf('text/html') === -1)
372 );
373 },
374 onProxyReq: proxyReq => {
375 // Browsers may send Origin headers even with same-origin
376 // requests. To prevent CORS issues, we have to change
377 // the Origin to match the target URL.
378 if (proxyReq.getHeader('origin')) {
379 proxyReq.setHeader('origin', target);
380 }
381 },
382 onError: onProxyError(target),
383 secure: false,
384 changeOrigin: true,
385 ws: true,
386 xfwd: true,
387 },
388 ];
389}
390
391function choosePort(host, defaultPort) {
392 return detect(defaultPort, host).then(
393 port =>
394 new Promise(resolve => {
395 if (port === defaultPort) {
396 return resolve(port);
397 }
398 const message =
399 process.platform !== 'win32' && defaultPort < 1024 && !isRoot()
400 ? `Admin permissions are required to run a server on a port below 1024.`
401 : `Something is already running on port ${defaultPort}.`;
402 if (isInteractive) {
403 clearConsole();
404 const existingProcess = getProcessForPort(defaultPort);
405 const question = {
406 type: 'confirm',
407 name: 'shouldChangePort',
408 message:
409 chalk.yellow(
410 message +
411 `${existingProcess ? ` Probably:\n ${existingProcess}` : ''}`
412 ) + '\n\nWould you like to run the app on another port instead?',
413 initial: true,
414 };
415 prompts(question).then(answer => {
416 if (answer.shouldChangePort) {
417 resolve(port);
418 } else {
419 resolve(null);
420 }
421 });
422 } else {
423 console.log(chalk.red(message));
424 resolve(null);
425 }
426 }),
427 err => {
428 throw new Error(
429 chalk.red(`Could not find an open port at ${chalk.bold(host)}.`) +
430 '\n' +
431 ('Network error message: ' + err.message || err) +
432 '\n'
433 );
434 }
435 );
436}
437
438module.exports = {
439 choosePort,
440 createCompiler,
441 prepareProxy,
442 prepareUrls,
443};
Note: See TracBrowser for help on using the repository browser.