No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 

403 líneas
12 KiB

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