text: /*
   * Copyright (c) 2020 International Business Machines
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   */
  
  const fs = require("fs");
  const path = require("path");
  const url = require("url");
  
  const address = require("address");
  const chalk = require("chalk");
  const detect = require("detect-port-alt");
  const inquirer = require("inquirer");
  const isRoot = require("is-root");
  // const clearConsole = require("react-dev-utils/clearConsole");
  const forkTsCheckerWebpackPlugin = require("react-dev-utils/ForkTsCheckerWebpackPlugin");
  const formatWebpackMessages = require("react-dev-utils/formatWebpackMessages");
  const getProcessForPort = require("react-dev-utils/getProcessForPort");
  const typescriptFormatter = require("react-dev-utils/typescriptFormatter");
  
  const isInteractive = process.stdout.isTTY;
  
  function prepareUrls(protocol, host, port, pathname = "/") {
    const formatUrl = (hostname) =>
      url.format({
        protocol,
        hostname,
        port,
        pathname,
      });
    const prettyPrintUrl = (hostname) =>
      url.format({
        protocol,
        hostname,
        port: chalk.bold(port),
        pathname,
      });
  
    const isUnspecifiedHost = host === "0.0.0.0" || host === "::";
    let prettyHost, lanUrlForConfig, lanUrlForTerminal;
    if (isUnspecifiedHost) {
      prettyHost = "localhost";
      try {
        // This can only return an IPv4 address
        lanUrlForConfig = address.ip();
        if (lanUrlForConfig) {
          // Check if the address is a private ip
          // https://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces
          if (
            /^10[.]|^172[.](1[6-9]|2[0-9]|3[0-1])[.]|^192[.]168[.]/.test(
              lanUrlForConfig
            )
          ) {
            // Address is private, format it for later use
            lanUrlForTerminal = prettyPrintUrl(lanUrlForConfig);
          } else {
            // Address is not private, so we will discard it
            lanUrlForConfig = undefined;
          }
        }
      } catch (_e) {
        // ignored
      }
    } else {
      prettyHost = host;
    }
    const localUrlForTerminal = prettyPrintUrl(prettyHost);
    const localUrlForBrowser = formatUrl(prettyHost);
    return {
      lanUrlForConfig,
      lanUrlForTerminal,
      localUrlForTerminal,
      localUrlForBrowser,
    };
  }
  
  function printInstructions(appName, urls, useYarn) {
    console.log();
    console.log(`You can now view ${chalk.bold(appName)} in the browser.`);
    console.log();
  
    if (urls.lanUrlForTerminal) {
      console.log(
        `  ${chalk.bold("Local:")}            ${urls.localUrlForTerminal}`
      );
      console.log(
        `  ${chalk.bold("On Your Network:")}  ${urls.lanUrlForTerminal}`
      );
    } else {
      console.log(`  ${urls.localUrlForTerminal}`);
    }
  
    console.log();
    console.log("Note that the development build is not optimized.");
    console.log(
      `To create a production build, use ` +
        `${chalk.cyan(`${useYarn ? "yarn" : "npm run"} build`)}.`
    );
    console.log();
  }
  
  function createCompiler({
    appName,
    config,
    devSocket,
    urls,
    useYarn,
    useTypeScript,
    tscCompileOnError,
    webpack,
  }) {
    // "Compiler" is a low-level interface to webpack.
    // It lets us listen to some events and provide our own custom messages.
    let compiler;
    try {
      compiler = webpack(config);
    } catch (err) {
      console.log(chalk.red("Failed to compile."));
      console.log();
      console.log(err.message || err);
      console.log();
      process.exit(1);
    }
  
    // "invalid" event fires when you have changed a file, and webpack is
    // recompiling a bundle. WebpackDevServer takes care to pause serving the
    // bundle, so if you refresh, it'll wait instead of serving the old one.
    // "invalid" is short for "bundle invalidated", it doesn't imply any errors.
    compiler.hooks.invalid.tap("invalid", () => {
      if (isInteractive) {
        // clearConsole();
      }
      console.log("Compiling...");
    });
  
    let isFirstCompile = true;
    let tsMessagesPromise;
    let tsMessagesResolver;
  
    if (useTypeScript) {
      compiler.hooks.beforeCompile.tap("beforeCompile", () => {
        tsMessagesPromise = new Promise((resolve) => {
          tsMessagesResolver = (msgs) => resolve(msgs);
        });
      });
  
      forkTsCheckerWebpackPlugin
        .getCompilerHooks(compiler)
        .receive.tap("afterTypeScriptCheck", (diagnostics, lints) => {
          const allMsgs = [...diagnostics, ...lints];
          const format = (message) =>
            `${message.file}\n${typescriptFormatter(message, true)}`;
  
          tsMessagesResolver({
            errors: allMsgs.filter((msg) => msg.severity === "error").map(format),
            warnings: allMsgs
              .filter((msg) => msg.severity === "warning")
              .map(format),
          });
        });
    }
  
    // "done" event fires when webpack has finished recompiling the bundle.
    // Whether or not you have warnings or errors, you will get this event.
    compiler.hooks.done.tap("done", async (stats) => {
      if (isInteractive) {
        // clearConsole();
      }
  
      // We have switched off the default webpack output in WebpackDevServer
      // options so we are going to "massage" the warnings and errors and present
      // them in a readable focused way.
      // We only construct the warnings and errors for speed:
      // https://github.com/facebook/create-react-app/issues/4492#issuecomment-421959548
      const statsData = stats.toJson({
        all: false,
        warnings: true,
        errors: true,
      });
  
      // webpack 5 fix.
      statsData.errors = statsData.errors.map((w) => w.message);
      statsData.warnings = statsData.warnings.map((w) => w.message);
  
      if (useTypeScript && statsData.errors.length === 0) {
        const delayedMsg = setTimeout(() => {
          console.log(
            chalk.yellow(
              "Files successfully emitted, waiting for typecheck results..."
            )
          );
        }, 100);
  
        const messages = await tsMessagesPromise;
        clearTimeout(delayedMsg);
        if (tscCompileOnError) {
          statsData.warnings.push(...messages.errors);
        } else {
          statsData.errors.push(...messages.errors);
        }
        statsData.warnings.push(...messages.warnings);
  
        // Push errors and warnings into compilation result
        // to show them after page refresh triggered by user.
        if (tscCompileOnError) {
          stats.compilation.warnings.push(...messages.errors);
        } else {
          stats.compilation.errors.push(...messages.errors);
        }
        stats.compilation.warnings.push(...messages.warnings);
  
        if (messages.errors.length > 0) {
          if (tscCompileOnError) {
            devSocket.warnings(messages.errors);
          } else {
            devSocket.errors(messages.errors);
          }
        } else if (messages.warnings.length > 0) {
          devSocket.warnings(messages.warnings);
        }
  
        if (isInteractive) {
          // clearConsole();
        }
      }
  
      const messages = formatWebpackMessages(statsData);
      const isSuccessful = !messages.errors.length && !messages.warnings.length;
      if (isSuccessful) {
        console.log(chalk.green("Compiled successfully!"));
      }
      if (isSuccessful && (isInteractive || isFirstCompile)) {
        printInstructions(appName, urls, useYarn);
      }
      isFirstCompile = false;
  
      // If errors exist, only show errors.
      if (messages.errors.length) {
        // Only keep the first error. Others are often indicative
        // of the same problem, but confuse the reader with noise.
        if (messages.errors.length > 1) {
          messages.errors.length = 1;
        }
        console.log(chalk.red("Failed to compile.\n"));
        console.log(messages.errors.join("\n\n"));
        return;
      }
  
      // Show warnings if no errors were found.
      if (messages.warnings.length) {
        console.log(chalk.yellow("Compiled with warnings.\n"));
        console.log(messages.warnings.join("\n\n"));
  
        // Teach some ESLint tricks.
        console.log(
          "\nSearch for the " +
            chalk.underline(chalk.yellow("keywords")) +
            " to learn more about each warning."
        );
        console.log(
          "To ignore, add " +
            chalk.cyan("// eslint-disable-next-line") +
            " to the line before.\n"
        );
      }
    });
  
    // You can safely remove this after ejecting.
    // We only use this block for testing of Create React App itself:
    const isSmokeTest = process.argv.some(
      (arg) => arg.indexOf("--smoke-test") > -1
    );
    if (isSmokeTest) {
      compiler.hooks.failed.tap("smokeTest", async () => {
        await tsMessagesPromise;
        process.exit(1);
      });
      compiler.hooks.done.tap("smokeTest", async (stats) => {
        await tsMessagesPromise;
        if (stats.hasErrors() || stats.hasWarnings()) {
          process.exit(1);
        } else {
          process.exit(0);
        }
      });
    }
  
    return compiler;
  }
  
  function resolveLoopback(proxy) {
    const o = url.parse(proxy);
    o.host = undefined;
    if (o.hostname !== "localhost") {
      return proxy;
    }
    // Unfortunately, many languages (unlike node) do not yet support IPv6.
    // This means even though localhost resolves to ::1, the application
    // must fall back to IPv4 (on 127.0.0.1).
    // We can re-enable this in a few years.
    /*try {
      o.hostname = address.ipv6() ? '::1' : '127.0.0.1';
    } catch (_ignored) {
      o.hostname = '127.0.0.1';
    }*/
  
    try {
      // Check if we're on a network; if we are, chances are we can resolve
      // localhost. Otherwise, we can just be safe and assume localhost is
      // IPv4 for maximum compatibility.
      if (!address.ip()) {
        o.hostname = "127.0.0.1";
      }
    } catch (_ignored) {
      o.hostname = "127.0.0.1";
    }
    return url.format(o);
  }
  
  // We need to provide a custom onError function for httpProxyMiddleware.
  // It allows us to log custom error messages on the console.
  function onProxyError(proxy) {
    return (err, req, res) => {
      const host = req.headers && req.headers.host;
      console.log(
        chalk.red("Proxy error:") +
          " Could not proxy request " +
          chalk.cyan(req.url) +
          " from " +
          chalk.cyan(host) +
          " to " +
          chalk.cyan(proxy) +
          "."
      );
      console.log(
        "See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (" +
          chalk.cyan(err.code) +
          ")."
      );
      console.log();
  
      // And immediately send the proper error response to the client.
      // Otherwise, the request will eventually timeout with ERR_EMPTY_RESPONSE on the client side.
      if (res.writeHead && !res.headersSent) {
        res.writeHead(500);
      }
      res.end(
        "Proxy error: Could not proxy request " +
          req.url +
          " from " +
          host +
          " to " +
          proxy +
          " (" +
          err.code +
          ")."
      );
    };
  }
  
  function prepareProxy(proxy, appPublicFolder, servedPathname) {
    // `proxy` lets you specify alternate servers for specific requests.
    if (!proxy) {
      return undefined;
    }
    if (typeof proxy !== "string") {
      console.log(
        chalk.red('When specified, "proxy" in package.json must be a string.')
      );
      console.log(
        chalk.red('Instead, the type of "proxy" was "' + typeof proxy + '".')
      );
      console.log(
        chalk.red('Either remove "proxy" from package.json, or make it a string.')
      );
      process.exit(1);
    }
  
    // If proxy is specified, let it handle any request except for
    // files in the public folder and requests to the WebpackDevServer socket endpoint.
    // https://github.com/facebook/create-react-app/issues/6720
    const sockPath = process.env.WDS_SOCKET_PATH || "/sockjs-node";
    const isDefaultSockHost = !process.env.WDS_SOCKET_HOST;
    function mayProxy(pathname) {
      const maybePublicPath = path.resolve(
        appPublicFolder,
        pathname.replace(new RegExp("^" + servedPathname), "")
      );
      const isPublicFileRequest = fs.existsSync(maybePublicPath);
      // used by webpackHotDevClient
      const isWdsEndpointRequest =
        isDefaultSockHost && pathname.startsWith(sockPath);
      return !(isPublicFileRequest || isWdsEndpointRequest);
    }
  
    if (!/^http(s)?:\/\//.test(proxy)) {
      console.log(
        chalk.red(
          'When "proxy" is specified in package.json it must start with either http:// or https://'
        )
      );
      process.exit(1);
    }
  
    let target;
    if (process.platform === "win32") {
      target = resolveLoopback(proxy);
    } else {
      target = proxy;
    }
    return [
      {
        target,
        logLevel: "silent",
        // For single page apps, we generally want to fallback to /index.html.
        // However we also want to respect `proxy` for API calls.
        // So if `proxy` is specified as a string, we need to decide which fallback to use.
        // We use a heuristic: We want to proxy all the requests that are not meant
        // for static assets and as all the requests for static assets will be using
        // `GET` method, we can proxy all non-`GET` requests.
        // For `GET` requests, if request `accept`s text/html, we pick /index.html.
        // Modern browsers include text/html into `accept` header when navigating.
        // However API calls like `fetch()` won’t generally accept text/html.
        // If this heuristic doesn’t work well for you, use `src/setupProxy.js`.
        context: function (pathname, req) {
          return (
            req.method !== "GET" ||
            (mayProxy(pathname) &&
              req.headers.accept &&
              req.headers.accept.indexOf("text/html") === -1)
          );
        },
        onProxyReq: (proxyReq) => {
          // Browsers may send Origin headers even with same-origin
          // requests. To prevent CORS issues, we have to change
          // the Origin to match the target URL.
          if (proxyReq.getHeader("origin")) {
            proxyReq.setHeader("origin", target);
          }
        },
        onError: onProxyError(target),
        secure: false,
        changeOrigin: true,
        ws: true,
        xfwd: true,
      },
    ];
  }
  
  function choosePort(host, defaultPort) {
    return detect(defaultPort, host).then(
      (port) =>
        new Promise((resolve) => {
          if (port === defaultPort) {
            return resolve(port);
          }
          const message =
            process.platform !== "win32" && defaultPort < 1024 && !isRoot()
              ? `Admin permissions are required to run a server on a port below 1024.`
              : `Something is already running on port ${defaultPort}.`;
          if (isInteractive) {
            // clearConsole();
            const existingProcess = getProcessForPort(defaultPort);
            const question = {
              type: "confirm",
              name: "shouldChangePort",
              message:
                chalk.yellow(
                  message +
                    `${existingProcess ? ` Probably:\n  ${existingProcess}` : ""}`
                ) + "\n\nWould you like to run the app on another port instead?",
              default: true,
            };
            inquirer.prompt(question).then((answer) => {
              if (answer.shouldChangePort) {
                resolve(port);
              } else {
                resolve(null);
              }
            });
          } else {
            console.log(chalk.red(message));
            resolve(null);
          }
        }),
      (err) => {
        throw new Error(
          chalk.red(`Could not find an open port at ${chalk.bold(host)}.`) +
            "\n" +
            ("Network error message: " + err.message || err) +
            "\n"
        );
      }
    );
  }
  
  module.exports = {
    choosePort,
    createCompiler,
    prepareProxy,
    prepareUrls,
  };
  