You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
900 lines
32 KiB
900 lines
32 KiB
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.bootstrapAnalyticsAsync = bootstrapAnalyticsAsync;
|
|
exports.helpGroupOrder = void 0;
|
|
exports.run = run;
|
|
exports.trackUsage = trackUsage;
|
|
function _bunyan() {
|
|
const data = _interopRequireDefault(require("@expo/bunyan"));
|
|
_bunyan = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _config() {
|
|
const data = require("@expo/config");
|
|
_config = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _boxen() {
|
|
const data = _interopRequireDefault(require("boxen"));
|
|
_boxen = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _chalk() {
|
|
const data = _interopRequireDefault(require("chalk"));
|
|
_chalk = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _commander() {
|
|
const data = _interopRequireWildcard(require("commander"));
|
|
_commander = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _fs() {
|
|
const data = _interopRequireDefault(require("fs"));
|
|
_fs = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _getenv() {
|
|
const data = _interopRequireDefault(require("getenv"));
|
|
_getenv = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _leven() {
|
|
const data = _interopRequireDefault(require("leven"));
|
|
_leven = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _findLastIndex() {
|
|
const data = _interopRequireDefault(require("lodash/findLastIndex"));
|
|
_findLastIndex = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _path() {
|
|
const data = _interopRequireDefault(require("path"));
|
|
_path = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _progress() {
|
|
const data = _interopRequireDefault(require("progress"));
|
|
_progress = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _stripAnsi() {
|
|
const data = _interopRequireDefault(require("strip-ansi"));
|
|
_stripAnsi = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _url() {
|
|
const data = _interopRequireDefault(require("url"));
|
|
_url = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _wrapAnsi() {
|
|
const data = _interopRequireDefault(require("wrap-ansi"));
|
|
_wrapAnsi = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _xdl() {
|
|
const data = require("xdl");
|
|
_xdl = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _StatusEventEmitter() {
|
|
const data = _interopRequireDefault(require("./analytics/StatusEventEmitter"));
|
|
_StatusEventEmitter = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _commands() {
|
|
const data = require("./commands");
|
|
_commands = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _accounts() {
|
|
const data = require("./commands/auth/accounts");
|
|
_accounts = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _TerminalLink() {
|
|
const data = require("./commands/utils/TerminalLink");
|
|
_TerminalLink = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _profileMethod() {
|
|
const data = require("./commands/utils/profileMethod");
|
|
_profileMethod = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _urlOpts() {
|
|
const data = _interopRequireDefault(require("./commands/utils/urlOpts"));
|
|
_urlOpts = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _log() {
|
|
const data = _interopRequireDefault(require("./log"));
|
|
_log = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _handleErrors() {
|
|
const data = require("./utils/handleErrors");
|
|
_handleErrors = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _matchFileNameOrURLFromStackTrace() {
|
|
const data = require("./utils/matchFileNameOrURLFromStackTrace");
|
|
_matchFileNameOrURLFromStackTrace = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _ora() {
|
|
const data = require("./utils/ora");
|
|
_ora = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _update() {
|
|
const data = _interopRequireDefault(require("./utils/update"));
|
|
_update = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
// We use require() to exclude package.json from TypeScript's analysis since it lives outside the
|
|
// src directory and would change the directory structure of the emitted files under the build
|
|
// directory
|
|
const packageJSON = require('../package.json');
|
|
_xdl().ApiV2.setClientName(packageJSON.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;
|
|
};
|
|
|
|
// Add support for logical command groupings
|
|
_commander().Command.prototype.helpGroup = function (name) {
|
|
if (this.commands[this.commands.length - 1]) {
|
|
this.commands[this.commands.length - 1].__helpGroup = name;
|
|
} else {
|
|
this.parent.helpGroup(name);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
// A longer description that will be displayed then the command is used with --help
|
|
_commander().Command.prototype.longDescription = function (name) {
|
|
if (this.commands[this.commands.length - 1]) {
|
|
this.commands[this.commands.length - 1].__longDescription = name;
|
|
} else {
|
|
this.parent.longDescription(name);
|
|
}
|
|
return this;
|
|
};
|
|
function pad(str, width) {
|
|
// Pulled from commander for overriding
|
|
const len = Math.max(0, width - (0, _stripAnsi().default)(str).length);
|
|
return str + Array(len + 1).join(' ');
|
|
}
|
|
function humanReadableArgName(arg) {
|
|
// Pulled from commander for overriding
|
|
const nameOutput = arg.name + (arg.variadic === true ? '...' : '');
|
|
return arg.required ? `<${nameOutput}>` : `[${nameOutput}]`;
|
|
}
|
|
function breakSentence(input) {
|
|
// Break a sentence by the word after a max character count, adjusting for ansi characters
|
|
return (0, _wrapAnsi().default)(input, 72);
|
|
}
|
|
_commander().Command.prototype.prepareCommands = function () {
|
|
return this.commands.filter(function (cmd) {
|
|
// Display all commands with EXPO_DEBUG, otherwise use the noHelp option.
|
|
if (_getenv().default.boolish('EXPO_DEBUG', false)) {
|
|
return true;
|
|
}
|
|
return !['internal'].includes(cmd.__helpGroup);
|
|
}).map(function (cmd, i) {
|
|
var _cmd$__helpGroup;
|
|
const args = cmd._args.map(humanReadableArgName).join(' ');
|
|
const description = cmd._description;
|
|
// Remove alias. We still show this with --help on the command.
|
|
// const alias = cmd._alias;
|
|
// const nameWithAlias = cmd._name + (alias ? '|' + alias : '');
|
|
const nameWithAlias = cmd._name;
|
|
return [nameWithAlias + (
|
|
// Remove the redundant [options] string that's shown after every command.
|
|
// (cmd.options.length ? ' [options]' : '') +
|
|
args ? ' ' + args : ''), breakSentence(description), (_cmd$__helpGroup = cmd.__helpGroup) !== null && _cmd$__helpGroup !== void 0 ? _cmd$__helpGroup : 'misc'];
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Set / get the command usage `str`.
|
|
*
|
|
* @param {String} str
|
|
* @return {String|Command}
|
|
* @api public
|
|
*/
|
|
|
|
// @ts-ignore
|
|
_commander().Command.prototype.usage = function (str) {
|
|
const args = this._args.map(function (arg) {
|
|
return humanReadableArgName(arg);
|
|
});
|
|
const commandsStr = this.commands.length ? '[command]' : '';
|
|
const argsStr = this._args.length ? args.join(' ') : '';
|
|
let usage = commandsStr + argsStr;
|
|
if (usage.length) usage += ' ';
|
|
usage += '[options]';
|
|
if (arguments.length === 0) {
|
|
return this._usage || usage;
|
|
}
|
|
this._usage = str;
|
|
return this;
|
|
};
|
|
_commander().Command.prototype.helpInformation = function () {
|
|
var _this$__longDescripti;
|
|
let desc = [];
|
|
// Use the long description if available, otherwise use the regular description.
|
|
const description = (_this$__longDescripti = this.__longDescription) !== null && _this$__longDescripti !== void 0 ? _this$__longDescripti : this._description;
|
|
if (description) {
|
|
desc = [replaceAll(breakSentence(description), '\n', pad('\n', 3)), ''];
|
|
const argsDescription = this._argsDescription;
|
|
if (argsDescription && this._args.length) {
|
|
const width = this.padWidth();
|
|
desc.push('Arguments:');
|
|
desc.push('');
|
|
this._args.forEach(({
|
|
name
|
|
}) => {
|
|
desc.push(' ' + pad(name, width) + ' ' + argsDescription[name]);
|
|
});
|
|
desc.push('');
|
|
}
|
|
}
|
|
let cmdName = this._name;
|
|
if (this._alias) {
|
|
// Here is the only place we show the command alias
|
|
cmdName = `${cmdName}|${this._alias}`;
|
|
}
|
|
|
|
// Dim the options to keep things consistent.
|
|
const usage = `${_chalk().default.bold`Usage:`} ${cmdName} ${_chalk().default.dim(this.usage())}\n`;
|
|
const commandHelp = '' + this.commandHelp();
|
|
const options = [_chalk().default.bold`Options:`, '\n' + this.optionHelp().replace(/^/gm, ' '), ''];
|
|
|
|
// return ['', usage, ...desc, ...options, commandHelp].join('\n') + '\n';
|
|
return ['', usage, ...desc, ...options, commandHelp].join(pad('\n', 3)) + '\n';
|
|
};
|
|
function replaceAll(string, search, replace) {
|
|
return string.split(search).join(replace);
|
|
}
|
|
const helpGroupOrder = ['core', 'auth', 'client', 'info', 'eject', 'publish', 'build', 'credentials', 'notifications', 'url', 'webhooks', 'upload', 'internal', 'deprecated'];
|
|
exports.helpGroupOrder = helpGroupOrder;
|
|
function sortHelpGroups(helpGroups) {
|
|
const groupOrder = [...new Set([...helpGroupOrder,
|
|
// add any others and remove duplicates
|
|
...Object.keys(helpGroups)])];
|
|
const subGroupOrder = {
|
|
core: ['init', 'start', 'start:web', 'publish', 'export']
|
|
};
|
|
const sortSubGroupWithOrder = (groupName, group) => {
|
|
const order = subGroupOrder[groupName];
|
|
if (!(order !== null && order !== void 0 && order.length)) {
|
|
return group;
|
|
}
|
|
const sortedCommands = [];
|
|
while (order.length) {
|
|
const key = order.shift();
|
|
const index = group.findIndex(item => item[0].startsWith(key));
|
|
if (index >= 0) {
|
|
const [item] = group.splice(index, 1);
|
|
sortedCommands.push(item);
|
|
}
|
|
}
|
|
return sortedCommands.concat(group);
|
|
};
|
|
|
|
// Reverse the groups
|
|
const sortedGroups = {};
|
|
while (groupOrder.length) {
|
|
const group = groupOrder.shift();
|
|
if (group in helpGroups) {
|
|
sortedGroups[group] = helpGroups[group];
|
|
}
|
|
}
|
|
return Object.keys(sortedGroups).reduce((prev, curr) => ({
|
|
...prev,
|
|
// Sort subgroups that have a defined order
|
|
[curr]: sortSubGroupWithOrder(curr, helpGroups[curr])
|
|
}), {});
|
|
}
|
|
|
|
// Extended the help renderer to add a custom format and groupings.
|
|
_commander().Command.prototype.commandHelp = function () {
|
|
if (!this.commands.length) {
|
|
return '';
|
|
}
|
|
const width = this.padWidth();
|
|
const commands = this.prepareCommands();
|
|
const helpGroups = {};
|
|
|
|
// Sort commands into helpGroups
|
|
for (const command of commands) {
|
|
const groupName = command[2];
|
|
if (!helpGroups[groupName]) {
|
|
helpGroups[groupName] = [];
|
|
}
|
|
helpGroups[groupName].push(command);
|
|
}
|
|
const sorted = sortHelpGroups(helpGroups);
|
|
|
|
// Render everything.
|
|
return ['' + _chalk().default.bold('Commands:'), '',
|
|
// Render all of the groups.
|
|
Object.values(sorted).map(group => group
|
|
// Render the command and description
|
|
.map(([cmd, description]) => {
|
|
// Dim the arguments that come after the command, this makes scanning a bit easier.
|
|
let [noArgsCmd, ...noArgsCmdArgs] = cmd.split(' ');
|
|
if (noArgsCmdArgs.length) {
|
|
noArgsCmd += ` ${_chalk().default.dim(noArgsCmdArgs.join(' '))}`;
|
|
}
|
|
|
|
// Word wrap the description.
|
|
let wrappedDescription = description;
|
|
if (description) {
|
|
// Ensure the wrapped description appears on the same padded line.
|
|
wrappedDescription = ' ' + replaceAll(description, '\n', pad('\n', width + 3));
|
|
}
|
|
const paddedName = wrappedDescription ? pad(noArgsCmd, width) : noArgsCmd;
|
|
return paddedName + wrappedDescription;
|
|
}).join('\n').replace(/^/gm, ' '))
|
|
// Double new line to add spacing between groups
|
|
.join('\n\n'), ''].join('\n');
|
|
};
|
|
_commander().default.on('--help', () => {
|
|
_log().default.log(` Run a command with --help for more info 💡`);
|
|
_log().default.log(` $ expo start --help`);
|
|
_log().default.log();
|
|
});
|
|
// 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) {
|
|
return this.action(async (...args) => {
|
|
if (!_getenv().default.boolish('EAS_BUILD', false) && !_commander().default.nonInteractive) {
|
|
try {
|
|
await (0, _profileMethod().profileMethod)(checkCliVersionAsync)();
|
|
} catch {}
|
|
}
|
|
try {
|
|
const options = args[args.length - 1];
|
|
if (options.offline) {
|
|
const {
|
|
ConnectionStatus
|
|
} = await Promise.resolve().then(() => _interopRequireWildcard(require('xdl')));
|
|
ConnectionStatus.setIsOffline(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
|
|
await Promise.all([_xdl().Analytics.flush(), _xdl().UnifiedAnalytics.flush()]);
|
|
} catch (err) {
|
|
// If the `--name` property is being used then we can be sure that the error is coming from `expo init`.
|
|
const commandName = typeof this.name === 'string' ? 'init (probably)' : this.name();
|
|
await (0, _handleErrors().handleErrorsAsync)(err, {
|
|
command: commandName
|
|
});
|
|
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, options = {}) {
|
|
this.option('--config [file]', `${_chalk().default.yellow('Deprecated:')} Use app.config.js to switch config files instead.`);
|
|
return this.asyncAction(async (projectRoot, ...args) => {
|
|
const opts = args[0];
|
|
if (!projectRoot) {
|
|
projectRoot = process.cwd();
|
|
} else {
|
|
projectRoot = _path().default.resolve(process.cwd(), projectRoot);
|
|
}
|
|
if (opts.config) {
|
|
_log().default.log(_chalk().default.yellow(`\u203A ${_chalk().default.bold('--config')} flag is deprecated. Use app.config.js instead. ${(0, _TerminalLink().learnMore)('https://expo.fyi/config-flag-migration')}`));
|
|
_log().default.newLine();
|
|
|
|
// @ts-ignore: This guards against someone passing --config without a path.
|
|
if (opts.config === true) {
|
|
_log().default.addNewLineIfNone();
|
|
_log().default.log('Please specify your custom config path:');
|
|
_log().default.log(_chalk().default.green(` expo ${this.name()} --config ${_chalk().default.cyan(`<app-config>`)}`));
|
|
_log().default.newLine();
|
|
process.exit(1);
|
|
}
|
|
const pathToConfig = _path().default.resolve(process.cwd(), opts.config);
|
|
// Warn the user when the custom config path they provided does not exist.
|
|
if (!_fs().default.existsSync(pathToConfig)) {
|
|
const relativeInput = _path().default.relative(process.cwd(), opts.config);
|
|
const formattedPath = _chalk().default.reset(pathToConfig).replace(relativeInput, _chalk().default.bold(relativeInput));
|
|
_log().default.addNewLineIfNone();
|
|
_log().default.nestedWarn(`Custom config file does not exist:\n${formattedPath}`);
|
|
_log().default.newLine();
|
|
const helpCommand = _chalk().default.green(`expo ${this.name()} --help`);
|
|
_log().default.log(`Run ${helpCommand} for more info`);
|
|
_log().default.newLine();
|
|
process.exit(1);
|
|
// throw new Error(`File at provided config path does not exist: ${pathToConfig}`);
|
|
}
|
|
|
|
(0, _config().setCustomConfigPath)(projectRoot, pathToConfig);
|
|
}
|
|
const logLines = (msg, logFn) => {
|
|
if (typeof msg === 'string') {
|
|
for (const line of msg.split('\n')) {
|
|
logFn(line);
|
|
}
|
|
} else {
|
|
logFn(msg);
|
|
}
|
|
};
|
|
const {
|
|
INTERNAL_CALLSITES_REGEX
|
|
} = await Promise.resolve().then(() => _interopRequireWildcard(require('@expo/metro-config')));
|
|
const logStackTrace = async (chunk, logFn, nestedLogFn) => {
|
|
let traceInfo;
|
|
try {
|
|
traceInfo = JSON.parse(chunk.msg);
|
|
} catch {
|
|
return logFn(chunk.msg);
|
|
}
|
|
const {
|
|
message,
|
|
stack
|
|
} = traceInfo;
|
|
_log().default.addNewLineIfNone();
|
|
logFn(_chalk().default.bold(message));
|
|
const isLibraryFrame = line => {
|
|
return line.startsWith('node_modules');
|
|
};
|
|
const stackFrames = stack.split('\n').filter(line => line);
|
|
const 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++) {
|
|
const line = stackFrames[i];
|
|
if (!line) {
|
|
continue;
|
|
}
|
|
let isCollapsed = false;
|
|
const fileNameOrUrl = (0, _matchFileNameOrURLFromStackTrace().matchFileNameOrURLFromStackTrace)(line);
|
|
if (fileNameOrUrl) {
|
|
// Use the same regex we use in Metro config to filter out traces:
|
|
isCollapsed = INTERNAL_CALLSITES_REGEX.test(fileNameOrUrl);
|
|
|
|
// Unless the user is in debug mode, skip printing the collapsed files.
|
|
if (!_log().default.isDebug && isCollapsed) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// If a file is collapsed, print it with dim styling.
|
|
const style = isCollapsed ? _chalk().default.dim : message => message;
|
|
// Use the `at` prefix to match Node.js
|
|
nestedLogFn(style('at ' + 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, _log().default.nested);
|
|
} else {
|
|
logLines(chunk.msg, _log().default.log);
|
|
}
|
|
} 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,
|
|
onStartBuildBundle({
|
|
bundleDetails
|
|
}) {
|
|
// TODO: Unify with commands/utils/progress.ts
|
|
const platform = _xdl().PackagerLogsStream.getPlatformTagForBuildDetails(bundleDetails);
|
|
bar = new (_progress().default)(`${platform}Bundling JavaScript [:bar] :percent`, {
|
|
width: 64,
|
|
total: 100,
|
|
clear: true,
|
|
complete: '=',
|
|
incomplete: ' '
|
|
});
|
|
_log().default.setBundleProgressBar(bar);
|
|
},
|
|
onProgressBuildBundle({
|
|
progress
|
|
}) {
|
|
if (!bar || bar.complete) return;
|
|
const ticks = progress - bar.curr;
|
|
ticks > 0 && bar.tick(ticks);
|
|
},
|
|
onFinishBuildBundle({
|
|
error,
|
|
start,
|
|
end,
|
|
bundleDetails
|
|
}) {
|
|
if (bar && !bar.complete) {
|
|
bar.tick(100 - bar.curr);
|
|
}
|
|
if (bar) {
|
|
_log().default.setBundleProgressBar(null);
|
|
bar.terminate();
|
|
bar = null;
|
|
const platform = _xdl().PackagerLogsStream.getPlatformTagForBuildDetails(bundleDetails);
|
|
const totalBuildTimeMs = end.getTime() - start.getTime();
|
|
const durationSuffix = _chalk().default.gray(` ${totalBuildTimeMs}ms`);
|
|
if (error) {
|
|
_log().default.log(_chalk().default.red(`${platform}Bundling failed` + durationSuffix));
|
|
} else {
|
|
_log().default.log(_chalk().default.green(`${platform}Bundling complete` + durationSuffix));
|
|
_StatusEventEmitter().default.emit('bundleBuildFinish', {
|
|
totalBuildTimeMs
|
|
});
|
|
}
|
|
}
|
|
},
|
|
updateLogs: updater => {
|
|
const newLogChunks = updater([]);
|
|
newLogChunks.forEach(newLogChunk => {
|
|
if (newLogChunk.issueId && newLogChunk.issueCleared) {
|
|
return;
|
|
}
|
|
logWithLevel(newLogChunk);
|
|
});
|
|
}
|
|
});
|
|
|
|
// needed for validation logging to function
|
|
_xdl().ProjectUtils.attachLoggerStream(projectRoot, {
|
|
stream: {
|
|
write: chunk => {
|
|
if (chunk.tag === 'device') {
|
|
logWithLevel(chunk);
|
|
_StatusEventEmitter().default.emit('deviceLogReceive', {
|
|
deviceId: chunk.deviceId,
|
|
deviceName: chunk.deviceName
|
|
});
|
|
}
|
|
}
|
|
},
|
|
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 (options.checkConfig && (await _xdl().ProjectSettings.getCurrentStatusAsync(projectRoot)) !== 'running') {
|
|
const spinner = (0, _ora().ora)('Making sure project is set up correctly...').start();
|
|
_log().default.setSpinner(spinner);
|
|
// validate that this is a good projectDir before we try anything else
|
|
|
|
const {
|
|
Doctor
|
|
} = await Promise.resolve().then(() => _interopRequireWildcard(require('xdl')));
|
|
const status = await Doctor.validateWithoutNetworkAsync(projectRoot, {
|
|
skipSDKVersionRequirement: options.skipSDKVersionRequirement
|
|
});
|
|
if (status === 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(projectRoot, ...args);
|
|
});
|
|
};
|
|
async function bootstrapAnalyticsAsync() {
|
|
_xdl().Analytics.initializeClient('1wHTzmVgmZvNjCalKL45chlc2VN', 'https://cdp.expo.dev', packageJSON.version);
|
|
_xdl().UnifiedAnalytics.initializeClient('1wabJGd5IiuF9Q8SGlcI90v8WTs', 'https://cdp.expo.dev', packageJSON.version);
|
|
const userData = await (0, _profileMethod().profileMethod)(_xdl().UserManager.getCachedUserDataAsync, 'getCachedUserDataAsync')();
|
|
if (!(userData !== null && userData !== void 0 && userData.userId)) return;
|
|
_xdl().UnifiedAnalytics.identifyUser(userData.userId, {
|
|
userId: userData.userId,
|
|
currentConnection: userData === null || userData === void 0 ? void 0 : userData.currentConnection,
|
|
username: userData === null || userData === void 0 ? void 0 : userData.username
|
|
});
|
|
}
|
|
function trackUsage(commands = []) {
|
|
var _commands$find;
|
|
const input = process.argv[2];
|
|
const ExpoCommand = cmd => (cmd._name === input || cmd._alias === input) && input !== undefined;
|
|
const subCommand = (_commands$find = commands.find(ExpoCommand)) === null || _commands$find === void 0 ? void 0 : _commands$find._name;
|
|
if (!subCommand) return; // only track valid expo commands
|
|
|
|
_xdl().UnifiedAnalytics.logEvent('action', {
|
|
action: `expo ${subCommand}`,
|
|
source: 'expo cli',
|
|
source_version: _xdl().UnifiedAnalytics.version
|
|
});
|
|
}
|
|
async function runAsync(programName) {
|
|
try {
|
|
_registerLogs();
|
|
if (_xdl().Env.shouldEnableAnalytics()) {
|
|
await bootstrapAnalyticsAsync();
|
|
}
|
|
_xdl().UserManager.setInteractiveAuthenticationCallback(_accounts().loginOrRegisterAsync);
|
|
if (process.env.SERVER_URL) {
|
|
let serverUrl = process.env.SERVER_URL;
|
|
if (!serverUrl.startsWith('http')) {
|
|
serverUrl = `http://${serverUrl}`;
|
|
}
|
|
const parsedUrl = _url().default.parse(serverUrl);
|
|
const port = parseInt(parsedUrl.port || '', 10);
|
|
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');
|
|
}
|
|
}
|
|
|
|
// Setup our commander instance
|
|
_commander().default.name(programName);
|
|
_commander().default.version(packageJSON.version).option('--non-interactive', 'Fail, if an interactive prompt would be required to continue.');
|
|
|
|
// Load each module found in ./commands by 'registering' it with our commander instance
|
|
(0, _profileMethod().profileMethod)(_commands().registerCommands)(_commander().default);
|
|
trackUsage(_commander().default.commands); // must be after register commands
|
|
|
|
_commander().default.on('command:detach', () => {
|
|
_log().default.warn('To eject your project to ExpoKit (previously "detach"), use `expo eject`.');
|
|
process.exit(0);
|
|
});
|
|
_commander().default.on('command:diagnostics', () => {
|
|
_log().default.warn('The `expo diagnostics` command is deprecated, use `npx expo-env-info` instead.');
|
|
process.exit(0);
|
|
});
|
|
_commander().default.on('command:*', subCommand => {
|
|
let msg = `"${subCommand}" is not an expo command. See "expo --help" for the full list of commands.`;
|
|
const availableCommands = _commander().default.commands.map(cmd => cmd._name);
|
|
// finding the best match whose edit distance is less than 40% of their length.
|
|
const suggestion = availableCommands.find(commandName => (0, _leven().default)(commandName, subCommand[0]) < commandName.length * 0.4);
|
|
if (suggestion) {
|
|
msg = `"${subCommand}" is not an expo command -- did you mean ${suggestion}?\n See "expo --help" for the full list of commands.`;
|
|
}
|
|
_log().default.warn(msg);
|
|
});
|
|
if (typeof _commander().default.nonInteractive === 'undefined') {
|
|
// Commander doesn't initialize boolean args with default values.
|
|
_commander().default.nonInteractive = !process.stdin.isTTY;
|
|
}
|
|
_commander().default.parse(process.argv);
|
|
|
|
// Show help when no sub-command specified
|
|
if (_commander().default.args.length === 0) {
|
|
_commander().default.help();
|
|
}
|
|
} catch (e) {
|
|
_log().default.error(e);
|
|
throw e;
|
|
}
|
|
}
|
|
async function checkCliVersionAsync() {
|
|
// Skip checking for latest version on EAS Build
|
|
if (_getenv().default.boolish('EAS_BUILD', false)) {
|
|
return;
|
|
}
|
|
const {
|
|
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 ${packageJSON.name} available (${latest}).
|
|
You are currently using ${packageJSON.name} ${current}
|
|
Install expo-cli globally using the package manager of your choice;
|
|
for example: \`npm install -g ${packageJSON.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() {
|
|
const stream = {
|
|
level: _log().default.isDebug ? 'debug' : 'info',
|
|
stream: {
|
|
write: chunk => {
|
|
if (chunk.code) {
|
|
switch (chunk.code) {
|
|
case _xdl().LoadingEvent.START_PROGRESS_BAR:
|
|
{
|
|
const bar = new (_progress().default)(chunk.msg, {
|
|
width: 64,
|
|
total: 100,
|
|
clear: true,
|
|
complete: '=',
|
|
incomplete: ' '
|
|
});
|
|
_log().default.setBundleProgressBar(bar);
|
|
return;
|
|
}
|
|
case _xdl().LoadingEvent.TICK_PROGRESS_BAR:
|
|
{
|
|
const bar = _log().default.getProgress();
|
|
if (bar) {
|
|
bar.tick(1, chunk.msg);
|
|
}
|
|
return;
|
|
}
|
|
case _xdl().LoadingEvent.STOP_PROGRESS_BAR:
|
|
{
|
|
const bar = _log().default.getProgress();
|
|
if (bar) {
|
|
_log().default.setBundleProgressBar(null);
|
|
bar.terminate();
|
|
}
|
|
return;
|
|
}
|
|
case _xdl().LoadingEvent.START_LOADING:
|
|
(0, _ora().logNewSection)(chunk.msg || '');
|
|
return;
|
|
case _xdl().LoadingEvent.STOP_LOADING:
|
|
{
|
|
const spinner = _log().default.getSpinner();
|
|
if (spinner) {
|
|
spinner.stop();
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (chunk.level === _bunyan().default.INFO) {
|
|
_log().default.log(chunk.msg);
|
|
} else if (chunk.level === _bunyan().default.WARN) {
|
|
_log().default.warn(chunk.msg);
|
|
} else if (chunk.level === _bunyan().default.DEBUG) {
|
|
_log().default.debug(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() {
|
|
const 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 => {
|
|
_log().default.error('Uncaught Error', e);
|
|
process.exit(1);
|
|
});
|
|
}
|
|
//# sourceMappingURL=exp.js.map
|