UNPKG

@expo/cli

Version:
290 lines (289 loc) 15.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: all[name] }); } _export(exports, { MetroTerminalReporter: function() { return MetroTerminalReporter; }, formatUsingNodeStandardLibraryError: function() { return formatUsingNodeStandardLibraryError; }, isNodeStdLibraryModule: function() { return isNodeStdLibraryModule; }, stripMetroInfo: function() { return stripMetroInfo; } }); function _chalk() { const data = /*#__PURE__*/ _interop_require_default(require("chalk")); _chalk = function() { return data; }; return data; } function _path() { const data = /*#__PURE__*/ _interop_require_default(require("path")); _path = function() { return data; }; return data; } const _TerminalReporter = require("./TerminalReporter"); const _externals = require("./externals"); const _env = require("../../../utils/env"); const _link = require("../../../utils/link"); const _serverLogLikeMetro = require("../serverLogLikeMetro"); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const debug = require('debug')('expo:metro:logger'); const MAX_PROGRESS_BAR_CHAR_WIDTH = 16; const DARK_BLOCK_CHAR = '\u2593'; const LIGHT_BLOCK_CHAR = '\u2591'; class MetroTerminalReporter extends _TerminalReporter.TerminalReporter { constructor(projectRoot, terminal){ super(terminal), this.projectRoot = projectRoot; } _log(event) { switch(event.type){ case 'unstable_server_log': var _event_data; if (typeof ((_event_data = event.data) == null ? void 0 : _event_data[0]) === 'string') { const message = event.data[0]; if (message.match(/JavaScript logs have moved/)) { // Hide this very loud message from upstream React Native in favor of the note in the terminal UI: // The "› Press j │ open debugger" // logger?.info( // '\u001B[1m\u001B[7m💡 JavaScript logs have moved!\u001B[22m They can now be ' + // 'viewed in React Native DevTools. Tip: Type \u001B[1mj\u001B[22m in ' + // 'the terminal to open (requires Google Chrome or Microsoft Edge).' + // '\u001B[27m', // ); return; } if (!_env.env.EXPO_DEBUG) { // In the context of developing an iOS app or website, the MetroInspectorProxy "connection" logs are very confusing. // Here we'll hide them behind EXPO_DEBUG or DEBUG=expo:*. In the future we can reformat them to clearly indicate that the "Connection" is regarding the debugger. // These logs are also confusing because they can say "connection established" even when the debugger is not in a usable state. Really they belong in a UI or behind some sort of debug logging. if (message.match(/Connection (closed|established|failed|terminated)/i)) { // Skip logging. return; } } } break; case 'client_log': { if (this.shouldFilterClientLog(event)) { return; } const { level } = event; if (!level) { break; } const mode = event.mode === 'NOBRIDGE' || event.mode === 'BRIDGE' ? '' : event.mode ?? ''; // @ts-expect-error if (level === 'warn' || level === 'error') { // Quick check to see if an unsymbolicated stack is being logged. const msg = event.data.join('\n'); if (msg.includes('.bundle//&platform=')) { const parsed = (0, _serverLogLikeMetro.parseErrorStringToObject)(msg); if (parsed) { (0, _serverLogLikeMetro.maybeSymbolicateAndFormatReactErrorLogAsync)(this.projectRoot, level, parsed).then((res)=>{ // Overwrite the Metro terminal logging so we can improve the warnings, symbolicate stacks, and inject extra info. (0, _serverLogLikeMetro.logLikeMetro)(this.terminal.log.bind(this.terminal), level, mode, res); }).catch((e)=>{ // Fallback on the original error message if we can't symbolicate the stack. debug('Error formatting stack', e); // Overwrite the Metro terminal logging so we can improve the warnings, symbolicate stacks, and inject extra info. (0, _serverLogLikeMetro.logLikeMetro)(this.terminal.log.bind(this.terminal), level, mode, ...event.data); }); return; } } } // Overwrite the Metro terminal logging so we can improve the warnings, symbolicate stacks, and inject extra info. (0, _serverLogLikeMetro.logLikeMetro)(this.terminal.log.bind(this.terminal), level, mode, ...event.data); return; } } return super._log(event); } // Used for testing _getElapsedTime(startTime) { return process.hrtime.bigint() - startTime; } /** * Extends the bundle progress to include the current platform that we're bundling. * * @returns `iOS path/to/bundle.js ▓▓▓▓▓░░░░░░░░░░░ 36.6% (4790/7922)` */ _getBundleStatusMessage(progress, phase) { var _progress_bundleDetails_customTransformOptions, _progress_bundleDetails; const env = getEnvironmentForBuildDetails(progress.bundleDetails); const platform = env || getPlatformTagForBuildDetails(progress.bundleDetails); const inProgress = phase === 'in_progress'; let localPath; if (typeof ((_progress_bundleDetails = progress.bundleDetails) == null ? void 0 : (_progress_bundleDetails_customTransformOptions = _progress_bundleDetails.customTransformOptions) == null ? void 0 : _progress_bundleDetails_customTransformOptions.dom) === 'string' && progress.bundleDetails.customTransformOptions.dom.includes(_path().default.sep)) { // Because we use a generated entry file for DOM components, we need to adjust the logging path so it // shows a unique path for each component. // Here, we take the relative import path and remove all the starting slashes. localPath = progress.bundleDetails.customTransformOptions.dom.replace(/^(\.?\.[\\/])+/, ''); } else { const inputFile = progress.bundleDetails.entryFile; localPath = _path().default.isAbsolute(inputFile) ? _path().default.relative(this.projectRoot, inputFile) : inputFile; } if (!inProgress) { const status = phase === 'done' ? `Bundled ` : `Bundling failed `; const color = phase === 'done' ? _chalk().default.green : _chalk().default.red; const startTime = this._bundleTimers.get(progress.bundleDetails.buildID); let time = ''; if (startTime != null) { const elapsed = this._getElapsedTime(startTime); const micro = Number(elapsed) / 1000; const converted = Number(elapsed) / 1e6; // If the milliseconds are < 0.5 then it will display as 0, so we display in microseconds. if (converted <= 0.5) { const tenthFractionOfMicro = (micro * 10 / 1000).toFixed(0); // Format as microseconds to nearest tenth time = _chalk().default.cyan.bold(`0.${tenthFractionOfMicro}ms`); } else { time = _chalk().default.dim(converted.toFixed(0) + 'ms'); } } // iOS Bundled 150ms const plural = progress.totalFileCount === 1 ? '' : 's'; return color(platform + status) + time + _chalk().default.reset.dim(` ${localPath} (${progress.totalFileCount} module${plural})`); } const filledBar = Math.floor(progress.ratio * MAX_PROGRESS_BAR_CHAR_WIDTH); const _progress = inProgress ? _chalk().default.green.bgGreen(DARK_BLOCK_CHAR.repeat(filledBar)) + _chalk().default.bgWhite.white(LIGHT_BLOCK_CHAR.repeat(MAX_PROGRESS_BAR_CHAR_WIDTH - filledBar)) + _chalk().default.bold(` ${(100 * progress.ratio).toFixed(1).padStart(4)}% `) + _chalk().default.dim(`(${progress.transformedFileCount.toString().padStart(progress.totalFileCount.toString().length)}/${progress.totalFileCount})`) : ''; return platform + _chalk().default.reset.dim(`${_path().default.dirname(localPath)}${_path().default.sep}`) + _chalk().default.bold(_path().default.basename(localPath)) + ' ' + _progress; } _logInitializing(port, hasReducedPerformance) { // Don't print a giant logo... this.terminal.log(_chalk().default.dim('Starting Metro Bundler')); } shouldFilterClientLog(event) { return isAppRegistryStartupMessage(event.data); } shouldFilterBundleEvent(event) { var _event_bundleDetails; return 'bundleDetails' in event && ((_event_bundleDetails = event.bundleDetails) == null ? void 0 : _event_bundleDetails.bundleType) === 'map'; } /** Print the cache clear message. */ transformCacheReset() { (0, _TerminalReporter.logWarning)(this.terminal, (0, _chalk().default)`Bundler cache is empty, rebuilding {dim (this may take a minute)}`); } /** One of the first logs that will be printed */ dependencyGraphLoading(hasReducedPerformance) { // this.terminal.log('Dependency graph is loading...'); if (hasReducedPerformance) { // Extends https://github.com/facebook/metro/blob/347b1d7ed87995d7951aaa9fd597c04b06013dac/packages/metro/src/lib/TerminalReporter.js#L283-L290 this.terminal.log(_chalk().default.red([ 'Metro is operating with reduced performance.', 'Fix the problem above and restart Metro.' ].join('\n'))); } } _logBundlingError(error) { const moduleResolutionError = formatUsingNodeStandardLibraryError(this.projectRoot, error); const cause = error.cause; if (moduleResolutionError) { let message = maybeAppendCodeFrame(moduleResolutionError, error.message); if (cause == null ? void 0 : cause._expoImportStack) { message += `\n\n${cause == null ? void 0 : cause._expoImportStack}`; } return this.terminal.log(message); } if (cause == null ? void 0 : cause._expoImportStack) { error.message += `\n\n${cause._expoImportStack}`; } return super._logBundlingError(error); } } function formatUsingNodeStandardLibraryError(projectRoot, error) { if (!error.message) { return null; } const { targetModuleName, originModulePath } = error; if (!targetModuleName || !originModulePath) { return null; } const relativePath = _path().default.relative(projectRoot, originModulePath); const DOCS_PAGE_URL = 'https://docs.expo.dev/workflow/using-libraries/#using-third-party-libraries'; if (isNodeStdLibraryModule(targetModuleName)) { if (originModulePath.includes('node_modules')) { return [ `The package at "${_chalk().default.bold(relativePath)}" attempted to import the Node standard library module "${_chalk().default.bold(targetModuleName)}".`, `It failed because the native React runtime does not include the Node standard library.`, (0, _link.learnMore)(DOCS_PAGE_URL) ].join('\n'); } else { return [ `You attempted to import the Node standard library module "${_chalk().default.bold(targetModuleName)}" from "${_chalk().default.bold(relativePath)}".`, `It failed because the native React runtime does not include the Node standard library.`, (0, _link.learnMore)(DOCS_PAGE_URL) ].join('\n'); } } return `Unable to resolve "${targetModuleName}" from "${relativePath}"`; } function isNodeStdLibraryModule(moduleName) { return /^node:/.test(moduleName) || _externals.NODE_STDLIB_MODULES.includes(moduleName); } /** If the code frame can be found then append it to the existing message. */ function maybeAppendCodeFrame(message, rawMessage) { const codeFrame = stripMetroInfo(rawMessage); if (codeFrame) { message += '\n' + codeFrame; } return message; } function stripMetroInfo(errorMessage) { // Newer versions of Metro don't include the list. if (!errorMessage.includes('4. Remove the cache')) { return null; } const lines = errorMessage.split('\n'); const index = lines.findIndex((line)=>line.includes('4. Remove the cache')); if (index === -1) { return null; } return lines.slice(index + 1).join('\n'); } /** @returns if the message matches the initial startup log */ function isAppRegistryStartupMessage(body) { return body.length === 1 && (/^Running application "main" with appParams:/.test(body[0]) || /^Running "main" with \{/.test(body[0])); } /** @returns platform specific tag for a `BundleDetails` object */ function getPlatformTagForBuildDetails(bundleDetails) { const platform = (bundleDetails == null ? void 0 : bundleDetails.platform) ?? null; if (platform) { const formatted = { ios: 'iOS', android: 'Android', web: 'Web' }[platform] || platform; return `${_chalk().default.bold(formatted)} `; } return ''; } /** @returns platform specific tag for a `BundleDetails` object */ function getEnvironmentForBuildDetails(bundleDetails) { var _bundleDetails_customTransformOptions, _bundleDetails_customTransformOptions1, _bundleDetails_customTransformOptions2; // Expo CLI will pass `customTransformOptions.environment = 'node'` when bundling for the server. const env = (bundleDetails == null ? void 0 : (_bundleDetails_customTransformOptions = bundleDetails.customTransformOptions) == null ? void 0 : _bundleDetails_customTransformOptions.environment) ?? null; if (env === 'node') { return _chalk().default.bold('λ') + ' '; } else if (env === 'react-server') { return _chalk().default.bold(`RSC(${getPlatformTagForBuildDetails(bundleDetails).trim()})`) + ' '; } if ((bundleDetails == null ? void 0 : (_bundleDetails_customTransformOptions1 = bundleDetails.customTransformOptions) == null ? void 0 : _bundleDetails_customTransformOptions1.dom) && typeof (bundleDetails == null ? void 0 : (_bundleDetails_customTransformOptions2 = bundleDetails.customTransformOptions) == null ? void 0 : _bundleDetails_customTransformOptions2.dom) === 'string') { return _chalk().default.bold(`DOM`) + ' '; } return ''; } //# sourceMappingURL=MetroTerminalReporter.js.map