@@ -90,6 +90,7 @@ export function createAngularMemoryPlugin(options: AngularMemoryPluginOptions):
90
90
map : mapContents && Buffer . from ( mapContents ) . toString ( 'utf-8' ) ,
91
91
} ;
92
92
} ,
93
+ // eslint-disable-next-line max-lines-per-function
93
94
configureServer ( server ) {
94
95
const originalssrTransform = server . ssrTransform ;
95
96
server . ssrTransform = async ( code , map , url , originalCode ) => {
@@ -122,6 +123,11 @@ export function createAngularMemoryPlugin(options: AngularMemoryPluginOptions):
122
123
// The base of the URL is unused but required to parse the URL.
123
124
const pathname = pathnameWithoutBasePath ( req . url , server . config . base ) ;
124
125
const extension = extname ( pathname ) ;
126
+ if ( ! extension ) {
127
+ next ( ) ;
128
+
129
+ return ;
130
+ }
125
131
126
132
// Rewrite all build assets to a vite raw fs URL
127
133
const assetSourcePath = assets . get ( pathname ) ;
@@ -169,6 +175,8 @@ export function createAngularMemoryPlugin(options: AngularMemoryPluginOptions):
169
175
// Returning a function, installs middleware after the main transform middleware but
170
176
// before the built-in HTML middleware
171
177
return ( ) => {
178
+ server . middlewares . use ( angularHtmlFallbackMiddleware ) ;
179
+
172
180
function angularSSRMiddleware (
173
181
req : Connect . IncomingMessage ,
174
182
res : ServerResponse ,
@@ -180,8 +188,8 @@ export function createAngularMemoryPlugin(options: AngularMemoryPluginOptions):
180
188
// Skip if path is not defined.
181
189
! url ||
182
190
// Skip if path is like a file.
183
- // NOTE: We use a regexp to mitigate against matching requests like: /browse/pl.0ef59752c0cd457dbf1391f08cbd936f
184
- / ^ \. [ a - z ] { 2 , 4 } $ / i . test ( extname ( url . split ( '?' ) [ 0 ] ) )
191
+ // NOTE: We use a mime type lookup to mitigate against matching requests like: /browse/pl.0ef59752c0cd457dbf1391f08cbd936f
192
+ lookupMimeTypeFromRequest ( req )
185
193
) {
186
194
next ( ) ;
187
195
@@ -306,3 +314,42 @@ function pathnameWithoutBasePath(url: string, basePath: string): string {
306
314
? pathname . slice ( basePath . length - 1 )
307
315
: pathname ;
308
316
}
317
+
318
+ function angularHtmlFallbackMiddleware (
319
+ req : Connect . IncomingMessage ,
320
+ res : ServerResponse ,
321
+ next : Connect . NextFunction ,
322
+ ) : void {
323
+ // Similar to how it is handled in vite
324
+ // https://github.com/vitejs/vite/blob/main/packages/vite/src/node/server/middlewares/htmlFallback.ts#L15C19-L15C45
325
+ if (
326
+ // Only accept GET or HEAD
327
+ ( req . method !== 'GET' && req . method !== 'HEAD' ) ||
328
+ // Has file extensions
329
+ lookupMimeTypeFromRequest ( req ) ||
330
+ // Require Accept: text/html or */*
331
+ ! (
332
+ req . headers . accept === undefined || // equivalent to `Accept: */*`
333
+ req . headers . accept === '' || // equivalent to `Accept: */*`
334
+ req . headers . accept . includes ( 'text/html' ) ||
335
+ req . headers . accept . includes ( '*/*' )
336
+ )
337
+ ) {
338
+ next ( ) ;
339
+
340
+ return ;
341
+ }
342
+
343
+ req . url = '/index.html' ;
344
+ next ( ) ;
345
+ }
346
+
347
+ function lookupMimeTypeFromRequest ( req : Connect . IncomingMessage ) : string | undefined {
348
+ const extension = req . originalUrl && extname ( req . originalUrl . split ( '?' ) [ 0 ] ) ;
349
+
350
+ if ( extension === '.ico' ) {
351
+ return 'image/x-icon' ;
352
+ }
353
+
354
+ return extension && lookupMimeType ( extension ) ;
355
+ }
0 commit comments