1
1
import type { IncomingMessage , ServerResponse } from 'http'
2
- import { findSourcePackage , type OriginalStackFrameResponse } from './shared'
2
+ import {
3
+ badRequest ,
4
+ findSourcePackage ,
5
+ getOriginalCodeFrame ,
6
+ internalServerError ,
7
+ json ,
8
+ noContent ,
9
+ type OriginalStackFrameResponse ,
10
+ } from './shared'
3
11
4
12
import fs , { constants as FS } from 'fs/promises'
5
- import { codeFrameColumns } from 'next/dist/compiled/babel/code-frame'
6
13
import { launchEditor } from '../internal/helpers/launchEditor'
7
-
8
- interface Project {
9
- getSourceForAsset ( filePath : string ) : Promise < string | null >
10
- traceSource (
11
- stackFrame : TurbopackStackFrame
12
- ) : Promise < TurbopackStackFrame | null >
13
- }
14
-
15
- interface TurbopackStackFrame {
16
- // 1-based
17
- column : number | null
18
- // 1-based
19
- file : string
20
- isServer : boolean
21
- line : number | null
22
- methodName : string | null
23
- isInternal ?: boolean
24
- }
14
+ import type { StackFrame } from 'next/dist/compiled/stacktrace-parser'
15
+ import type { Project , TurbopackStackFrame } from '../../../../build/swc'
25
16
26
17
const currentSourcesByFile : Map < string , Promise < string | null > > = new Map ( )
27
18
export async function batchedTraceSource (
28
19
project : Project ,
29
20
frame : TurbopackStackFrame
30
- ) {
21
+ ) : Promise < { frame : StackFrame ; source : string | null } | undefined > {
31
22
const file = frame . file ? decodeURIComponent ( frame . file ) : undefined
32
- if ( ! file ) {
33
- return
34
- }
23
+ if ( ! file ) return
35
24
36
25
const sourceFrame = await project . traceSource ( frame )
37
-
38
- if ( ! sourceFrame ) {
39
- return
40
- }
41
-
42
- let source
43
- // Don't show code frames for node_modules. These can also often be large bundled files.
44
- if ( ! sourceFrame . file . includes ( 'node_modules' ) && ! sourceFrame . isInternal ) {
26
+ if ( ! sourceFrame ) return
27
+
28
+ let source = null
29
+ // Don't look up source for node_modules or internals. These can often be large bundled files.
30
+ if (
31
+ sourceFrame . file &&
32
+ ! ( sourceFrame . file . includes ( 'node_modules' ) || sourceFrame . isInternal )
33
+ ) {
45
34
let sourcePromise = currentSourcesByFile . get ( sourceFrame . file )
46
35
if ( ! sourcePromise ) {
47
36
sourcePromise = project . getSourceForAsset ( sourceFrame . file )
48
37
currentSourcesByFile . set ( sourceFrame . file , sourcePromise )
49
38
setTimeout ( ( ) => {
50
39
// Cache file reads for 100ms, as frames will often reference the same
51
40
// files and can be large.
52
- currentSourcesByFile . delete ( sourceFrame . file )
41
+ currentSourcesByFile . delete ( sourceFrame . file ! )
53
42
} , 100 )
54
43
}
55
44
@@ -59,12 +48,12 @@ export async function batchedTraceSource(
59
48
return {
60
49
frame : {
61
50
file : sourceFrame . file ,
62
- lineNumber : sourceFrame . line ,
63
- column : sourceFrame . column ,
51
+ lineNumber : sourceFrame . line ?? 0 ,
52
+ column : sourceFrame . column ?? 0 ,
64
53
methodName : sourceFrame . methodName ?? frame . methodName ?? '<unknown>' ,
65
54
arguments : [ ] ,
66
55
} ,
67
- source : source ?? null ,
56
+ source,
68
57
}
69
58
}
70
59
@@ -74,29 +63,15 @@ export async function createOriginalStackFrame(
74
63
) : Promise < OriginalStackFrameResponse | null > {
75
64
const traced = await batchedTraceSource ( project , frame )
76
65
if ( ! traced ) {
77
- const sourcePackage = findSourcePackage ( frame . file )
66
+ const sourcePackage = findSourcePackage ( frame )
78
67
if ( sourcePackage ) return { sourcePackage }
79
68
return null
80
69
}
81
70
82
71
return {
83
72
originalStackFrame : traced . frame ,
84
- originalCodeFrame :
85
- traced . source === null
86
- ? null
87
- : codeFrameColumns (
88
- traced . source ,
89
- {
90
- start : {
91
- // 1-based, but -1 means start line without highlighting
92
- line : traced . frame . lineNumber ?? - 1 ,
93
- // 1-based, but 0 means whole line without column highlighting
94
- column : traced . frame . column ?? 0 ,
95
- } ,
96
- } ,
97
- { forceColor : true }
98
- ) ,
99
- sourcePackage : findSourcePackage ( traced . frame . file ) ,
73
+ originalCodeFrame : getOriginalCodeFrame ( traced . frame , traced . source ) ,
74
+ sourcePackage : findSourcePackage ( traced . frame ) ,
100
75
}
101
76
}
102
77
@@ -106,7 +81,7 @@ export function getOverlayMiddleware(project: Project) {
106
81
107
82
const frame = {
108
83
file : searchParams . get ( 'file' ) as string ,
109
- methodName : searchParams . get ( 'methodName' ) ,
84
+ methodName : searchParams . get ( 'methodName' ) ?? '<unknown>' ,
110
85
line : parseInt ( searchParams . get ( 'lineNumber' ) ?? '0' , 10 ) || 0 ,
111
86
column : parseInt ( searchParams . get ( 'column' ) ?? '0' , 10 ) || 0 ,
112
87
isServer : searchParams . get ( 'isServer' ) === 'true' ,
@@ -117,55 +92,32 @@ export function getOverlayMiddleware(project: Project) {
117
92
try {
118
93
originalStackFrame = await createOriginalStackFrame ( project , frame )
119
94
} catch ( e : any ) {
120
- res . statusCode = 500
121
- res . write ( e . message )
122
- res . end ( )
123
- return
95
+ return internalServerError ( res , e . message )
124
96
}
125
97
126
- if ( originalStackFrame === null ) {
98
+ if ( ! originalStackFrame ) {
127
99
res . statusCode = 404
128
- res . write ( 'Unable to resolve sourcemap' )
129
- res . end ( )
130
- return
100
+ return res . end ( 'Unable to resolve sourcemap' )
131
101
}
132
102
133
- res . statusCode = 200
134
- res . setHeader ( 'Content-Type' , 'application/json' )
135
- res . write ( Buffer . from ( JSON . stringify ( originalStackFrame ) ) )
136
- res . end ( )
137
- return
103
+ return json ( res , originalStackFrame )
138
104
} else if ( pathname === '/__nextjs_launch-editor' ) {
139
- if ( ! frame . file ) {
140
- res . statusCode = 400
141
- res . write ( 'Bad Request' )
142
- res . end ( )
143
- return
144
- }
105
+ if ( ! frame . file ) return badRequest ( res )
145
106
146
107
const fileExists = await fs . access ( frame . file , FS . F_OK ) . then (
147
108
( ) => true ,
148
109
( ) => false
149
110
)
150
- if ( ! fileExists ) {
151
- res . statusCode = 204
152
- res . write ( 'No Content' )
153
- res . end ( )
154
- return
155
- }
111
+ if ( ! fileExists ) return noContent ( res )
156
112
157
113
try {
158
114
launchEditor ( frame . file , frame . line ?? 1 , frame . column ?? 1 )
159
115
} catch ( err ) {
160
116
console . log ( 'Failed to launch editor:' , err )
161
- res . statusCode = 500
162
- res . write ( 'Internal Server Error' )
163
- res . end ( )
164
- return
117
+ return internalServerError ( res )
165
118
}
166
119
167
- res . statusCode = 204
168
- res . end ( )
120
+ noContent ( res )
169
121
}
170
122
}
171
123
}
0 commit comments