Skip to content

Commit 74df975

Browse files
Add function
1 parent e72a177 commit 74df975

File tree

4 files changed

+14063
-10
lines changed

4 files changed

+14063
-10
lines changed

example/src/App.tsx

Lines changed: 91 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,104 @@
1-
import { Text, View, StyleSheet } from 'react-native';
2-
import { multiply } from 'react-native-console-symbolicator';
1+
import {
2+
View,
3+
StyleSheet,
4+
useColorScheme,
5+
FlatList,
6+
SafeAreaView,
7+
Text,
8+
} from 'react-native';
9+
import { installConsoleSymbolicator } from 'react-native-console-symbolicator';
10+
import Button from './components/Button';
311

4-
const result = multiply(3, 7);
12+
installConsoleSymbolicator();
13+
14+
const message =
15+
'When reading this error in React Native Dev Tools, you should see the original source code location instead of the bundle.';
516

617
export default function App() {
18+
const colorScheme = useColorScheme();
19+
const isDarkMode = colorScheme === 'dark';
20+
721
return (
8-
<View style={styles.container}>
9-
<Text>Result: {result}</Text>
10-
</View>
22+
<SafeAreaView
23+
style={[styles.container, isDarkMode && styles.containerDark]}
24+
>
25+
<View style={styles.header}>
26+
<Text style={[styles.headerText, isDarkMode && styles.headerTextDark]}>
27+
Console Examples
28+
</Text>
29+
</View>
30+
<FlatList
31+
data={[
32+
{
33+
title: 'Throw example error',
34+
action: () => {
35+
throw new Error(message);
36+
},
37+
},
38+
{
39+
title: 'console.error',
40+
action: () => console.error(new Error(message)),
41+
},
42+
{
43+
title: 'console.warn',
44+
action: () => console.warn(new Error(message)),
45+
},
46+
{
47+
title: 'console.log',
48+
action: () => console.log(new Error(message)),
49+
},
50+
{
51+
title: 'console.info',
52+
action: () => console.info(new Error(message)),
53+
},
54+
{
55+
title: 'console.debug',
56+
action: () => console.debug(new Error(message)),
57+
},
58+
{
59+
title: 'console.assert',
60+
action: () =>
61+
console.assert(
62+
false,
63+
'For the example purpose this is always `false`.'
64+
),
65+
},
66+
]}
67+
keyExtractor={(item) => item.title}
68+
renderItem={({ item }) => (
69+
<Button title={item.title} onPress={item.action} />
70+
)}
71+
ItemSeparatorComponent={() => <View style={{ height: 10 }} />}
72+
contentContainerStyle={{ width: '100%', padding: 20 }}
73+
/>
74+
</SafeAreaView>
1175
);
1276
}
1377

1478
const styles = StyleSheet.create({
1579
container: {
80+
backgroundColor: 'white',
1681
flex: 1,
1782
alignItems: 'center',
18-
justifyContent: 'center',
83+
justifyContent: 'flex-start',
84+
},
85+
containerDark: {
86+
backgroundColor: 'black',
87+
},
88+
header: {
89+
width: '100%',
90+
paddingVertical: 16,
91+
paddingHorizontal: 20,
92+
borderBottomWidth: 1,
93+
borderBottomColor: '#e0e0e0',
94+
alignItems: 'center',
95+
},
96+
headerText: {
97+
fontSize: 22,
98+
fontWeight: 'bold',
99+
color: '#333',
100+
},
101+
headerTextDark: {
102+
color: '#f0f0f0',
19103
},
20104
});

example/src/components/Button.tsx

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { Pressable, Text, StyleSheet, useColorScheme } from 'react-native';
2+
3+
interface ButtonProps {
4+
title: string;
5+
onPress: () => void;
6+
}
7+
8+
const Button = ({ title, onPress }: ButtonProps) => {
9+
const colorScheme = useColorScheme();
10+
const isDarkMode = colorScheme === 'dark';
11+
12+
return (
13+
<Pressable
14+
onPress={onPress}
15+
style={({ pressed }) => [
16+
styles.button,
17+
isDarkMode && styles.buttonDark,
18+
pressed && styles.buttonPressed,
19+
]}
20+
>
21+
<Text style={[styles.text, isDarkMode && styles.textDark]}>{title}</Text>
22+
</Pressable>
23+
);
24+
};
25+
26+
const styles = StyleSheet.create({
27+
button: {
28+
borderColor: 'black',
29+
borderWidth: 1,
30+
borderRadius: 8,
31+
paddingVertical: 10,
32+
paddingHorizontal: 20,
33+
alignItems: 'center',
34+
justifyContent: 'center',
35+
},
36+
buttonDark: {
37+
borderColor: 'white',
38+
},
39+
buttonPressed: {
40+
transform: [{ scale: 0.95 }],
41+
},
42+
text: {
43+
color: 'black',
44+
fontSize: 16,
45+
fontWeight: '500',
46+
},
47+
textDark: {
48+
color: 'white',
49+
},
50+
});
51+
52+
export default Button;

src/index.tsx

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,77 @@
1-
export function multiply(a: number, b: number): number {
2-
return a * b;
3-
}
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+
export const installConsoleSymbolicator = () => {
7+
if (!__DEV__) {
8+
return;
9+
}
10+
11+
const originalConsole = {
12+
error: console.error,
13+
warn: console.warn,
14+
log: console.log,
15+
info: console.info,
16+
debug: console.debug,
17+
assert: console.assert,
18+
};
19+
20+
const makeSymbolicatedConsole = (fn: ConsoleLogFunction) =>
21+
symbolicatedConsole(fn, originalConsole.error);
22+
23+
console.error = makeSymbolicatedConsole(originalConsole.error);
24+
console.warn = makeSymbolicatedConsole(originalConsole.warn);
25+
console.log = makeSymbolicatedConsole(originalConsole.log);
26+
console.info = makeSymbolicatedConsole(originalConsole.info);
27+
console.debug = makeSymbolicatedConsole(originalConsole.debug);
28+
console.assert = makeSymbolicatedConsole(
29+
originalConsole.assert as ConsoleLogFunction
30+
);
31+
};
32+
33+
const symbolicatedConsole =
34+
(original: ConsoleLogFunction, logError: ConsoleLogFunction) =>
35+
async (...args: unknown[]) => {
36+
const symbolicating: Array<Promise<unknown>> = args.map(async (arg) => {
37+
if (!(arg instanceof Error) || !arg.stack) {
38+
return arg;
39+
}
40+
41+
// Symbolication works only with the dev server running
42+
try {
43+
// @ts-ignore = the TS types are broken, the function returns object with `stack` property and a code snippet
44+
const { stack: frames } = await symbolicateStackTrace(
45+
// @ts-ignore - the TS types are broken, the function requires the stack string not the error object
46+
parseErrorStack(arg.stack)
47+
);
48+
const newStack = frames
49+
.map(
50+
(frame: {
51+
// FIXME: (@krystofwoldrich) Remove when RN types are fixed
52+
methodName: string;
53+
file: string | null | undefined;
54+
lineNumber: number | null | undefined;
55+
column: number | null | undefined;
56+
}) => {
57+
const fileInfo =
58+
frame.file != null && frame.file.length > 0
59+
? `${frame.file}:${frame.lineNumber || 0}:${frame.column || 0}`
60+
: 'unknown';
61+
return ` at ${frame.methodName} (${fileInfo})`;
62+
}
63+
)
64+
.join('\n');
65+
66+
arg.stack = arg.message ? `${arg.message}\n${newStack}` : newStack;
67+
} catch (Oo) {
68+
logError('Error during symbolication:', Oo);
69+
}
70+
71+
return arg;
72+
});
73+
74+
const symbolicated = await Promise.all(symbolicating);
75+
76+
original(...symbolicated);
77+
};

0 commit comments

Comments
 (0)