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.
407 lines
15 KiB
407 lines
15 KiB
"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
|