6
6
* found in the LICENSE file at https://angular.dev/license
7
7
*/
8
8
9
- import { ɵConsole } from '@angular/core' ;
10
- import type { ApplicationRef , StaticProvider , Type } from '@angular/core' ;
11
9
import {
10
+ ApplicationRef ,
11
+ type PlatformRef ,
12
+ type StaticProvider ,
13
+ type Type ,
14
+ ɵConsole ,
15
+ } from '@angular/core' ;
16
+ import {
17
+ INITIAL_CONFIG ,
12
18
ɵSERVER_CONTEXT as SERVER_CONTEXT ,
13
- renderApplication ,
14
- renderModule ,
19
+ platformServer ,
20
+ ɵrenderInternal as renderInternal ,
15
21
} from '@angular/platform-server' ;
16
22
import { Console } from '../console' ;
17
23
import { stripIndexHtmlFromURL } from './url' ;
@@ -41,16 +47,26 @@ export type AngularBootstrap = Type<unknown> | (() => Promise<ApplicationRef>);
41
47
* rendering process.
42
48
* @param serverContext - A string representing the server context, used to provide additional
43
49
* context or metadata during server-side rendering.
44
- * @returns A promise that resolves to a string containing the rendered HTML.
50
+ * @returns A promise resolving to an object containing a `content` method, which returns a
51
+ * promise that resolves to the rendered HTML string.
45
52
*/
46
- export function renderAngular (
53
+ export async function renderAngular (
47
54
html : string ,
48
55
bootstrap : AngularBootstrap ,
49
56
url : URL ,
50
57
platformProviders : StaticProvider [ ] ,
51
58
serverContext : string ,
52
- ) : Promise < string > {
53
- const providers = [
59
+ ) : Promise < { content : ( ) => Promise < string > } > {
60
+ // A request to `http://www.example.com/page/index.html` will render the Angular route corresponding to `http://www.example.com/page`.
61
+ const urlToRender = stripIndexHtmlFromURL ( url ) . toString ( ) ;
62
+ const platformRef = platformServer ( [
63
+ {
64
+ provide : INITIAL_CONFIG ,
65
+ useValue : {
66
+ url : urlToRender ,
67
+ document : html ,
68
+ } ,
69
+ } ,
54
70
{
55
71
provide : SERVER_CONTEXT ,
56
72
useValue : serverContext ,
@@ -64,22 +80,34 @@ export function renderAngular(
64
80
useFactory : ( ) => new Console ( ) ,
65
81
} ,
66
82
...platformProviders ,
67
- ] ;
83
+ ] ) ;
68
84
69
- // A request to `http://www.example.com/page/index.html` will render the Angular route corresponding to `http://www.example.com/page`.
70
- const urlToRender = stripIndexHtmlFromURL ( url ) . toString ( ) ;
85
+ try {
86
+ let applicationRef : ApplicationRef ;
87
+ if ( isNgModule ( bootstrap ) ) {
88
+ const moduleRef = await platformRef . bootstrapModule ( bootstrap ) ;
89
+ applicationRef = moduleRef . injector . get ( ApplicationRef ) ;
90
+ } else {
91
+ applicationRef = await bootstrap ( ) ;
92
+ }
71
93
72
- return isNgModule ( bootstrap )
73
- ? renderModule ( bootstrap , {
74
- url : urlToRender ,
75
- document : html ,
76
- extraProviders : providers ,
77
- } )
78
- : renderApplication ( bootstrap , {
79
- url : urlToRender ,
80
- document : html ,
81
- platformProviders : providers ,
82
- } ) ;
94
+ // Block until application is stable.
95
+ await applicationRef . whenStable ( ) ;
96
+
97
+ return {
98
+ content : async ( ) => {
99
+ try {
100
+ return renderInternal ( platformRef , applicationRef ) ;
101
+ } finally {
102
+ await asyncDestroyPlatform ( platformRef ) ;
103
+ }
104
+ } ,
105
+ } ;
106
+ } catch ( error ) {
107
+ await asyncDestroyPlatform ( platformRef ) ;
108
+
109
+ throw error ;
110
+ }
83
111
}
84
112
85
113
/**
@@ -93,3 +121,18 @@ export function renderAngular(
93
121
export function isNgModule ( value : AngularBootstrap ) : value is Type < unknown > {
94
122
return 'ɵmod' in value ;
95
123
}
124
+
125
+ /**
126
+ * Gracefully destroys the application in a macrotask, allowing pending promises to resolve
127
+ * and surfacing any potential errors to the user.
128
+ *
129
+ * @param platformRef - The platform reference to be destroyed.
130
+ */
131
+ function asyncDestroyPlatform ( platformRef : PlatformRef ) : Promise < void > {
132
+ return new Promise ( ( resolve ) => {
133
+ setTimeout ( ( ) => {
134
+ platformRef . destroy ( ) ;
135
+ resolve ( ) ;
136
+ } , 0 ) ;
137
+ } ) ;
138
+ }
0 commit comments