8
8
9
9
import type { BuilderContext } from '@angular-devkit/architect' ;
10
10
import type { json } from '@angular-devkit/core' ;
11
+ import { lookup as lookupMimeType } from 'mrmime' ;
11
12
import assert from 'node:assert' ;
12
13
import { BinaryLike , createHash } from 'node:crypto' ;
13
14
import { readFile } from 'node:fs/promises' ;
@@ -21,7 +22,7 @@ import type { NormalizedDevServerOptions } from './options';
21
22
import type { DevServerBuilderOutput } from './webpack-server' ;
22
23
23
24
interface OutputFileRecord {
24
- text : string ;
25
+ contents : Uint8Array ;
25
26
size : number ;
26
27
hash ?: Buffer ;
27
28
updated : boolean ;
@@ -69,7 +70,7 @@ export async function* serveWithVite(
69
70
// Skip analysis of sourcemaps
70
71
if ( filePath . endsWith ( '.map' ) ) {
71
72
outputFiles . set ( filePath , {
72
- text : file . text ,
73
+ contents : file . contents ,
73
74
size : file . contents . byteLength ,
74
75
updated : false ,
75
76
} ) ;
@@ -82,7 +83,7 @@ export async function* serveWithVite(
82
83
if ( existingRecord && existingRecord . size === file . contents . byteLength ) {
83
84
// Only hash existing file when needed
84
85
if ( existingRecord . hash === undefined ) {
85
- existingRecord . hash = hashContent ( existingRecord . text ) ;
86
+ existingRecord . hash = hashContent ( existingRecord . contents ) ;
86
87
}
87
88
88
89
// Compare against latest result output
@@ -95,7 +96,7 @@ export async function* serveWithVite(
95
96
}
96
97
97
98
outputFiles . set ( filePath , {
98
- text : file . text ,
99
+ contents : file . contents ,
99
100
size : file . contents . byteLength ,
100
101
hash : fileHash ,
101
102
updated : true ,
@@ -213,25 +214,59 @@ async function setupServer(
213
214
} ,
214
215
load ( id ) {
215
216
const [ file ] = id . split ( '?' , 1 ) ;
216
- const code = outputFiles . get ( file ) ?. text ;
217
+ const code = outputFiles . get ( file ) ?. contents ;
218
+ const map = outputFiles . get ( file + '.map' ) ?. contents ;
217
219
218
220
return (
219
221
code && {
220
- code,
221
- map : outputFiles . get ( file + '. map' ) ?. text ,
222
+ code : code && Buffer . from ( code ) . toString ( 'utf-8' ) ,
223
+ map : map && Buffer . from ( map ) . toString ( 'utf-8' ) ,
222
224
}
223
225
) ;
224
226
} ,
225
227
configureServer ( server ) {
226
- // Assets get handled first
228
+ // Assets and resources get handled first
227
229
server . middlewares . use ( function angularAssetsMiddleware ( req , res , next ) {
228
- if ( req . url ) {
229
- // Rewrite all build assets to a vite raw fs URL
230
- const assetSource = assets . get ( req . url ) ;
231
- if ( assetSource !== undefined ) {
232
- req . url = `/@fs/${ assetSource } ` ;
230
+ if ( req . url === undefined || res . writableEnded ) {
231
+ return ;
232
+ }
233
+
234
+ // Parse the incoming request.
235
+ // The base of the URL is unused but required to parse the URL.
236
+ const parsedUrl = new URL ( req . url , 'http://localhost' ) ;
237
+ const extension = path . extname ( parsedUrl . pathname ) ;
238
+
239
+ // Rewrite all build assets to a vite raw fs URL
240
+ const assetSourcePath = assets . get ( parsedUrl . pathname ) ;
241
+ if ( assetSourcePath !== undefined ) {
242
+ req . url = `/@fs/${ assetSourcePath } ` ;
243
+ next ( ) ;
244
+
245
+ return ;
246
+ }
247
+
248
+ // Resource files are handled directly.
249
+ // Global stylesheets (CSS files) are currently considered resources to workaround
250
+ // dev server sourcemap issues with stylesheets.
251
+ if ( extension !== '.js' && extension !== '.html' ) {
252
+ const outputFile = outputFiles . get ( parsedUrl . pathname ) ;
253
+ if ( outputFile ) {
254
+ const mimeType = lookupMimeType ( extension ) ;
255
+ if ( mimeType ) {
256
+ res . setHeader ( 'Content-Type' , mimeType ) ;
257
+ }
258
+ res . setHeader ( 'Cache-Control' , 'no-cache' ) ;
259
+ if ( serverOptions . headers ) {
260
+ Object . entries ( serverOptions . headers ) . forEach ( ( [ name , value ] ) =>
261
+ res . setHeader ( name , value ) ,
262
+ ) ;
263
+ }
264
+ res . end ( outputFile . contents ) ;
265
+
266
+ return ;
233
267
}
234
268
}
269
+
235
270
next ( ) ;
236
271
} ) ;
237
272
@@ -240,10 +275,14 @@ async function setupServer(
240
275
return ( ) =>
241
276
server . middlewares . use ( function angularIndexMiddleware ( req , res , next ) {
242
277
if ( req . url === '/' || req . url === `/index.html` ) {
243
- const rawHtml = outputFiles . get ( '/index.html' ) ?. text ;
278
+ const rawHtml = outputFiles . get ( '/index.html' ) ?. contents ;
244
279
if ( rawHtml ) {
245
280
server
246
- . transformIndexHtml ( req . url , rawHtml , req . originalUrl )
281
+ . transformIndexHtml (
282
+ req . url ,
283
+ Buffer . from ( rawHtml ) . toString ( 'utf-8' ) ,
284
+ req . originalUrl ,
285
+ )
247
286
. then ( ( processedHtml ) => {
248
287
res . setHeader ( 'Content-Type' , 'text/html' ) ;
249
288
res . setHeader ( 'Cache-Control' , 'no-cache' ) ;
0 commit comments