"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; function _chalk() { const data = _interopRequireDefault(require("chalk")); _chalk = function () { return data; }; return data; } function _getenv() { const data = _interopRequireDefault(require("getenv")); _getenv = function () { return data; }; return data; } function _path() { const data = _interopRequireDefault(require("path")); _path = function () { return data; }; return data; } function _internal() { const data = require("../internal"); _internal = function () { return data; }; return data; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } class PackagerLogsStream { constructor({ projectRoot, getCurrentOpenProjectId, updateLogs, onStartBuildBundle, onProgressBuildBundle, onFinishBuildBundle, getSnippetForError }) { _defineProperty(this, "_projectRoot", void 0); _defineProperty(this, "_getCurrentOpenProjectId", void 0); _defineProperty(this, "_updateLogs", void 0); _defineProperty(this, "_logsToAdd", []); _defineProperty(this, "_bundleBuildChunkID", null); _defineProperty(this, "_onStartBuildBundle", void 0); _defineProperty(this, "_onProgressBuildBundle", void 0); _defineProperty(this, "_onFinishBuildBundle", void 0); _defineProperty(this, "_bundleBuildStart", null); _defineProperty(this, "_getSnippetForError", void 0); _defineProperty(this, "projectId", void 0); _defineProperty(this, "bundleDetailsCache", {}); _defineProperty(this, "_handleBundleTransformEvent", chunk => { const msg = chunk.msg; const bundleDetails = 'buildID' in msg ? this.bundleDetailsCache[msg.buildID] || null : null; if (msg.type === 'bundle_build_started') { // Cache bundle details for later. this.bundleDetailsCache[String(msg.buildID)] = msg.bundleDetails; chunk._metroEventType = 'BUILD_STARTED'; this._handleNewBundleTransformStarted(chunk, msg.bundleDetails); } else if (msg.type === 'bundle_transform_progressed' && this._bundleBuildChunkID) { chunk._metroEventType = 'BUILD_PROGRESS'; this._handleUpdateBundleTransformProgress(chunk, bundleDetails); } else if (msg.type === 'bundle_build_failed' && this._bundleBuildChunkID) { chunk._metroEventType = 'BUILD_FAILED'; this._handleUpdateBundleTransformProgress(chunk, bundleDetails); } else if (msg.type === 'bundle_build_done' && this._bundleBuildChunkID) { chunk._metroEventType = 'BUILD_DONE'; this._handleUpdateBundleTransformProgress(chunk, bundleDetails); } }); _defineProperty(this, "_enqueueFlushLogsToAdd", () => { this._updateLogs(logs => { if (this._logsToAdd.length === 0) { return logs; } const nextLogs = logs.concat(this._logsToAdd); this._logsToAdd = []; return nextLogs; }); }); _defineProperty(this, "_cleanUpNodeErrors", chunk => { if (typeof chunk.msg !== 'string') { return chunk; } if (chunk.msg.match(/\(node:.\d*\)/)) { // Example: (node:13817) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): SyntaxError: SyntaxError /Users/brent/universe/apps/new-project-template/main.js: Unexpected token (10:6) // The first part of this is totally useless, so let's remove it. if (chunk.msg.match(/UnhandledPromiseRejectionWarning/)) { chunk.msg = chunk.msg.replace(/\(node:.*\(rejection .*\):/, ''); if (chunk.msg.match(/SyntaxError: SyntaxError/)) { chunk.msg = chunk.msg.replace('SyntaxError: ', ''); } } else if (chunk.msg.match(/DeprecationWarning/)) { chunk.msg = ''; } } return chunk; }); this._projectRoot = projectRoot; this._getCurrentOpenProjectId = getCurrentOpenProjectId || (() => 1); this._updateLogs = updateLogs; // Optional properties in case the consumer wants to handle updates on // its own, eg: for a progress bar this._onStartBuildBundle = onStartBuildBundle; this._onProgressBuildBundle = onProgressBuildBundle; this._onFinishBuildBundle = onFinishBuildBundle; // Optional function for creating custom code frame snippet // (e.g. with terminal colors) from a syntax error. this._getSnippetForError = getSnippetForError; this._attachLoggerStream(); } _attachLoggerStream() { this.projectId = this._getCurrentOpenProjectId(); _internal().ProjectUtils.attachLoggerStream(this._projectRoot, { stream: { write: this._handleChunk.bind(this) }, type: 'raw' }); } _handleChunk(chunk) { if (chunk.tag !== 'metro' && chunk.tag !== 'expo') { return; } else if (this._getCurrentOpenProjectId() !== this.projectId) { // TODO: We should be confident that we are properly unsubscribing // from the stream rather than doing a defensive check like this. return; } chunk = this._maybeParseMsgJSON(chunk); chunk = this._cleanUpNodeErrors(chunk); if (chunk.tag === 'metro') { this._handleMetroEvent(chunk); } else if (typeof chunk.msg === 'string' && chunk.msg.match(/\w/) && chunk.msg[0] !== '{') { this._enqueueAppendLogChunk(chunk); } } _handleMetroEvent(originalChunk) { const chunk = { ...originalChunk }; const { msg } = chunk; if (typeof msg === 'string') { if (msg.includes('HTTP/1.1') && !_getenv().default.boolish('EXPO_DEBUG', false)) { // Do nothing with this message - we want to filter out network requests logged by Metro. } else { // If Metro crashes for some reason, it may log an error message as a plain string to stderr. this._enqueueAppendLogChunk(chunk); } return; } switch (msg.type) { // Bundle transform events case 'bundle_build_started': case 'bundle_transform_progressed': case 'bundle_build_failed': case 'bundle_build_done': this._handleBundleTransformEvent(chunk); return; case 'initialize_started': chunk._metroEventType = 'METRO_INITIALIZE_STARTED'; chunk.msg = 'Starting Metro Bundler'; break; case 'initialize_done': chunk.msg = `Started Metro Bundler`; break; case 'initialize_failed': { // SDK <=22 const code = msg.error.code; chunk.msg = code === 'EADDRINUSE' ? `Metro Bundler can't listen on port ${msg.port}. The port is in use.` : `Metro Bundler failed to start. (code: ${code})`; break; } case 'bundling_error': chunk.msg = this._formatModuleResolutionError(msg.error) || this._formatBundlingError(msg.error) || msg; chunk.level = _internal().Logger.ERROR; break; case 'bundling_warning': chunk.msg = msg.warning; chunk.level = _internal().Logger.WARN; break; case 'transform_cache_reset': chunk.msg = 'Your JavaScript transform cache is empty, rebuilding (this may take a minute).'; break; case 'hmr_client_error': chunk.msg = `A WebSocket client got a connection error. Please reload your device to get HMR working again.`; break; case 'global_cache_disabled': if (msg.reason === 'too_many_errors') { chunk.msg = 'The global cache is now disabled because it has been failing too many times.'; } else if (msg.reason === 'too_many_misses') { chunk.msg = `The global cache is now disabled because it has been missing too many consecutive keys.`; } else { chunk.msg = `The global cache is now disabled. Reason: ${msg.reason}`; } break; case 'worker_stdout_chunk': chunk.msg = this._formatWorkerChunk('stdout', msg.chunk); break; case 'worker_stderr_chunk': chunk.msg = this._formatWorkerChunk('stderr', msg.chunk); break; // Ignored events. case 'client_log': case 'dep_graph_loading': case 'dep_graph_loaded': case 'global_cache_error': case 'transformer_load_started': case 'transformer_load_done': return; default: chunk.msg = `Unrecognized event: ${JSON.stringify(msg)}`; break; } this._enqueueAppendLogChunk(chunk); } // A cache of { [buildID]: BundleDetails } which can be used to // add more contextual logs. BundleDetails is currently only sent with `bundle_build_started` // so we need to cache the details in order to print the platform info with other event types. static getPlatformTagForBuildDetails(bundleDetails) { var _bundleDetails$platfo; const platform = (_bundleDetails$platfo = bundleDetails === null || bundleDetails === void 0 ? void 0 : bundleDetails.platform) !== null && _bundleDetails$platfo !== void 0 ? _bundleDetails$platfo : null; if (platform) { const formatted = { ios: 'iOS', android: 'Android', web: 'Web' }[platform] || platform; return `${_chalk().default.bold(formatted)} `; } return ''; } _handleNewBundleTransformStarted(chunk, bundleDetails) { if (this._bundleBuildChunkID) { return; } this._bundleBuildChunkID = chunk.id; this._bundleBuildStart = new Date(); chunk.msg = 'Building JavaScript bundle'; if (this._onStartBuildBundle) { this._onStartBuildBundle({ chunk, bundleDetails }); } else { this._enqueueAppendLogChunk(chunk); } } _handleUpdateBundleTransformProgress(progressChunk, bundleDetails) { const msg = progressChunk.msg; let percentProgress; let bundleComplete = false; if (msg.type === 'bundle_build_done') { percentProgress = 100; bundleComplete = true; if (this._bundleBuildStart) { const duration = new Date().getTime() - this._bundleBuildStart.getTime(); progressChunk.msg = `Building JavaScript bundle: finished in ${duration}ms.`; } else { progressChunk.msg = `Building JavaScript bundle: finished.`; } } else if (msg.type === 'bundle_build_failed') { percentProgress = -1; bundleComplete = true; progressChunk.msg = `Building JavaScript bundle: error`; progressChunk.level = _internal().Logger.ERROR; } else if (msg.type === 'bundle_transform_progressed') { if (msg.percentage) { percentProgress = msg.percentage * 100; } else { percentProgress = msg.transformedFileCount / msg.totalFileCount * 100; // percentProgress = Math.floor((msg.transformedFileCount / msg.totalFileCount) * 100); } const roundedPercentProgress = Math.floor(100 * percentProgress) / 100; progressChunk.msg = `Building JavaScript bundle: ${roundedPercentProgress}%`; } else { return; } if (this._bundleBuildChunkID) { progressChunk.id = this._bundleBuildChunkID; } if (this._onProgressBuildBundle) { this._onProgressBuildBundle({ progress: percentProgress, start: this._bundleBuildStart, chunk: progressChunk, bundleDetails }); if (bundleComplete) { if (this._onFinishBuildBundle && this._bundleBuildStart) { const error = msg.type === 'bundle_build_failed' ? 'Build failed' : null; this._onFinishBuildBundle({ error, start: this._bundleBuildStart, end: new Date(), chunk: progressChunk, bundleDetails }); } this._bundleBuildStart = null; this._bundleBuildChunkID = null; } } else { this._updateLogs(logs => { if (!logs || !logs.length) { return []; } logs.forEach(log => { if (log.id === this._bundleBuildChunkID) { log.msg = progressChunk.msg; } }); if (bundleComplete) { this._bundleBuildChunkID = null; } return [...logs]; }); } } _formatModuleResolutionError(error) { if (!error.message) { return null; } const match = /^Unable to resolve module `(.+?)`/.exec(error.message); const originModulePath = error.originModulePath; if (!match || !originModulePath) { return null; } const moduleName = match[1]; const relativePath = _path().default.relative(this._projectRoot, originModulePath); const DOCS_PAGE_URL = 'https://docs.expo.dev/workflow/using-libraries/#using-third-party-libraries'; if (NODE_STDLIB_MODULES.includes(moduleName)) { if (originModulePath.includes('node_modules')) { return `The package at "${relativePath}" attempted to import the Node standard library module "${moduleName}". It failed because the native React runtime does not include the Node standard library. Read more at ${DOCS_PAGE_URL}`; } else { return `You attempted attempted to import the Node standard library module "${moduleName}" from "${relativePath}". It failed because the native React runtime does not include the Node standard library. Read more at ${DOCS_PAGE_URL}`; } } return `Unable to resolve "${moduleName}" from "${relativePath}"`; } _formatBundlingError(error) { let message = error.message; if (!message && Array.isArray(error.errors) && error.errors.length) { message = error.errors[0].description; } if (!message) { return null; } message = _chalk().default.red(message); const snippet = this._getSnippetForError && this._getSnippetForError(error) || error.snippet; if (snippet) { message += `\n${snippet}`; } // Import errors are already pretty useful and don't need extra info added to them. const isAmbiguousError = !error.name || ['SyntaxError'].includes(error.name); // When you have a basic syntax error in application code it will tell you the file // and usually also provide a well informed error. const isComprehensiveTransformError = error.type === 'TransformError' && error.filename; // console.log(require('util').inspect(error, { depth: 4 })); if (error.stack && isAmbiguousError && !isComprehensiveTransformError) { message += `\n${_chalk().default.gray(error.stack)}`; } return message; } _formatWorkerChunk(origin, chunk) { return chunk; // const lines = chunk.split('\n'); // if (lines.length >= 1 && lines[lines.length - 1] === '') { // lines.splice(lines.length - 1, 1); // } // return lines.map(line => `transform[${origin}]: ${line}`).join('\n'); } _enqueueAppendLogChunk(chunk) { if (!chunk.shouldHide) { this._logsToAdd.push(chunk); this._enqueueFlushLogsToAdd(); } } _maybeParseMsgJSON(chunk) { try { const parsedMsg = JSON.parse(chunk.msg); chunk.msg = parsedMsg; } catch { // non-JSON message } return chunk; } } exports.default = PackagerLogsStream; const NODE_STDLIB_MODULES = ['assert', 'async_hooks', 'buffer', 'child_process', 'cluster', 'crypto', 'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'https', 'net', 'os', 'path', 'punycode', 'querystring', 'readline', 'repl', 'stream', 'string_decoder', 'tls', 'tty', 'url', 'util', 'v8', 'vm', 'zlib']; //# sourceMappingURL=PackagerLogsStream.js.map