@@ -17,6 +17,8 @@ const app = express();
17
17
const compress = require ( 'compression' ) ;
18
18
const { Readable} = require ( 'node:stream' ) ;
19
19
20
+ const nodeModule = require ( 'node:module' ) ;
21
+
20
22
app . use ( compress ( ) ) ;
21
23
22
24
// Application
@@ -116,6 +118,88 @@ app.get('/todos', function (req, res) {
116
118
] ) ;
117
119
} ) ;
118
120
121
+ if ( process . env . NODE_ENV === 'development' ) {
122
+ const rootDir = path . resolve ( __dirname , '../' ) ;
123
+
124
+ app . get ( '/source-maps' , async function ( req , res , next ) {
125
+ try {
126
+ res . set ( 'Content-type' , 'application/json' ) ;
127
+ let requestedFilePath = req . query . name ;
128
+
129
+ let isCompiledOutput = false ;
130
+ if ( requestedFilePath . startsWith ( 'file://' ) ) {
131
+ // We assume that if it was prefixed with file:// it's referring to the compiled output
132
+ // and if it's a direct file path we assume it's source mapped back to original format.
133
+ isCompiledOutput = true ;
134
+ requestedFilePath = url . fileURLToPath ( requestedFilePath ) ;
135
+ }
136
+
137
+ const relativePath = path . relative ( rootDir , requestedFilePath ) ;
138
+ if ( relativePath . startsWith ( '..' ) || path . isAbsolute ( relativePath ) ) {
139
+ // This is outside the root directory of the app. Forbid it to be served.
140
+ res . status = 403 ;
141
+ res . write ( '{}' ) ;
142
+ res . end ( ) ;
143
+ return ;
144
+ }
145
+
146
+ const sourceMap = nodeModule . findSourceMap ( requestedFilePath ) ;
147
+ let map ;
148
+ if ( requestedFilePath . startsWith ( 'node:' ) ) {
149
+ // This is a node internal. We don't include any source code for this but we still
150
+ // generate a source map for it so that we can add it to an ignoreList automatically.
151
+ map = {
152
+ version : 3 ,
153
+ // We use the node:// protocol convention to teach Chrome DevTools that this is
154
+ // on a different protocol and not part of the current page.
155
+ sources : [ 'node:///' + requestedFilePath . slice ( 5 ) ] ,
156
+ sourcesContent : [ '// Node Internals' ] ,
157
+ mappings : 'AAAA' ,
158
+ ignoreList : [ 0 ] ,
159
+ sourceRoot : '' ,
160
+ } ;
161
+ } else if ( ! sourceMap || ! isCompiledOutput ) {
162
+ // If a file doesn't have a source map, such as this file, then we generate a blank
163
+ // source map that just contains the original content and segments pointing to the
164
+ // original lines. If a line number points to uncompiled output, like if source mapping
165
+ // was already applied we also use this path.
166
+ const sourceContent = await readFile ( requestedFilePath , 'utf8' ) ;
167
+ const lines = sourceContent . split ( '\n' ) . length ;
168
+ // We ensure to absolute
169
+ const sourceURL = url . pathToFileURL ( requestedFilePath ) ;
170
+ map = {
171
+ version : 3 ,
172
+ sources : [ sourceURL ] ,
173
+ sourcesContent : [ sourceContent ] ,
174
+ // Note: This approach to mapping each line only lets you jump to each line
175
+ // not jump to a column within a line. To do that, you need a proper source map
176
+ // generated for each parsed segment or add a segment for each column.
177
+ mappings : 'AAAA' + ';AACA' . repeat ( lines - 1 ) ,
178
+ sourceRoot : '' ,
179
+ // Add any node_modules to the ignore list automatically.
180
+ ignoreList : requestedFilePath . includes ( 'node_modules' )
181
+ ? [ 0 ]
182
+ : undefined ,
183
+ } ;
184
+ } else {
185
+ // We always set prepareStackTrace before reading the stack so that we get the stack
186
+ // without source maps applied. Therefore we have to use the original source map.
187
+ // If something read .stack before we did, we might observe the line/column after
188
+ // source mapping back to the original file. We use the isCompiledOutput check above
189
+ // in that case.
190
+ map = sourceMap . payload ;
191
+ }
192
+ res . write ( JSON . stringify ( map ) ) ;
193
+ res . end ( ) ;
194
+ } catch ( x ) {
195
+ res . status = 500 ;
196
+ res . write ( '{}' ) ;
197
+ res . end ( ) ;
198
+ console . error ( x ) ;
199
+ }
200
+ } ) ;
201
+ }
202
+
119
203
app . listen ( 3001 , ( ) => {
120
204
console . log ( 'Regional Flight Server listening on port 3001...' ) ;
121
205
} ) ;
0 commit comments