|
1 | 1 | import type { IncomingMessage } from 'node:http'; |
2 | 2 | import type { Socket } from 'node:net'; |
3 | | -import path from 'node:path'; |
4 | | -import { promisify } from 'node:util'; |
5 | | -import { parse as parseStack } from 'stacktrace-parser'; |
6 | 3 | import type Ws from '../../compiled/ws/index.js'; |
7 | | -import { JS_REGEX } from '../constants.js'; |
8 | 4 | import { |
9 | | - color, |
10 | 5 | getAllStatsErrors, |
11 | 6 | getAllStatsWarnings, |
12 | 7 | getStatsOptions, |
13 | 8 | } from '../helpers'; |
14 | 9 | import { formatStatsMessages } from '../helpers/format'; |
15 | 10 | import { logger } from '../logger'; |
16 | | -import type { |
17 | | - DevConfig, |
18 | | - EnvironmentContext, |
19 | | - InternalContext, |
20 | | - Rspack, |
21 | | -} from '../types'; |
22 | | -import { getFileFromUrl } from './assets-middleware/getFileFromUrl.js'; |
23 | | -import type { OutputFileSystem } from './assets-middleware/index.js'; |
| 11 | +import type { DevConfig, InternalContext, Rspack } from '../types'; |
| 12 | +import { reportRuntimeError } from './browserLogs.js'; |
24 | 13 | import { genOverlayHTML } from './overlay'; |
25 | 14 |
|
26 | 15 | interface ExtWebSocket extends Ws { |
@@ -80,108 +69,6 @@ const parseQueryString = (req: IncomingMessage) => { |
80 | 69 | return queryStr ? Object.fromEntries(new URLSearchParams(queryStr)) : {}; |
81 | 70 | }; |
82 | 71 |
|
83 | | -async function mapSourceMapPosition( |
84 | | - rawSourceMap: string, |
85 | | - line: number, |
86 | | - column: number, |
87 | | -) { |
88 | | - const { SourceMapConsumer } = await import( |
89 | | - '../../compiled/source-map/index.js' |
90 | | - ); |
91 | | - const consumer = await new SourceMapConsumer(rawSourceMap); |
92 | | - const originalPosition = consumer.originalPositionFor({ line, column }); |
93 | | - consumer.destroy(); |
94 | | - return originalPosition; |
95 | | -} |
96 | | - |
97 | | -/** |
98 | | - * Resolve source filename and original position from runtime stack trace |
99 | | - */ |
100 | | -const resolveSourceLocation = async ( |
101 | | - stack: string, |
102 | | - fs: Rspack.OutputFileSystem, |
103 | | - environments: Record<string, EnvironmentContext>, |
104 | | -) => { |
105 | | - const parsed = parseStack(stack); |
106 | | - if (!parsed.length) { |
107 | | - return; |
108 | | - } |
109 | | - |
110 | | - // only parse JS files |
111 | | - const { file, column, lineNumber } = parsed[0]; |
112 | | - if ( |
113 | | - file === null || |
114 | | - column === null || |
115 | | - lineNumber === null || |
116 | | - !JS_REGEX.test(file) |
117 | | - ) { |
118 | | - return; |
119 | | - } |
120 | | - |
121 | | - const sourceMapInfo = getFileFromUrl( |
122 | | - `${file}.map`, |
123 | | - fs as OutputFileSystem, |
124 | | - environments, |
125 | | - ); |
126 | | - |
127 | | - if (!sourceMapInfo || 'errorCode' in sourceMapInfo) { |
128 | | - return; |
129 | | - } |
130 | | - |
131 | | - const readFile = promisify(fs.readFile); |
132 | | - try { |
133 | | - const sourceMap = await readFile(sourceMapInfo.filename); |
134 | | - if (sourceMap) { |
135 | | - return await mapSourceMapPosition( |
136 | | - sourceMap.toString(), |
137 | | - lineNumber, |
138 | | - column, |
139 | | - ); |
140 | | - } |
141 | | - } catch {} |
142 | | -}; |
143 | | - |
144 | | -const formatErrorLocation = async ( |
145 | | - stack: string, |
146 | | - context: InternalContext, |
147 | | - fs: Rspack.OutputFileSystem, |
148 | | -) => { |
149 | | - const parsed = await resolveSourceLocation(stack, fs, context.environments); |
150 | | - |
151 | | - if (!parsed) { |
152 | | - return; |
153 | | - } |
154 | | - |
155 | | - const { source, line, column } = parsed; |
156 | | - if (!source) { |
157 | | - return; |
158 | | - } |
159 | | - |
160 | | - let rawLocation = path.relative(context.rootPath, source); |
161 | | - if (line !== null) { |
162 | | - rawLocation += column === null ? `:${line}` : `:${line}:${column}`; |
163 | | - } |
164 | | - return rawLocation; |
165 | | -}; |
166 | | - |
167 | | -/** |
168 | | - * Handle runtime errors from browser |
169 | | - */ |
170 | | -const onRuntimeError = async ( |
171 | | - message: ClientMessageRuntimeError, |
172 | | - context: InternalContext, |
173 | | - fs: Rspack.OutputFileSystem, |
174 | | -) => { |
175 | | - let log = `${color.cyan('[browser]')} ${color.red(message.message)}`; |
176 | | - |
177 | | - if (message.stack) { |
178 | | - const rawLocation = await formatErrorLocation(message.stack, context, fs); |
179 | | - log += color.dim(` (${rawLocation})`); |
180 | | - } |
181 | | - |
182 | | - logger.error(log); |
183 | | -}; |
184 | | - |
185 | 72 | export class SocketServer { |
186 | 73 | private wsServer!: Ws.Server; |
187 | 74 |
|
@@ -385,7 +272,7 @@ export class SocketServer { |
385 | 272 | ); |
386 | 273 |
|
387 | 274 | if (message.type === 'runtime-error') { |
388 | | - onRuntimeError(message, this.context, this.getOutputFileSystem()); |
| 275 | + reportRuntimeError(message, this.context, this.getOutputFileSystem()); |
389 | 276 | } |
390 | 277 | } catch {} |
391 | 278 | }); |
|
0 commit comments