( error: Error, skipFrames: number, )
| 172 | } |
| 173 | |
| 174 | export function parseStackTrace( |
| 175 | error: Error, |
| 176 | skipFrames: number, |
| 177 | ): ReactStackTrace { |
| 178 | // We can only get structured data out of error objects once. So we cache the information |
| 179 | // so we can get it again each time. It also helps performance when the same error is |
| 180 | // referenced more than once. |
| 181 | const existing = stackTraceCache.get(error); |
| 182 | if (existing !== undefined) { |
| 183 | return existing; |
| 184 | } |
| 185 | // We override Error.prepareStackTrace with our own version that collects |
| 186 | // the structured data. We need more information than the raw stack gives us |
| 187 | // and we need to ensure that we don't get the source mapped version. |
| 188 | collectedStackTrace = null; |
| 189 | framesToSkip = skipFrames; |
| 190 | const previousPrepare = Error.prepareStackTrace; |
| 191 | Error.prepareStackTrace = collectStackTrace; |
| 192 | let stack; |
| 193 | try { |
| 194 | // eslint-disable-next-line react-internal/safe-string-coercion |
| 195 | stack = String(error.stack); |
| 196 | } finally { |
| 197 | Error.prepareStackTrace = previousPrepare; |
| 198 | } |
| 199 | |
| 200 | if (collectedStackTrace !== null) { |
| 201 | const result = collectedStackTrace; |
| 202 | collectedStackTrace = null; |
| 203 | stackTraceCache.set(error, result); |
| 204 | return result; |
| 205 | } |
| 206 | |
| 207 | // If the stack has already been read, or this is not actually a V8 compatible |
| 208 | // engine then we might not get a normalized stack and it might still have been |
| 209 | // source mapped. Regardless we try our best to parse it. This works best if the |
| 210 | // environment just uses default V8 formatting and no source mapping. |
| 211 | |
| 212 | if (stack.startsWith('Error: react-stack-top-frame\n')) { |
| 213 | // V8's default formatting prefixes with the error message which we |
| 214 | // don't want/need. |
| 215 | stack = stack.slice(29); |
| 216 | } |
| 217 | let idx = stack.indexOf('react_stack_bottom_frame'); |
| 218 | if (idx !== -1) { |
| 219 | idx = stack.lastIndexOf('\n', idx); |
| 220 | } |
| 221 | if (idx !== -1) { |
| 222 | // Cut off everything after the bottom frame since it'll be internals. |
| 223 | stack = stack.slice(0, idx); |
| 224 | } |
| 225 | const frames = stack.split('\n'); |
| 226 | const parsedFrames: ReactStackTrace = []; |
| 227 | // We skip top frames here since they may or may not be parseable but we |
| 228 | // want to skip the same number of frames regardless. I.e. we can't do it |
| 229 | // in the caller. |
| 230 | for (let i = skipFrames; i < frames.length; i++) { |
| 231 | const parsed = frameRegExp.exec(frames[i]); |
no test coverage detected