@@ -17,6 +17,11 @@ import type { Connect, DepOptimizationConfig, InlineConfig, ViteDevServer } from
17
17
import { createAngularMemoryPlugin } from '../../tools/vite/angular-memory-plugin' ;
18
18
import { createAngularLocaleDataPlugin } from '../../tools/vite/i18n-locale-plugin' ;
19
19
import { createRemoveIdPrefixPlugin } from '../../tools/vite/id-prefix-plugin' ;
20
+ import {
21
+ ServerSsrMode ,
22
+ createAngularSetupMiddlewaresPlugin ,
23
+ } from '../../tools/vite/setup-middlewares-plugin' ;
24
+ import { createAngularSsrServerPlugin } from '../../tools/vite/ssr-server-plugin' ;
20
25
import { loadProxyConfiguration , normalizeSourceMaps } from '../../utils' ;
21
26
import { loadEsmModule } from '../../utils/load-esm' ;
22
27
import { Result , ResultFile , ResultKind } from '../application/results' ;
@@ -313,14 +318,25 @@ export async function* serveWithVite(
313
318
? browserOptions . polyfills
314
319
: [ browserOptions . polyfills ] ;
315
320
321
+ let ssrMode : ServerSsrMode = ServerSsrMode . NoSsr ;
322
+ if (
323
+ browserOptions . outputMode &&
324
+ typeof browserOptions . ssr === 'object' &&
325
+ browserOptions . ssr . entry
326
+ ) {
327
+ ssrMode = ServerSsrMode . ExternalSsrMiddleware ;
328
+ } else if ( browserOptions . server ) {
329
+ ssrMode = ServerSsrMode . InternalSsrMiddleware ;
330
+ }
331
+
316
332
// Setup server and start listening
317
333
const serverConfiguration = await setupServer (
318
334
serverOptions ,
319
335
generatedFiles ,
320
336
assetFiles ,
321
337
browserOptions . preserveSymlinks ,
322
338
externalMetadata ,
323
- ! ! browserOptions . ssr ,
339
+ ssrMode ,
324
340
prebundleTransformer ,
325
341
target ,
326
342
isZonelessApp ( polyfills ) ,
@@ -337,7 +353,10 @@ export async function* serveWithVite(
337
353
if ( browserOptions . ssr && serverOptions . prebundle !== false ) {
338
354
// Warm up the SSR request and begin optimizing dependencies.
339
355
// Without this, Vite will only start optimizing SSR modules when the first request is made.
340
- void server . warmupRequest ( './main.server.mjs' , { ssr : true } ) ;
356
+ void Promise . allSettled ( [
357
+ server . warmupRequest ( './server.mjs' , { ssr : true } ) ,
358
+ server . warmupRequest ( './main.server.mjs' , { ssr : true } ) ,
359
+ ] ) ;
341
360
}
342
361
343
362
const urls = server . resolvedUrls ;
@@ -385,34 +404,37 @@ async function handleUpdate(
385
404
usedComponentStyles : Map < string , string [ ] > ,
386
405
) : Promise < void > {
387
406
const updatedFiles : string [ ] = [ ] ;
388
- let isServerFileUpdated = false ;
407
+ let destroyAngularServerAppCalled = false ;
389
408
390
409
// Invalidate any updated files
391
- for ( const [ file , record ] of generatedFiles ) {
392
- if ( record . updated ) {
393
- updatedFiles . push ( file ) ;
394
- isServerFileUpdated ||= record . type === BuildOutputFileType . ServerApplication ;
410
+ for ( const [ file , { updated , type } ] of generatedFiles ) {
411
+ if ( ! updated ) {
412
+ continue ;
413
+ }
395
414
396
- const updatedModules = server . moduleGraph . getModulesByFile (
397
- normalizePath ( join ( server . config . root , file ) ) ,
398
- ) ;
399
- updatedModules ?. forEach ( ( m ) => server ?. moduleGraph . invalidateModule ( m ) ) ;
415
+ if ( type === BuildOutputFileType . ServerApplication && ! destroyAngularServerAppCalled ) {
416
+ // Clear the server app cache
417
+ // This must be done before module invalidation.
418
+ const { ɵdestroyAngularServerApp } = ( await server . ssrLoadModule ( '/main.server.mjs' ) ) as {
419
+ ɵdestroyAngularServerApp : typeof destroyAngularServerApp ;
420
+ } ;
421
+
422
+ ɵdestroyAngularServerApp ( ) ;
423
+ destroyAngularServerAppCalled = true ;
400
424
}
425
+
426
+ updatedFiles . push ( file ) ;
427
+
428
+ const updatedModules = server . moduleGraph . getModulesByFile (
429
+ normalizePath ( join ( server . config . root , file ) ) ,
430
+ ) ;
431
+ updatedModules ?. forEach ( ( m ) => server . moduleGraph . invalidateModule ( m ) ) ;
401
432
}
402
433
403
434
if ( ! updatedFiles . length ) {
404
435
return ;
405
436
}
406
437
407
- // clean server apps cache
408
- if ( isServerFileUpdated ) {
409
- const { ɵdestroyAngularServerApp } = ( await server . ssrLoadModule ( '/main.server.mjs' ) ) as {
410
- ɵdestroyAngularServerApp : typeof destroyAngularServerApp ;
411
- } ;
412
-
413
- ɵdestroyAngularServerApp ( ) ;
414
- }
415
-
416
438
if ( serverOptions . liveReload || serverOptions . hmr ) {
417
439
if ( updatedFiles . every ( ( f ) => f . endsWith ( '.css' ) ) ) {
418
440
const timestamp = Date . now ( ) ;
@@ -534,7 +556,7 @@ export async function setupServer(
534
556
assets : Map < string , string > ,
535
557
preserveSymlinks : boolean | undefined ,
536
558
externalMetadata : DevServerExternalResultMetadata ,
537
- ssr : boolean ,
559
+ ssrMode : ServerSsrMode ,
538
560
prebundleTransformer : JavaScriptTransformer ,
539
561
target : string [ ] ,
540
562
zoneless : boolean ,
@@ -637,19 +659,22 @@ export async function setupServer(
637
659
} ,
638
660
plugins : [
639
661
createAngularLocaleDataPlugin ( ) ,
640
- createAngularMemoryPlugin ( {
641
- workspaceRoot : serverOptions . workspaceRoot ,
642
- virtualProjectRoot,
662
+ createAngularSetupMiddlewaresPlugin ( {
643
663
outputFiles,
644
664
assets,
645
- ssr,
646
- external : externalMetadata . explicitBrowser ,
647
665
indexHtmlTransformer,
648
666
extensionMiddleware,
649
- normalizePath,
650
667
usedComponentStyles,
668
+ ssrMode,
651
669
} ) ,
652
670
createRemoveIdPrefixPlugin ( externalMetadata . explicitBrowser ) ,
671
+ await createAngularSsrServerPlugin ( serverOptions . workspaceRoot ) ,
672
+ await createAngularMemoryPlugin ( {
673
+ workspaceRoot : serverOptions . workspaceRoot ,
674
+ virtualProjectRoot,
675
+ outputFiles,
676
+ external : externalMetadata . explicitBrowser ,
677
+ } ) ,
653
678
] ,
654
679
// Browser only optimizeDeps. (This does not run for SSR dependencies).
655
680
optimizeDeps : getDepOptimizationConfig ( {
0 commit comments