1"use strict"; 2/** 3 * Copyright (c) 650 Industries. 4 * Copyright (c) Meta Platforms, Inc. and affiliates. 5 * 6 * This source code is licensed under the MIT license found in the 7 * LICENSE file in the root directory of this source tree. 8 */ 9var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 10 if (k2 === undefined) k2 = k; 11 var desc = Object.getOwnPropertyDescriptor(m, k); 12 if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 13 desc = { enumerable: true, get: function() { return m[k]; } }; 14 } 15 Object.defineProperty(o, k2, desc); 16}) : (function(o, m, k, k2) { 17 if (k2 === undefined) k2 = k; 18 o[k2] = m[k]; 19})); 20var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 21 Object.defineProperty(o, "default", { enumerable: true, value: v }); 22}) : function(o, v) { 23 o["default"] = v; 24}); 25var __importStar = (this && this.__importStar) || function (mod) { 26 if (mod && mod.__esModule) return mod; 27 var result = {}; 28 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 29 __setModuleDefault(result, mod); 30 return result; 31}; 32var __importDefault = (this && this.__importDefault) || function (mod) { 33 return (mod && mod.__esModule) ? mod : { "default": mod }; 34}; 35Object.defineProperty(exports, "__esModule", { value: true }); 36exports.withSubscription = exports.observe = exports.isDisabled = exports.setDisabled = exports.addIgnorePatterns = exports.getIgnorePatterns = exports.dismiss = exports.clearErrors = exports.clearWarnings = exports.setSelectedLog = exports.clear = exports.symbolicateLogLazy = exports.retrySymbolicateLogNow = exports.symbolicateLogNow = exports.addException = exports.addLog = exports.isMessageIgnored = exports.isLogBoxErrorMessage = exports.reportUnexpectedLogBoxError = exports.reportLogBoxError = void 0; 37const React = __importStar(require("react")); 38const LogBoxLog_1 = require("./LogBoxLog"); 39const LogContext_1 = require("./LogContext"); 40const parseLogBoxLog_1 = require("./parseLogBoxLog"); 41const NativeLogBox_1 = __importDefault(require("../modules/NativeLogBox")); 42const parseErrorStack_1 = __importDefault(require("../modules/parseErrorStack")); 43const observers = new Set(); 44const ignorePatterns = new Set(); 45let logs = new Set(); 46let updateTimeout = null; 47let _isDisabled = false; 48let _selectedIndex = -1; 49const LOGBOX_ERROR_MESSAGE = 'An error was thrown when attempting to render log messages via LogBox.'; 50function getNextState() { 51 return { 52 logs, 53 isDisabled: _isDisabled, 54 selectedLogIndex: _selectedIndex, 55 }; 56} 57function reportLogBoxError(error, componentStack) { 58 const ExceptionsManager = require('../modules/ExceptionsManager').default; 59 if (componentStack != null) { 60 error.componentStack = componentStack; 61 } 62 ExceptionsManager.handleException(error); 63} 64exports.reportLogBoxError = reportLogBoxError; 65function reportUnexpectedLogBoxError(error, componentStack) { 66 error.message = `${LOGBOX_ERROR_MESSAGE}\n\n${error.message}`; 67 return reportLogBoxError(error, componentStack); 68} 69exports.reportUnexpectedLogBoxError = reportUnexpectedLogBoxError; 70function isLogBoxErrorMessage(message) { 71 return typeof message === 'string' && message.includes(LOGBOX_ERROR_MESSAGE); 72} 73exports.isLogBoxErrorMessage = isLogBoxErrorMessage; 74function isMessageIgnored(message) { 75 for (const pattern of ignorePatterns) { 76 if ((pattern instanceof RegExp && pattern.test(message)) || 77 (typeof pattern === 'string' && message.includes(pattern))) { 78 return true; 79 } 80 } 81 return false; 82} 83exports.isMessageIgnored = isMessageIgnored; 84function setImmediateShim(callback) { 85 if (!global.setImmediate) { 86 return setTimeout(callback, 0); 87 } 88 return global.setImmediate(callback); 89} 90function handleUpdate() { 91 if (updateTimeout == null) { 92 updateTimeout = setImmediateShim(() => { 93 updateTimeout = null; 94 const nextState = getNextState(); 95 observers.forEach(({ observer }) => observer(nextState)); 96 }); 97 } 98} 99function appendNewLog(newLog) { 100 // Don't want store these logs because they trigger a 101 // state update when we add them to the store. 102 if (isMessageIgnored(newLog.message.content)) { 103 return; 104 } 105 // If the next log has the same category as the previous one 106 // then roll it up into the last log in the list by incrementing 107 // the count (similar to how Chrome does it). 108 const lastLog = Array.from(logs).pop(); 109 if (lastLog && lastLog.category === newLog.category) { 110 lastLog.incrementCount(); 111 handleUpdate(); 112 return; 113 } 114 if (newLog.level === 'fatal') { 115 // If possible, to avoid jank, we don't want to open the error before 116 // it's symbolicated. To do that, we optimistically wait for 117 // symbolication for up to a second before adding the log. 118 const OPTIMISTIC_WAIT_TIME = 1000; 119 let addPendingLog = () => { 120 logs.add(newLog); 121 if (_selectedIndex < 0) { 122 setSelectedLog(logs.size - 1); 123 } 124 else { 125 handleUpdate(); 126 } 127 addPendingLog = null; 128 }; 129 const optimisticTimeout = setTimeout(() => { 130 if (addPendingLog) { 131 addPendingLog(); 132 } 133 }, OPTIMISTIC_WAIT_TIME); 134 // TODO: HANDLE THIS 135 newLog.symbolicate('component'); 136 newLog.symbolicate('stack', (status) => { 137 if (addPendingLog && status !== 'PENDING') { 138 addPendingLog(); 139 clearTimeout(optimisticTimeout); 140 } 141 else if (status !== 'PENDING') { 142 // The log has already been added but we need to trigger a render. 143 handleUpdate(); 144 } 145 }); 146 } 147 else if (newLog.level === 'syntax') { 148 logs.add(newLog); 149 setSelectedLog(logs.size - 1); 150 } 151 else { 152 logs.add(newLog); 153 handleUpdate(); 154 } 155} 156function addLog(log) { 157 const errorForStackTrace = new Error(); 158 // Parsing logs are expensive so we schedule this 159 // otherwise spammy logs would pause rendering. 160 setImmediate(() => { 161 try { 162 const stack = (0, parseErrorStack_1.default)(errorForStackTrace?.stack); 163 appendNewLog(new LogBoxLog_1.LogBoxLog({ 164 level: log.level, 165 message: log.message, 166 isComponentError: false, 167 stack, 168 category: log.category, 169 componentStack: log.componentStack, 170 })); 171 } 172 catch (error) { 173 reportUnexpectedLogBoxError(error); 174 } 175 }); 176} 177exports.addLog = addLog; 178function addException(error) { 179 // Parsing logs are expensive so we schedule this 180 // otherwise spammy logs would pause rendering. 181 setImmediate(() => { 182 try { 183 appendNewLog(new LogBoxLog_1.LogBoxLog((0, parseLogBoxLog_1.parseLogBoxException)(error))); 184 } 185 catch (loggingError) { 186 reportUnexpectedLogBoxError(loggingError); 187 } 188 }); 189} 190exports.addException = addException; 191function symbolicateLogNow(type, log) { 192 log.symbolicate(type, () => { 193 handleUpdate(); 194 }); 195} 196exports.symbolicateLogNow = symbolicateLogNow; 197function retrySymbolicateLogNow(type, log) { 198 log.retrySymbolicate(type, () => { 199 handleUpdate(); 200 }); 201} 202exports.retrySymbolicateLogNow = retrySymbolicateLogNow; 203function symbolicateLogLazy(type, log) { 204 log.symbolicate(type); 205} 206exports.symbolicateLogLazy = symbolicateLogLazy; 207function clear() { 208 if (logs.size > 0) { 209 logs = new Set(); 210 setSelectedLog(-1); 211 } 212} 213exports.clear = clear; 214function setSelectedLog(proposedNewIndex) { 215 const oldIndex = _selectedIndex; 216 let newIndex = proposedNewIndex; 217 const logArray = Array.from(logs); 218 let index = logArray.length - 1; 219 while (index >= 0) { 220 // The latest syntax error is selected and displayed before all other logs. 221 if (logArray[index].level === 'syntax') { 222 newIndex = index; 223 break; 224 } 225 index -= 1; 226 } 227 _selectedIndex = newIndex; 228 handleUpdate(); 229 if (NativeLogBox_1.default) { 230 setTimeout(() => { 231 if (oldIndex < 0 && newIndex >= 0) { 232 NativeLogBox_1.default.show(); 233 } 234 else if (oldIndex >= 0 && newIndex < 0) { 235 NativeLogBox_1.default.hide(); 236 } 237 }, 0); 238 } 239} 240exports.setSelectedLog = setSelectedLog; 241function clearWarnings() { 242 const newLogs = Array.from(logs).filter((log) => log.level !== 'warn'); 243 if (newLogs.length !== logs.size) { 244 logs = new Set(newLogs); 245 setSelectedLog(-1); 246 handleUpdate(); 247 } 248} 249exports.clearWarnings = clearWarnings; 250function clearErrors() { 251 const newLogs = Array.from(logs).filter((log) => log.level !== 'error' && log.level !== 'fatal'); 252 if (newLogs.length !== logs.size) { 253 logs = new Set(newLogs); 254 setSelectedLog(-1); 255 } 256} 257exports.clearErrors = clearErrors; 258function dismiss(log) { 259 if (logs.has(log)) { 260 logs.delete(log); 261 handleUpdate(); 262 } 263} 264exports.dismiss = dismiss; 265function getIgnorePatterns() { 266 return Array.from(ignorePatterns); 267} 268exports.getIgnorePatterns = getIgnorePatterns; 269function addIgnorePatterns(patterns) { 270 const existingSize = ignorePatterns.size; 271 // The same pattern may be added multiple times, but adding a new pattern 272 // can be expensive so let's find only the ones that are new. 273 patterns.forEach((pattern) => { 274 if (pattern instanceof RegExp) { 275 for (const existingPattern of ignorePatterns) { 276 if (existingPattern instanceof RegExp && 277 existingPattern.toString() === pattern.toString()) { 278 return; 279 } 280 } 281 ignorePatterns.add(pattern); 282 } 283 ignorePatterns.add(pattern); 284 }); 285 if (ignorePatterns.size === existingSize) { 286 return; 287 } 288 // We need to recheck all of the existing logs. 289 // This allows adding an ignore pattern anywhere in the codebase. 290 // Without this, if you ignore a pattern after the a log is created, 291 // then we would keep showing the log. 292 logs = new Set(Array.from(logs).filter((log) => !isMessageIgnored(log.message.content))); 293 handleUpdate(); 294} 295exports.addIgnorePatterns = addIgnorePatterns; 296function setDisabled(value) { 297 if (value === _isDisabled) { 298 return; 299 } 300 _isDisabled = value; 301 handleUpdate(); 302} 303exports.setDisabled = setDisabled; 304function isDisabled() { 305 return _isDisabled; 306} 307exports.isDisabled = isDisabled; 308function observe(observer) { 309 const subscription = { observer }; 310 observers.add(subscription); 311 observer(getNextState()); 312 return { 313 unsubscribe() { 314 observers.delete(subscription); 315 }, 316 }; 317} 318exports.observe = observe; 319function withSubscription(WrappedComponent) { 320 class LogBoxStateSubscription extends React.Component { 321 static getDerivedStateFromError() { 322 return { hasError: true }; 323 } 324 componentDidCatch(err, errorInfo) { 325 /* $FlowFixMe[class-object-subtyping] added when improving typing for 326 * this parameters */ 327 reportLogBoxError(err, errorInfo.componentStack); 328 } 329 _subscription; 330 state = { 331 logs: new Set(), 332 isDisabled: false, 333 hasError: false, 334 selectedLogIndex: -1, 335 }; 336 render() { 337 if (this.state.hasError) { 338 // This happens when the component failed to render, in which case we delegate to the native redbox. 339 // We can't show any fallback UI here, because the error may be with <View> or <Text>. 340 return null; 341 } 342 return (React.createElement(LogContext_1.LogContext.Provider, { value: { 343 selectedLogIndex: this.state.selectedLogIndex, 344 isDisabled: this.state.isDisabled, 345 logs: Array.from(this.state.logs), 346 } }, 347 this.props.children, 348 React.createElement(WrappedComponent, null))); 349 } 350 componentDidMount() { 351 this._subscription = observe((data) => { 352 this.setState(data); 353 }); 354 } 355 componentWillUnmount() { 356 if (this._subscription != null) { 357 this._subscription.unsubscribe(); 358 } 359 } 360 _handleDismiss = () => { 361 // Here we handle the cases when the log is dismissed and it 362 // was either the last log, or when the current index 363 // is now outside the bounds of the log array. 364 const { selectedLogIndex, logs: stateLogs } = this.state; 365 const logsArray = Array.from(stateLogs); 366 if (selectedLogIndex != null) { 367 if (logsArray.length - 1 <= 0) { 368 setSelectedLog(-1); 369 } 370 else if (selectedLogIndex >= logsArray.length - 1) { 371 setSelectedLog(selectedLogIndex - 1); 372 } 373 dismiss(logsArray[selectedLogIndex]); 374 } 375 }; 376 _handleMinimize = () => { 377 setSelectedLog(-1); 378 }; 379 _handleSetSelectedLog = (index) => { 380 setSelectedLog(index); 381 }; 382 } 383 // @ts-expect-error 384 return LogBoxStateSubscription; 385} 386exports.withSubscription = withSubscription; 387//# sourceMappingURL=LogBoxData.js.map