Skip to content

Commit 202abcd

Browse files
committed
feat: conditionally load node dependencies and return empty if they can't be loaded
1 parent 3f02326 commit 202abcd

File tree

2 files changed

+71
-17
lines changed

2 files changed

+71
-17
lines changed

src/__tests__/get-user-trace.js

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
1+
import fs from 'fs'
12
import {getUserTrace} from '../get-user-trace'
23

34
jest.mock('fs', () => ({
4-
readFileSync: () => `
5+
readFileSync: jest.fn(
6+
() => `
57
import { screen } from '@testing-library/dom'
68
79
it('renders', () => {
810
document.body.appendChild(
911
document.createTextNode('Hello world')
1012
)
1113
screen.debug()
12-
13-
14+
15+
1416
expect(screen.getByText('Hello world')).toBeInTheDocument()
1517
})
1618
`,
19+
),
1720
}))
1821

1922
let globalErrorMock
@@ -65,3 +68,25 @@ test('it returns only client error when node frames are present afterwards', ()
6568
"
6669
`)
6770
})
71+
72+
test("it returns empty string if file from frame can't be read", () => {
73+
const consoleWarnSpy = jest
74+
.spyOn(global.console, 'warn')
75+
.mockImplementationOnce(jest.fn)
76+
77+
// Make fire read purposely fail
78+
fs.readFileSync.mockImplementationOnce(() => {
79+
throw Error()
80+
})
81+
const filePath = '/home/john/projects/sample-error/error-example.js'
82+
const stack = `Error: Kaboom
83+
at somethingWrong (${filePath}:8:7)
84+
`
85+
globalErrorMock.mockImplementationOnce(() => ({stack}))
86+
87+
expect(getUserTrace(stack)).toEqual('')
88+
expect(consoleWarnSpy).toHaveBeenCalledTimes(1)
89+
expect(consoleWarnSpy).toHaveBeenCalledWith(
90+
`Couldn't read file ${filePath} for displaying the code frame`,
91+
)
92+
})

src/get-user-trace.js

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,65 @@
1-
import fs from 'fs'
2-
import { codeFrameColumns } from '@babel/code-frame';
1+
// We try to load node dependencies
2+
let readFileSync = null
3+
let codeFrameColumns = null
34

4-
// Frame has the form "at myMethod (location/to/my/file.js:10:2)"
5+
try {
6+
const nodeRequire = module && module.require
7+
readFileSync = nodeRequire.call(module, 'fs').readFileSync
8+
codeFrameColumns = nodeRequire.call(module, '@babel/code-frame')
9+
.codeFrameColumns
10+
} catch {
11+
// We're in a browser environment
12+
console.warn(
13+
'Printing the user trace is not supported in a browser environment',
14+
)
15+
}
16+
17+
// frame has the form "at myMethod (location/to/my/file.js:10:2)"
518
function getCodeFrame(frame) {
619
const locationStart = frame.indexOf('(') + 1
720
const locationEnd = frame.indexOf(')')
821
const frameLocation = frame.slice(locationStart, locationEnd)
922

10-
const frameLocationElements = frameLocation.split(":")
11-
const [filename, line, column] = [frameLocationElements[0], parseInt(frameLocationElements[1], 10), parseInt(frameLocationElements[2], 10)]
12-
let rawFileContents = ""
23+
const frameLocationElements = frameLocation.split(':')
24+
const [filename, line, column] = [
25+
frameLocationElements[0],
26+
parseInt(frameLocationElements[1], 10),
27+
parseInt(frameLocationElements[2], 10),
28+
]
29+
30+
let rawFileContents = ''
1331
try {
14-
rawFileContents = fs.readFileSync(filename, 'utf-8')
32+
rawFileContents = readFileSync(filename, 'utf-8')
1533
} catch {
1634
console.warn(`Couldn't read file ${filename} for displaying the code frame`)
35+
return ''
1736
}
18-
return codeFrameColumns(rawFileContents, {
19-
start: { line, column },
20-
}, {
21-
highlightCode: true,
22-
linesBelow: 0,
23-
})
37+
38+
const codeFrame = codeFrameColumns(
39+
rawFileContents,
40+
{
41+
start: {line, column},
42+
},
43+
{
44+
highlightCode: true,
45+
linesBelow: 0,
46+
},
47+
)
48+
return `${codeFrame}\n`
2449
}
2550

2651
function getUserTrace() {
52+
// If we couldn't load dependencies, we can't generate a user trace
53+
if (!readFileSync || !codeFrameColumns) {
54+
return ''
55+
}
2756
const err = new Error()
2857
const firstClientCodeFrame = err.stack
2958
.split('\n')
3059
.slice(1) // Remove first line which has the form "Error: TypeError"
3160
.find(frame => !frame.includes('node_modules/')) // Ignore frames from 3rd party libraries
3261

33-
return `${getCodeFrame(firstClientCodeFrame)}\n`
62+
return getCodeFrame(firstClientCodeFrame)
3463
}
3564

3665
export {getUserTrace}

0 commit comments

Comments
 (0)