"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.run = run;

function _fs() {
  const data = _interopRequireDefault(require("fs"));

  _fs = function () {
    return data;
  };

  return data;
}

function _path() {
  const data = _interopRequireDefault(require("path"));

  _path = function () {
    return data;
  };

  return data;
}

function _url() {
  const data = _interopRequireDefault(require("url"));

  _url = function () {
    return data;
  };

  return data;
}

function _progress() {
  const data = _interopRequireDefault(require("progress"));

  _progress = function () {
    return data;
  };

  return data;
}

function _last() {
  const data = _interopRequireDefault(require("lodash/last"));

  _last = function () {
    return data;
  };

  return data;
}

function _compact() {
  const data = _interopRequireDefault(require("lodash/compact"));

  _compact = function () {
    return data;
  };

  return data;
}

function _findLastIndex() {
  const data = _interopRequireDefault(require("lodash/findLastIndex"));

  _findLastIndex = function () {
    return data;
  };

  return data;
}

function _boxen() {
  const data = _interopRequireDefault(require("boxen"));

  _boxen = function () {
    return data;
  };

  return data;
}

function _bunyan() {
  const data = _interopRequireDefault(require("@expo/bunyan"));

  _bunyan = function () {
    return data;
  };

  return data;
}

function _chalk() {
  const data = _interopRequireDefault(require("chalk"));

  _chalk = function () {
    return data;
  };

  return data;
}

function _ora() {
  const data = _interopRequireDefault(require("ora"));

  _ora = function () {
    return data;
  };

  return data;
}

function _simpleSpinner() {
  const data = _interopRequireDefault(require("@expo/simple-spinner"));

  _simpleSpinner = function () {
    return data;
  };

  return data;
}

function _getenv() {
  const data = _interopRequireDefault(require("getenv"));

  _getenv = function () {
    return data;
  };

  return data;
}

function _commander() {
  const data = _interopRequireWildcard(require("commander"));

  _commander = function () {
    return data;
  };

  return data;
}

function _xdl() {
  const data = require("@expo/xdl");

  _xdl = function () {
    return data;
  };

  return data;
}

function ConfigUtils() {
  const data = _interopRequireWildcard(require("@expo/config"));

  ConfigUtils = function () {
    return data;
  };

  return data;
}

function _accounts() {
  const data = require("./accounts");

  _accounts = function () {
    return data;
  };

  return data;
}

function _log() {
  const data = _interopRequireDefault(require("./log"));

  _log = function () {
    return data;
  };

  return data;
}

function _update() {
  const data = _interopRequireDefault(require("./update"));

  _update = function () {
    return data;
  };

  return data;
}

function _urlOpts() {
  const data = _interopRequireDefault(require("./urlOpts"));

  _urlOpts = function () {
    return data;
  };

  return data;
}

function _package() {
  const data = _interopRequireDefault(require("../package.json"));

  _package = function () {
    return data;
  };

  return data;
}

function _commands() {
  const data = require("./commands");

  _commands = function () {
    return data;
  };

  return data;
}

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

_xdl().Api.setClientName(_package().default.version);

_xdl().ApiV2.setClientName(_package().default.version); // The following prototyped functions are not used here, but within in each file found in `./commands`
// Extending commander to easily add more options to certain command line arguments


_commander().Command.prototype.urlOpts = function () {
  _urlOpts().default.addOptions(this);

  return this;
};

_commander().Command.prototype.allowOffline = function () {
  this.option('--offline', 'Allows this command to run while offline');
  return this;
};

_commander().default.on('--help', () => {
  (0, _log().default)(`To learn more about a specific command and its options use 'expo [command] --help'\n`);
});

// asyncAction is a wrapper for all commands/actions to be executed after commander is done
// parsing the command input
_commander().Command.prototype.asyncAction = function (asyncFn, skipUpdateCheck) {
  return this.action(async (...args) => {
    if (!skipUpdateCheck) {
      try {
        await checkCliVersionAsync();
      } catch (e) {}
    }

    try {
      let options = (0, _last().default)(args);

      if (options.output === 'raw') {
        _log().default.config.raw = true;
      }

      if (options.offline) {
        _xdl().Config.offline = true;
      }

      await asyncFn(...args); // After a command, flush the analytics queue so the program will not have any active timers
      // This allows node js to exit immediately

      _xdl().Analytics.flush();
    } catch (err) {
      // TODO: Find better ways to consolidate error messages
      if (err.isCommandError) {
        _log().default.error(err.message);
      } else if (err._isApiError) {
        _log().default.error(_chalk().default.red(err.message));
      } else if (err.isXDLError) {
        _log().default.error(err.message);
      } else {
        _log().default.error(err.message); // TODO: Is there a better way to do this? EXPO_DEBUG needs to be set to view the stack trace


        if (_getenv().default.boolish('EXPO_DEBUG', false)) {
          _log().default.error(_chalk().default.gray(err.stack));
        } else {
          _log().default.error(_chalk().default.grey('Set EXPO_DEBUG=true in your env to view the stack trace.'));
        }
      }

      process.exit(1);
    }
  });
}; // asyncActionProjectDir captures the projectDirectory from the command line,
// setting it to cwd if it is not provided.
// Commands such as `start` and `publish` use this.
// It does several things:
// - Everything in asyncAction
// - Checks if the user is logged in or out
// - Checks for updates
// - Attaches the bundling logger
// - Checks if the project directory is valid or not
// - Runs AsyncAction with the projectDir as an argument


_commander().Command.prototype.asyncActionProjectDir = function (asyncFn, skipProjectValidation, skipAuthCheck) {
  this.option('--config [file]', 'Specify a path to app.json');
  return this.asyncAction(async (projectDir, ...args) => {
    const opts = args[0];

    if (!projectDir) {
      projectDir = process.cwd();
    } else {
      projectDir = _path().default.resolve(process.cwd(), projectDir);
    }

    if (opts.config) {
      const pathToConfig = _path().default.resolve(process.cwd(), opts.config);

      if (!_fs().default.existsSync(pathToConfig)) {
        throw new Error(`File at provide config path does not exist: ${pathToConfig}`);
      }

      ConfigUtils().setCustomConfigPath(projectDir, pathToConfig);
    }

    const logLines = (msg, logFn) => {
      if (typeof msg === 'string') {
        for (let line of msg.split('\n')) {
          logFn(line);
        }
      } else {
        logFn(msg);
      }
    };

    const logStackTrace = (chunk, logFn, nestedLogFn) => {
      let traceInfo;

      try {
        traceInfo = JSON.parse(chunk.msg);
      } catch (e) {
        return logFn(chunk.msg);
      }

      let {
        message,
        stack
      } = traceInfo;

      _log().default.addNewLineIfNone();

      logFn(_chalk().default.bold(message));

      const isLibraryFrame = line => {
        return line.startsWith('node_modules');
      };

      const stackFrames = (0, _compact().default)(stack.split('\n'));
      let lastAppCodeFrameIndex = (0, _findLastIndex().default)(stackFrames, line => {
        return !isLibraryFrame(line);
      });
      let lastFrameIndexToLog = Math.min(stackFrames.length - 1, lastAppCodeFrameIndex + 2 // show max two more frames after last app code frame
      );
      let unloggedFrames = stackFrames.length - lastFrameIndexToLog; // If we're only going to exclude one frame, just log them all

      if (unloggedFrames === 1) {
        lastFrameIndexToLog = stackFrames.length - 1;
        unloggedFrames = 0;
      }

      for (let i = 0; i <= lastFrameIndexToLog; i++) {
        let line = stackFrames[i];

        if (!line) {
          continue;
        } else if (line.match(/react-native\/.*YellowBox.js/)) {
          continue;
        }

        if (line.startsWith('node_modules')) {
          nestedLogFn('- ' + line);
        } else {
          nestedLogFn('* ' + line);
        }
      }

      if (unloggedFrames > 0) {
        nestedLogFn(`- ... ${unloggedFrames} more stack frames from framework internals`);
      }

      _log().default.printNewLineBeforeNextLog();
    };

    const logWithLevel = chunk => {
      if (!chunk.msg) {
        return;
      }

      if (chunk.level <= _bunyan().default.INFO) {
        if (chunk.includesStack) {
          logStackTrace(chunk, _log().default, _log().default.nested);
        } else {
          logLines(chunk.msg, _log().default);
        }
      } else if (chunk.level === _bunyan().default.WARN) {
        if (chunk.includesStack) {
          logStackTrace(chunk, _log().default.warn, _log().default.nestedWarn);
        } else {
          logLines(chunk.msg, _log().default.warn);
        }
      } else {
        if (chunk.includesStack) {
          logStackTrace(chunk, _log().default.error, _log().default.nestedError);
        } else {
          logLines(chunk.msg, _log().default.error);
        }
      }
    };

    let bar; // eslint-disable-next-line no-new

    new (_xdl().PackagerLogsStream)({
      projectRoot: projectDir,
      onStartBuildBundle: () => {
        bar = new (_progress().default)('Building JavaScript bundle [:bar] :percent', {
          total: 100,
          clear: true,
          complete: '=',
          incomplete: ' '
        });

        _log().default.setBundleProgressBar(bar);
      },
      onProgressBuildBundle: percent => {
        if (!bar || bar.complete) return;
        let ticks = percent - bar.curr;
        ticks > 0 && bar.tick(ticks);
      },
      onFinishBuildBundle: (err, startTime, endTime) => {
        if (bar && !bar.complete) {
          bar.tick(100 - bar.curr);
        }

        if (bar) {
          _log().default.setBundleProgressBar(null);

          bar = null;

          if (err) {
            (0, _log().default)(_chalk().default.red('Failed building JavaScript bundle.'));
          } else {
            (0, _log().default)(_chalk().default.green(`Finished building JavaScript bundle in ${endTime.getTime() - startTime.getTime()}ms.`));
          }
        }
      },
      updateLogs: updater => {
        let newLogChunks = updater([]);
        newLogChunks.forEach(newLogChunk => {
          if (newLogChunk.issueId && newLogChunk.issueCleared) {
            return;
          }

          logWithLevel(newLogChunk);
        });
      }
    }); // needed for validation logging to function

    _xdl().ProjectUtils.attachLoggerStream(projectDir, {
      stream: {
        write: chunk => {
          if (chunk.tag === 'device') {
            logWithLevel(chunk);
          }
        }
      },
      type: 'raw'
    }); // The existing CLI modules only pass one argument to this function, so skipProjectValidation
    // will be undefined in most cases. we can explicitly pass a truthy value here to avoid
    // validation (eg for init)
    //
    // If the packager/manifest server is running and healthy, there is no need
    // to rerun Doctor because the directory was already checked previously
    // This is relevant for command such as `send`


    if (!skipProjectValidation && (await _xdl().Project.currentStatus(projectDir)) !== 'running') {
      let spinner = (0, _ora().default)('Making sure project is set up correctly...').start();

      _log().default.setSpinner(spinner); // validate that this is a good projectDir before we try anything else


      let status = await _xdl().Doctor.validateLowLatencyAsync(projectDir);

      if (status === _xdl().Doctor.FATAL) {
        throw new Error(`There is an error with your project. See above logs for information.`);
      }

      spinner.stop();

      _log().default.setSpinner(null);
    } // the existing CLI modules only pass one argument to this function, so skipProjectValidation
    // will be undefined in most cases. we can explicitly pass a truthy value here to avoid validation (eg for init)


    return asyncFn(projectDir, ...args);
  });
};

function runAsync(programName) {
  try {
    // Setup analytics
    _xdl().Analytics.setSegmentNodeKey('vGu92cdmVaggGA26s3lBX6Y5fILm8SQ7');

    _xdl().Analytics.setVersionName(_package().default.version);

    _registerLogs();

    _xdl().UserManager.setInteractiveAuthenticationCallback(_accounts().loginOrRegisterIfLoggedOut);

    if (process.env.SERVER_URL) {
      let serverUrl = process.env.SERVER_URL;

      if (!serverUrl.startsWith('http')) {
        serverUrl = `http://${serverUrl}`;
      }

      let parsedUrl = _url().default.parse(serverUrl);

      const port = parseInt(parsedUrl.port || '');

      if (parsedUrl.hostname && port) {
        _xdl().Config.api.host = parsedUrl.hostname;
        _xdl().Config.api.port = port;
      } else {
        throw new Error('Environment variable SERVER_URL is not a valid url');
      }
    }

    _xdl().Config.developerTool = _package().default.name; // Setup our commander instance

    _commander().default.name(programName);

    _commander().default.version(_package().default.version).option('-o, --output [format]', 'Output format. pretty (default), raw').option('--non-interactive', 'Fail, if an interactive prompt would be required to continue. Enabled by default if stdin is not a TTY.'); // Load each module found in ./commands by 'registering' it with our commander instance


    (0, _commands().registerCommands)(_commander().default);
    let subCommand = process.argv[2];
    let argv = process.argv.filter(arg => {
      // Remove deprecated `--github` option here in order to fallback to password login/signup.
      if (subCommand === 'login' && arg === '--github') {
        _log().default.nestedWarn('GitHub login is not currently available.\nPlease log in with your Expo account.');

        return false;
      }

      if (subCommand === 'register' && arg === '--github') {
        _log().default.nestedWarn('GitHub sign up is not currently available.');

        return false;
      }

      return true;
    });

    _commander().default.parse(argv);

    if (typeof _commander().default.nonInteractive === 'undefined') {
      // Commander doesn't initialize boolean args with default values.
      _commander().default.nonInteractive = !process.stdin.isTTY;
    } // Display a message if the user does not input a valid command


    if (subCommand) {
      let commands = [];

      _commander().default.commands.forEach(command => {
        commands.push(command['_name']);
        let alias = command['_alias'];

        if (alias) {
          commands.push(alias);
        }
      });

      if (!commands.includes(subCommand)) {
        _log().default.warn(`"${subCommand}" is not an ${programName} command. See "${programName} --help" for the full list of commands.`);

        if (subCommand === 'detach') {
          (0, _log().default)('To eject your project to ExpoKit (previously "detach"), use `expo eject`.');
        }
      }
    } else {
      _commander().default.help();
    }
  } catch (e) {
    console.error(e);
    throw e;
  }
}

async function checkCliVersionAsync() {
  let {
    updateIsAvailable,
    current,
    latest,
    deprecated
  } = await _update().default.checkForUpdateAsync();

  if (updateIsAvailable) {
    _log().default.nestedWarn((0, _boxen().default)(_chalk().default.green(`There is a new version of ${_package().default.name} available (${latest}).
You are currently using ${_package().default.name} ${current}
Install expo-cli globally using the package manager of your choice; for example: \`npm install -g ${_package().default.name}\` to get the latest version`), {
      borderColor: 'green',
      padding: 1
    }));
  }

  if (deprecated) {
    _log().default.nestedWarn((0, _boxen().default)(_chalk().default.red(`This version of expo-cli is not supported anymore. 
It's highly recommended to update to the newest version.

The API endpoints used in this version of expo-cli might not exist,
any interaction with Expo servers may result in unexpected behaviour.`), {
      borderColor: 'red',
      padding: 1
    }));
  }
}

function _registerLogs() {
  let stream = {
    stream: {
      write: chunk => {
        if (chunk.code) {
          switch (chunk.code) {
            case _xdl().NotificationCode.START_LOADING:
              _simpleSpinner().default.start();

              return;

            case _xdl().NotificationCode.STOP_LOADING:
              _simpleSpinner().default.stop();

              return;

            case _xdl().NotificationCode.DOWNLOAD_CLI_PROGRESS:
              return;
          }
        }

        if (chunk.level === _bunyan().default.INFO) {
          (0, _log().default)(chunk.msg);
        } else if (chunk.level === _bunyan().default.WARN) {
          _log().default.warn(chunk.msg);
        } else if (chunk.level >= _bunyan().default.ERROR) {
          _log().default.error(chunk.msg);
        }
      }
    },
    type: 'raw'
  };

  _xdl().Logger.notifications.addStream(stream);

  _xdl().Logger.global.addStream(stream);
}

async function writePathAsync() {
  let subCommand = process.argv[2];

  if (subCommand === 'prepare-detached-build') {
    // This is being run from Android Studio or Xcode. Don't want to write PATH in this case.
    return;
  }

  await _xdl().Binaries.writePathToUserSettingsAsync();
} // This is the entry point of the CLI


function run(programName) {
  (async function () {
    await Promise.all([writePathAsync(), runAsync(programName)]);
  })().catch(e => {
    console.error('Uncaught Error', e);
    process.exit(1);
  });
}
//# sourceMappingURL=__sourcemaps__/exp.js.map