/
stack-trace-utils.ts
143 lines (126 loc) · 3.4 KB
/
stack-trace-utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import stackTrace, { StackFrame } from "stack-trace"
import { codeFrameColumns } from "@babel/code-frame"
import {
NullableMappedPosition,
SourceMapConsumer,
RawSourceMap,
RawIndexMap,
} from "source-map"
const fs = require(`fs-extra`)
const path = require(`path`)
const chalk = require(`chalk`)
const { isNodeInternalModulePath } = require(`gatsby-core-utils`)
const getDirName = (arg: unknown): string => {
// Caveat related to executing in engines:
// After webpack bundling we would get number here (webpack module id) and that would crash when doing
// path.dirname(number).
if (typeof arg === `string`) {
return path.dirname(arg)
}
return `-cant-resolve-`
}
const gatsbyLocation = getDirName(require.resolve(`gatsby/package.json`))
const reduxThunkLocation = getDirName(
require.resolve(`redux-thunk/package.json`)
)
const reduxLocation = getDirName(require.resolve(`redux/package.json`))
const getNonGatsbyCallSite = (): StackFrame | undefined =>
stackTrace
.get()
.find(
callSite =>
callSite &&
callSite.getFileName() &&
!callSite.getFileName().includes(gatsbyLocation) &&
!callSite.getFileName().includes(reduxLocation) &&
!callSite.getFileName().includes(reduxThunkLocation) &&
!isNodeInternalModulePath(callSite.getFileName())
)
interface ICodeFrame {
fileName: string
line: number
column: number
codeFrame: string
}
export const getNonGatsbyCodeFrame = ({
highlightCode = true,
stack,
}: {
highlightCode?: boolean
stack?: string
} = {}): null | ICodeFrame => {
let callSite
if (stack) {
callSite = stackTrace.parse({ stack, name: ``, message: `` })[0]
} else {
callSite = getNonGatsbyCallSite()
}
if (!callSite) {
return null
}
const fileName = callSite.getFileName()
const line = callSite.getLineNumber()
const column = callSite.getColumnNumber()
const code = fs.readFileSync(fileName, { encoding: `utf-8` })
return {
fileName,
line,
column,
codeFrame: codeFrameColumns(
code,
{
start: {
line,
column,
},
},
{
highlightCode,
}
),
}
}
export const getNonGatsbyCodeFrameFormatted = ({
highlightCode = true,
stack,
}: {
highlightCode?: boolean
stack?: string
} = {}): null | string => {
const possibleCodeFrame = getNonGatsbyCodeFrame({
highlightCode,
stack,
})
if (!possibleCodeFrame) {
return null
}
const { fileName, line, column, codeFrame } = possibleCodeFrame
return `File ${chalk.bold(`${fileName}:${line}:${column}`)}\n${codeFrame}`
}
interface IOriginalSourcePositionAndContent {
sourcePosition: NullableMappedPosition | null
sourceContent: string | null
}
export async function findOriginalSourcePositionAndContent(
webpackSource: RawSourceMap | RawIndexMap | string,
position: { line: number; column: number | null }
): Promise<IOriginalSourcePositionAndContent> {
return await SourceMapConsumer.with(webpackSource, null, consumer => {
const sourcePosition = consumer.originalPositionFor({
line: position.line,
column: position.column ?? 0,
})
if (!sourcePosition.source) {
return {
sourcePosition: null,
sourceContent: null,
}
}
const sourceContent: string | null =
consumer.sourceContentFor(sourcePosition.source, true) ?? null
return {
sourcePosition,
sourceContent,
}
})
}