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