Skip to content

Commit 48385b9

Browse files
Skip for web
1 parent af94b52 commit 48385b9

File tree

3 files changed

+134
-130
lines changed

3 files changed

+134
-130
lines changed

src/impl.tsx

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import symbolicateStackTrace from 'react-native/Libraries/Core/Devtools/symbolicateStackTrace';
2+
import parseErrorStack from 'react-native/Libraries/Core/Devtools/parseErrorStack';
3+
4+
type ConsoleLogFunction = (...args: unknown[]) => void;
5+
6+
/**
7+
* Installs a console symbolicator that will symbolicate stack traces in console
8+
* logs, making it easier to debug errors in React Native applications.
9+
*
10+
* This function should be called at the start of your application, typically
11+
* in the entry file (e.g., `_layout.tsx` or `App.tsx`).
12+
*
13+
* @param {Object} options - Configuration options for the symbolicator.
14+
* @param {boolean} [options.excludeReactNativeCoreFrames=false] - If set to `true`,
15+
* the symbolicator will drop frames from `node_modules/react-native`
16+
* package. This helps readability of the stack trace, but information
17+
* about the internal React Native Core stack is lost.
18+
*
19+
* @example
20+
* installConsoleSymbolicator({ excludeReactNativeCoreFrames: true });
21+
*/
22+
export const installConsoleSymbolicator = ({
23+
excludeReactNativeCoreFrames = false,
24+
}: {
25+
/**
26+
* If set to `true`,
27+
* the symbolicator will drop frames from `node_modules/react-native`
28+
* package. This helps readability of the stack trace, but information
29+
* about the internal React Native Core stack is lost.
30+
*
31+
* @default false
32+
*
33+
* @example
34+
* installConsoleSymbolicator({ excludeReactNativeCoreFrames: true });
35+
*/
36+
excludeReactNativeCoreFrames?: boolean;
37+
} = {}) => {
38+
if (!__DEV__) {
39+
return;
40+
}
41+
42+
const originalConsole = {
43+
error: console.error,
44+
warn: console.warn,
45+
log: console.log,
46+
info: console.info,
47+
debug: console.debug,
48+
assert: console.assert,
49+
};
50+
51+
const makeSymbolicatedConsole = (fn: ConsoleLogFunction) =>
52+
symbolicatedConsole(fn, {
53+
logError: originalConsole.error,
54+
excludeReactNativeCoreFrames,
55+
});
56+
57+
console.error = makeSymbolicatedConsole(originalConsole.error);
58+
console.warn = makeSymbolicatedConsole(originalConsole.warn);
59+
console.log = makeSymbolicatedConsole(originalConsole.log);
60+
console.info = makeSymbolicatedConsole(originalConsole.info);
61+
console.debug = makeSymbolicatedConsole(originalConsole.debug);
62+
console.assert = makeSymbolicatedConsole(
63+
originalConsole.assert as ConsoleLogFunction
64+
);
65+
};
66+
67+
const symbolicatedConsole =
68+
(
69+
original: ConsoleLogFunction,
70+
{
71+
logError,
72+
excludeReactNativeCoreFrames,
73+
}: {
74+
logError: ConsoleLogFunction;
75+
excludeReactNativeCoreFrames: boolean;
76+
}
77+
) =>
78+
async (...args: unknown[]) => {
79+
const symbolicating: Array<Promise<unknown>> = args.map(async (arg) => {
80+
if (!(arg instanceof Error) || !arg.stack) {
81+
return arg;
82+
}
83+
84+
// Symbolication works only with the dev server running
85+
try {
86+
// @ts-ignore = the TS types are broken, the function returns object with `stack` property and a code snippet
87+
const { stack: frames } = await symbolicateStackTrace(
88+
// @ts-ignore - the TS types are broken, the function requires the stack string not the error object
89+
parseErrorStack(arg.stack)
90+
);
91+
92+
if (!frames || frames.length === 0) {
93+
return arg;
94+
}
95+
96+
const newStack = frames
97+
.map(
98+
(frame: {
99+
// FIXME: (@krystofwoldrich) Remove when RN types are fixed
100+
methodName: string;
101+
file: string | null | undefined;
102+
lineNumber: number | null | undefined;
103+
column: number | null | undefined;
104+
}) => {
105+
const fileInfo =
106+
frame.file != null && frame.file.length > 0
107+
? `${frame.file}:${frame.lineNumber || 0}:${frame.column || 0}`
108+
: 'unknown';
109+
return ` at ${frame.methodName} (${fileInfo})`;
110+
}
111+
)
112+
.filter(
113+
(line: string) =>
114+
!excludeReactNativeCoreFrames ||
115+
!line.includes('node_modules/react-native')
116+
)
117+
.join('\n');
118+
119+
arg.stack = arg.message ? `${arg.message}\n${newStack}` : newStack;
120+
} catch (Oo) {
121+
logError('Error during symbolication:', Oo);
122+
}
123+
124+
return arg;
125+
});
126+
127+
const symbolicated = await Promise.all(symbolicating);
128+
129+
original(...symbolicated);
130+
};

src/impl.web.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const installConsoleSymbolicator = () => {
2+
// no-op for web
3+
};

src/index.tsx

Lines changed: 1 addition & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,130 +1 @@
1-
import symbolicateStackTrace from 'react-native/Libraries/Core/Devtools/symbolicateStackTrace';
2-
import parseErrorStack from 'react-native/Libraries/Core/Devtools/parseErrorStack';
3-
4-
type ConsoleLogFunction = (...args: unknown[]) => void;
5-
6-
/**
7-
* Installs a console symbolicator that will symbolicate stack traces in console
8-
* logs, making it easier to debug errors in React Native applications.
9-
*
10-
* This function should be called at the start of your application, typically
11-
* in the entry file (e.g., `_layout.tsx` or `App.tsx`).
12-
*
13-
* @param {Object} options - Configuration options for the symbolicator.
14-
* @param {boolean} [options.excludeReactNativeCoreFrames=false] - If set to `true`,
15-
* the symbolicator will drop frames from `node_modules/react-native`
16-
* package. This helps readability of the stack trace, but information
17-
* about the internal React Native Core stack is lost.
18-
*
19-
* @example
20-
* installConsoleSymbolicator({ excludeReactNativeCoreFrames: true });
21-
*/
22-
export const installConsoleSymbolicator = ({
23-
excludeReactNativeCoreFrames = false,
24-
}: {
25-
/**
26-
* If set to `true`,
27-
* the symbolicator will drop frames from `node_modules/react-native`
28-
* package. This helps readability of the stack trace, but information
29-
* about the internal React Native Core stack is lost.
30-
*
31-
* @default false
32-
*
33-
* @example
34-
* installConsoleSymbolicator({ excludeReactNativeCoreFrames: true });
35-
*/
36-
excludeReactNativeCoreFrames?: boolean;
37-
} = {}) => {
38-
if (!__DEV__) {
39-
return;
40-
}
41-
42-
const originalConsole = {
43-
error: console.error,
44-
warn: console.warn,
45-
log: console.log,
46-
info: console.info,
47-
debug: console.debug,
48-
assert: console.assert,
49-
};
50-
51-
const makeSymbolicatedConsole = (fn: ConsoleLogFunction) =>
52-
symbolicatedConsole(fn, {
53-
logError: originalConsole.error,
54-
excludeReactNativeCoreFrames,
55-
});
56-
57-
console.error = makeSymbolicatedConsole(originalConsole.error);
58-
console.warn = makeSymbolicatedConsole(originalConsole.warn);
59-
console.log = makeSymbolicatedConsole(originalConsole.log);
60-
console.info = makeSymbolicatedConsole(originalConsole.info);
61-
console.debug = makeSymbolicatedConsole(originalConsole.debug);
62-
console.assert = makeSymbolicatedConsole(
63-
originalConsole.assert as ConsoleLogFunction
64-
);
65-
};
66-
67-
const symbolicatedConsole =
68-
(
69-
original: ConsoleLogFunction,
70-
{
71-
logError,
72-
excludeReactNativeCoreFrames,
73-
}: {
74-
logError: ConsoleLogFunction;
75-
excludeReactNativeCoreFrames: boolean;
76-
}
77-
) =>
78-
async (...args: unknown[]) => {
79-
const symbolicating: Array<Promise<unknown>> = args.map(async (arg) => {
80-
if (!(arg instanceof Error) || !arg.stack) {
81-
return arg;
82-
}
83-
84-
// Symbolication works only with the dev server running
85-
try {
86-
// @ts-ignore = the TS types are broken, the function returns object with `stack` property and a code snippet
87-
const { stack: frames } = await symbolicateStackTrace(
88-
// @ts-ignore - the TS types are broken, the function requires the stack string not the error object
89-
parseErrorStack(arg.stack)
90-
);
91-
92-
if (!frames || frames.length === 0) {
93-
return arg;
94-
}
95-
96-
const newStack = frames
97-
.map(
98-
(frame: {
99-
// FIXME: (@krystofwoldrich) Remove when RN types are fixed
100-
methodName: string;
101-
file: string | null | undefined;
102-
lineNumber: number | null | undefined;
103-
column: number | null | undefined;
104-
}) => {
105-
const fileInfo =
106-
frame.file != null && frame.file.length > 0
107-
? `${frame.file}:${frame.lineNumber || 0}:${frame.column || 0}`
108-
: 'unknown';
109-
return ` at ${frame.methodName} (${fileInfo})`;
110-
}
111-
)
112-
.filter(
113-
(line: string) =>
114-
!excludeReactNativeCoreFrames ||
115-
!line.includes('node_modules/react-native')
116-
)
117-
.join('\n');
118-
119-
arg.stack = arg.message ? `${arg.message}\n${newStack}` : newStack;
120-
} catch (Oo) {
121-
logError('Error during symbolication:', Oo);
122-
}
123-
124-
return arg;
125-
});
126-
127-
const symbolicated = await Promise.all(symbolicating);
128-
129-
original(...symbolicated);
130-
};
1+
export { installConsoleSymbolicator } from './impl';

0 commit comments

Comments
 (0)