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 __importDefault = (this && this.__importDefault) || function (mod) {
10    return (mod && mod.__esModule) ? mod : { "default": mod };
11};
12Object.defineProperty(exports, "__esModule", { value: true });
13exports.parseLogBoxLog = exports.parseLogBoxException = exports.parseComponentStack = exports.parseInterpolation = void 0;
14const UTFSequence_1 = __importDefault(require("react-native/Libraries/UTFSequence"));
15const parseErrorStack_1 = __importDefault(require("../modules/parseErrorStack"));
16const stringifySafe_1 = __importDefault(require("../modules/stringifySafe"));
17const BABEL_TRANSFORM_ERROR_FORMAT = /^(?:TransformError )?(?:SyntaxError: |ReferenceError: )(.*): (.*) \((\d+):(\d+)\)\n\n([\s\S]+)/;
18const BABEL_CODE_FRAME_ERROR_FORMAT = /^(?:TransformError )?(?:.*):? (?:.*?)(\/.*): ([\s\S]+?)\n([ >]{2}[\d\s]+ \|[\s\S]+|\u{001b}[\s\S]+)/u;
19const METRO_ERROR_FORMAT = /^(?:InternalError Metro has encountered an error:) (.*): (.*) \((\d+):(\d+)\)\n\n([\s\S]+)/u;
20const SUBSTITUTION = UTFSequence_1.default.BOM + '%s';
21function parseInterpolation(args) {
22    const categoryParts = [];
23    const contentParts = [];
24    const substitutionOffsets = [];
25    const remaining = [...args];
26    if (typeof remaining[0] === 'string') {
27        const formatString = String(remaining.shift());
28        const formatStringParts = formatString.split('%s');
29        const substitutionCount = formatStringParts.length - 1;
30        const substitutions = remaining.splice(0, substitutionCount);
31        let categoryString = '';
32        let contentString = '';
33        let substitutionIndex = 0;
34        for (const formatStringPart of formatStringParts) {
35            categoryString += formatStringPart;
36            contentString += formatStringPart;
37            if (substitutionIndex < substitutionCount) {
38                if (substitutionIndex < substitutions.length) {
39                    // Don't stringify a string type.
40                    // It adds quotation mark wrappers around the string,
41                    // which causes the LogBox to look odd.
42                    const substitution = typeof substitutions[substitutionIndex] === 'string'
43                        ? substitutions[substitutionIndex]
44                        : (0, stringifySafe_1.default)(substitutions[substitutionIndex]);
45                    substitutionOffsets.push({
46                        length: substitution.length,
47                        offset: contentString.length,
48                    });
49                    categoryString += SUBSTITUTION;
50                    contentString += substitution;
51                }
52                else {
53                    substitutionOffsets.push({
54                        length: 2,
55                        offset: contentString.length,
56                    });
57                    categoryString += '%s';
58                    contentString += '%s';
59                }
60                substitutionIndex++;
61            }
62        }
63        categoryParts.push(categoryString);
64        contentParts.push(contentString);
65    }
66    const remainingArgs = remaining.map((arg) => {
67        // Don't stringify a string type.
68        // It adds quotation mark wrappers around the string,
69        // which causes the LogBox to look odd.
70        return typeof arg === 'string' ? arg : (0, stringifySafe_1.default)(arg);
71    });
72    categoryParts.push(...remainingArgs);
73    contentParts.push(...remainingArgs);
74    return {
75        category: categoryParts.join(' '),
76        message: {
77            content: contentParts.join(' '),
78            substitutions: substitutionOffsets,
79        },
80    };
81}
82exports.parseInterpolation = parseInterpolation;
83function isComponentStack(consoleArgument) {
84    const isOldComponentStackFormat = / {4}in/.test(consoleArgument);
85    const isNewComponentStackFormat = / {4}at/.test(consoleArgument);
86    const isNewJSCComponentStackFormat = /@.*\n/.test(consoleArgument);
87    return isOldComponentStackFormat || isNewComponentStackFormat || isNewJSCComponentStackFormat;
88}
89function parseComponentStack(message) {
90    // In newer versions of React, the component stack is formatted as a call stack frame.
91    // First try to parse the component stack as a call stack frame, and if that doesn't
92    // work then we'll fallback to the old custom component stack format parsing.
93    const stack = (0, parseErrorStack_1.default)(message);
94    if (stack && stack.length > 0) {
95        return stack.map((frame) => ({
96            content: frame.methodName,
97            collapse: frame.collapse || false,
98            fileName: frame.file == null ? 'unknown' : frame.file,
99            location: {
100                column: frame.column == null ? -1 : frame.column,
101                row: frame.lineNumber == null ? -1 : frame.lineNumber,
102            },
103        }));
104    }
105    return message
106        .split(/\n {4}in /g)
107        .map((s) => {
108        if (!s) {
109            return null;
110        }
111        const match = s.match(/(.*) \(at (.*\.js):([\d]+)\)/);
112        if (!match) {
113            return null;
114        }
115        const [content, fileName, row] = match.slice(1);
116        return {
117            content,
118            fileName,
119            location: { column: -1, row: parseInt(row, 10) },
120        };
121    })
122        .filter(Boolean);
123}
124exports.parseComponentStack = parseComponentStack;
125function parseLogBoxException(error) {
126    const message = error.originalMessage != null ? error.originalMessage : 'Unknown';
127    const metroInternalError = message.match(METRO_ERROR_FORMAT);
128    if (metroInternalError) {
129        const [content, fileName, row, column, codeFrame] = metroInternalError.slice(1);
130        return {
131            level: 'fatal',
132            type: 'Metro Error',
133            stack: [],
134            isComponentError: false,
135            componentStack: [],
136            codeFrame: {
137                fileName,
138                location: {
139                    row: parseInt(row, 10),
140                    column: parseInt(column, 10),
141                },
142                content: codeFrame,
143            },
144            message: {
145                content,
146                substitutions: [],
147            },
148            category: `${fileName}-${row}-${column}`,
149        };
150    }
151    const babelTransformError = message.match(BABEL_TRANSFORM_ERROR_FORMAT);
152    if (babelTransformError) {
153        // Transform errors are thrown from inside the Babel transformer.
154        const [fileName, content, row, column, codeFrame] = babelTransformError.slice(1);
155        return {
156            level: 'syntax',
157            stack: [],
158            isComponentError: false,
159            componentStack: [],
160            codeFrame: {
161                fileName,
162                location: {
163                    row: parseInt(row, 10),
164                    column: parseInt(column, 10),
165                },
166                content: codeFrame,
167            },
168            message: {
169                content,
170                substitutions: [],
171            },
172            category: `${fileName}-${row}-${column}`,
173        };
174    }
175    const babelCodeFrameError = message.match(BABEL_CODE_FRAME_ERROR_FORMAT);
176    if (babelCodeFrameError) {
177        // Codeframe errors are thrown from any use of buildCodeFrameError.
178        const [fileName, content, codeFrame] = babelCodeFrameError.slice(1);
179        return {
180            level: 'syntax',
181            stack: [],
182            isComponentError: false,
183            componentStack: [],
184            codeFrame: {
185                fileName,
186                location: null,
187                content: codeFrame,
188            },
189            message: {
190                content,
191                substitutions: [],
192            },
193            category: `${fileName}-${1}-${1}`,
194        };
195    }
196    if (message.match(/^TransformError /)) {
197        return {
198            level: 'syntax',
199            stack: error.stack,
200            isComponentError: error.isComponentError,
201            componentStack: [],
202            message: {
203                content: message,
204                substitutions: [],
205            },
206            category: message,
207        };
208    }
209    const componentStack = error.componentStack;
210    if (error.isFatal || error.isComponentError) {
211        return {
212            level: 'fatal',
213            stack: error.stack,
214            isComponentError: error.isComponentError,
215            componentStack: componentStack != null ? parseComponentStack(componentStack) : [],
216            ...parseInterpolation([message]),
217        };
218    }
219    if (componentStack != null) {
220        // It is possible that console errors have a componentStack.
221        return {
222            level: 'error',
223            stack: error.stack,
224            isComponentError: error.isComponentError,
225            componentStack: parseComponentStack(componentStack),
226            ...parseInterpolation([message]),
227        };
228    }
229    // Most `console.error` calls won't have a componentStack. We parse them like
230    // regular logs which have the component stack burried in the message.
231    return {
232        level: 'error',
233        stack: error.stack,
234        isComponentError: error.isComponentError,
235        ...parseLogBoxLog([message]),
236    };
237}
238exports.parseLogBoxException = parseLogBoxException;
239function parseLogBoxLog(args) {
240    const message = args[0];
241    let argsWithoutComponentStack = [];
242    let componentStack = [];
243    // Extract component stack from warnings like "Some warning%s".
244    if (typeof message === 'string' && message.slice(-2) === '%s' && args.length > 0) {
245        const lastArg = args[args.length - 1];
246        if (typeof lastArg === 'string' && isComponentStack(lastArg)) {
247            argsWithoutComponentStack = args.slice(0, -1);
248            argsWithoutComponentStack[0] = message.slice(0, -2);
249            componentStack = parseComponentStack(lastArg);
250        }
251    }
252    if (componentStack.length === 0) {
253        // Try finding the component stack elsewhere.
254        for (const arg of args) {
255            if (typeof arg === 'string' && isComponentStack(arg)) {
256                // Strip out any messages before the component stack.
257                let messageEndIndex = arg.search(/\n {4}(in|at) /);
258                if (messageEndIndex < 0) {
259                    // Handle JSC component stacks.
260                    messageEndIndex = arg.search(/\n/);
261                }
262                if (messageEndIndex > 0) {
263                    argsWithoutComponentStack.push(arg.slice(0, messageEndIndex));
264                }
265                componentStack = parseComponentStack(arg);
266            }
267            else {
268                argsWithoutComponentStack.push(arg);
269            }
270        }
271    }
272    return {
273        ...parseInterpolation(argsWithoutComponentStack),
274        componentStack,
275    };
276}
277exports.parseLogBoxLog = parseLogBoxLog;
278//# sourceMappingURL=parseLogBoxLog.js.map