Skip to content

Commit e9c9e49

Browse files
committed
fix(@angular/ssr): resolve circular dependency issue from main.server.js reference in manifest
The issue was addressed by changing the top-level import to a dynamic import. Closes #28358
1 parent 7d9ce24 commit e9c9e49

File tree

8 files changed

+66
-45
lines changed

8 files changed

+66
-45
lines changed

packages/angular/build/src/utils/server-rendering/manifest.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,15 @@ export function generateAngularServerAppManifest(
8989
if (
9090
file.path === INDEX_HTML_SERVER ||
9191
file.path === INDEX_HTML_CSR ||
92-
file.path.endsWith('.css')
92+
(inlineCriticalCss && file.path.endsWith('.css'))
9393
) {
9494
serverAssetsContent.push(`['${file.path}', async () => ${JSON.stringify(file.text)}]`);
9595
}
9696
}
9797

9898
const manifestContent = `
99-
import bootstrap from './main.server.mjs';
100-
10199
export default {
102-
bootstrap: () => bootstrap,
100+
bootstrap: () => import('./main.server.mjs').then(m => m.default),
103101
inlineCriticalCss: ${inlineCriticalCss},
104102
routes: ${JSON.stringify(routes, undefined, 2)},
105103
assets: new Map([${serverAssetsContent.join(', \n')}]),

packages/angular/ssr/src/app.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { getAngularAppManifest } from './manifest';
1515
import { ServerRouter } from './routes/router';
1616
import { REQUEST, REQUEST_CONTEXT, RESPONSE_INIT } from './tokens';
1717
import { InlineCriticalCssProcessor } from './utils/inline-critical-css';
18-
import { renderAngular } from './utils/ng';
18+
import { AngularBootstrap, renderAngular } from './utils/ng';
1919

2020
/**
2121
* Enum representing the different contexts in which server rendering can occur.
@@ -58,6 +58,11 @@ export class AngularServerApp {
5858
*/
5959
private inlineCriticalCssProcessor: InlineCriticalCssProcessor | undefined;
6060

61+
/**
62+
* The bootstrap mechanism for the server application.
63+
*/
64+
private boostrap: AngularBootstrap | undefined;
65+
6166
/**
6267
* Renders a response for the given HTTP request using the server application.
6368
*
@@ -176,7 +181,9 @@ export class AngularServerApp {
176181
html = await hooks.run('html:transform:pre', { html });
177182
}
178183

179-
html = await renderAngular(html, manifest.bootstrap(), new URL(request.url), platformProviders);
184+
this.boostrap ??= await manifest.bootstrap();
185+
186+
html = await renderAngular(html, this.boostrap, new URL(request.url), platformProviders);
180187

181188
if (manifest.inlineCriticalCss) {
182189
// Optionally inline critical CSS.

packages/angular/ssr/src/manifest.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,10 @@ export interface AngularAppManifest {
7171

7272
/**
7373
* The bootstrap mechanism for the server application.
74-
* A function that returns a reference to an NgModule or a function returning a promise that resolves to an ApplicationRef.
74+
* A function that returns a promise that resolves to an `NgModule` or a function
75+
* returning a promise that resolves to an `ApplicationRef`.
7576
*/
76-
readonly bootstrap: () => AngularBootstrap;
77+
readonly bootstrap: () => Promise<AngularBootstrap>;
7778

7879
/**
7980
* Indicates whether critical CSS should be inlined into the HTML.

packages/angular/ssr/src/routes/ng-routes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ export async function extractRoutesAndCreateRouteTree(
274274
const routeTree = new RouteTree();
275275
const document = await new ServerAssets(manifest).getIndexServerHtml();
276276
const { baseHref, routes } = await getRoutesFromAngularRouterConfig(
277-
manifest.bootstrap(),
277+
await manifest.bootstrap(),
278278
document,
279279
url,
280280
);

packages/angular/ssr/test/testing-utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export function setAngularAppTestingManifest(routes: Routes, baseHref = ''): voi
3838
</html>`,
3939
}),
4040
),
41-
bootstrap: () => () => {
41+
bootstrap: async () => () => {
4242
@Component({
4343
standalone: true,
4444
selector: 'app-root',

tests/legacy-cli/e2e/tests/build/ssr/express-engine-csp-nonce.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -136,25 +136,30 @@ export default async function () {
136136
`,
137137
});
138138

139-
async function ngDevSsr(): Promise<number> {
139+
async function spawnServer(): Promise<number> {
140140
const port = await findFreePort();
141-
const useWebpackBuilder = !getGlobalVariable('argv')['esbuild'];
142-
const validBundleRegEx = useWebpackBuilder ? /Compiled successfully\./ : /complete\./;
141+
142+
const runCommand = useWebpackBuilder ? 'serve:ssr' : 'serve:ssr:test-project';
143143

144144
await execAndWaitForOutputToMatch(
145-
'ng',
146-
[
147-
'run',
148-
`test-project:${useWebpackBuilder ? 'serve-ssr' : 'serve'}:production`,
149-
'--port',
150-
String(port),
151-
],
152-
validBundleRegEx,
145+
'npm',
146+
['run', runCommand],
147+
/Node Express server listening on/,
148+
{
149+
'PORT': String(port),
150+
},
153151
);
154152

155153
return port;
156154
}
157155

158-
const port = await ngDevSsr();
156+
await ng('build');
157+
158+
if (useWebpackBuilder) {
159+
// Build server code
160+
await ng('run', 'test-project:server');
161+
}
162+
163+
const port = await spawnServer();
159164
await ng('e2e', `--base-url=http://localhost:${port}`, '--dev-server-target=');
160165
}

tests/legacy-cli/e2e/tests/build/ssr/express-engine-ngmodule.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -141,28 +141,34 @@ export default async function () {
141141
`,
142142
});
143143

144-
async function ngDevSsr(): Promise<number> {
144+
async function spawnServer(): Promise<number> {
145145
const port = await findFreePort();
146-
const validBundleRegEx = useWebpackBuilder ? /Compiled successfully\./ : /complete\./;
146+
147+
const runCommand = useWebpackBuilder ? 'serve:ssr' : `serve:ssr:test-project-two`;
147148

148149
await execAndWaitForOutputToMatch(
149-
'ng',
150-
[
151-
'run',
152-
`test-project-two:${useWebpackBuilder ? 'serve-ssr' : 'serve'}:production`,
153-
'--port',
154-
String(port),
155-
],
156-
validBundleRegEx,
150+
'npm',
151+
['run', runCommand],
152+
/Node Express server listening on/,
153+
{
154+
'PORT': String(port),
155+
},
157156
);
158157

159158
return port;
160159
}
161160

162-
const port = await ngDevSsr();
161+
await ng('build', 'test-project-two');
162+
163+
if (useWebpackBuilder) {
164+
// Build server code
165+
await ng('run', `test-project-two:server`);
166+
}
167+
168+
const port = await spawnServer();
163169
await ng(
164170
'e2e',
165-
'test-project-two',
171+
'--project=test-project-two',
166172
`--base-url=http://localhost:${port}`,
167173
'--dev-server-target=',
168174
);

tests/legacy-cli/e2e/tests/build/ssr/express-engine-standalone.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -106,24 +106,28 @@ export default async function () {
106106
`,
107107
});
108108

109-
async function ngDevSsr(): Promise<number> {
109+
async function spawnServer(): Promise<number> {
110110
const port = await findFreePort();
111-
const validBundleRegEx = useWebpackBuilder ? /Compiled successfully\./ : /complete\./;
111+
const runCommand = useWebpackBuilder ? 'serve:ssr' : 'serve:ssr:test-project';
112112

113113
await execAndWaitForOutputToMatch(
114-
'ng',
115-
[
116-
'run',
117-
`test-project:${useWebpackBuilder ? 'serve-ssr' : 'serve'}:production`,
118-
'--port',
119-
String(port),
120-
],
121-
validBundleRegEx,
114+
'npm',
115+
['run', runCommand],
116+
/Node Express server listening on/,
117+
{
118+
'PORT': String(port),
119+
},
122120
);
123121

124122
return port;
125123
}
126124

127-
const port = await ngDevSsr();
125+
await ng('build');
126+
if (useWebpackBuilder) {
127+
// Build server code
128+
await ng('run', `test-project:server`);
129+
}
130+
131+
const port = await spawnServer();
128132
await ng('e2e', `--base-url=http://localhost:${port}`, '--dev-server-target=');
129133
}

0 commit comments

Comments
 (0)