Skip to content

Commit

Permalink
fix(@angular/ssr): resolve circular dependency issue from main.server…
Browse files Browse the repository at this point in the history
….js reference in manifest

The issue was addressed by changing the top-level import to a dynamic import.

Closes #28358
  • Loading branch information
alan-agius4 committed Sep 6, 2024
1 parent 7d9ce24 commit e9c9e49
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 45 deletions.
6 changes: 2 additions & 4 deletions packages/angular/build/src/utils/server-rendering/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,15 @@ export function generateAngularServerAppManifest(
if (
file.path === INDEX_HTML_SERVER ||
file.path === INDEX_HTML_CSR ||
file.path.endsWith('.css')
(inlineCriticalCss && file.path.endsWith('.css'))
) {
serverAssetsContent.push(`['${file.path}', async () => ${JSON.stringify(file.text)}]`);
}
}

const manifestContent = `
import bootstrap from './main.server.mjs';
export default {
bootstrap: () => bootstrap,
bootstrap: () => import('./main.server.mjs').then(m => m.default),
inlineCriticalCss: ${inlineCriticalCss},
routes: ${JSON.stringify(routes, undefined, 2)},
assets: new Map([${serverAssetsContent.join(', \n')}]),
Expand Down
11 changes: 9 additions & 2 deletions packages/angular/ssr/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { getAngularAppManifest } from './manifest';
import { ServerRouter } from './routes/router';
import { REQUEST, REQUEST_CONTEXT, RESPONSE_INIT } from './tokens';
import { InlineCriticalCssProcessor } from './utils/inline-critical-css';
import { renderAngular } from './utils/ng';
import { AngularBootstrap, renderAngular } from './utils/ng';

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

/**
* The bootstrap mechanism for the server application.
*/
private boostrap: AngularBootstrap | undefined;

/**
* Renders a response for the given HTTP request using the server application.
*
Expand Down Expand Up @@ -176,7 +181,9 @@ export class AngularServerApp {
html = await hooks.run('html:transform:pre', { html });
}

html = await renderAngular(html, manifest.bootstrap(), new URL(request.url), platformProviders);
this.boostrap ??= await manifest.bootstrap();

html = await renderAngular(html, this.boostrap, new URL(request.url), platformProviders);

if (manifest.inlineCriticalCss) {
// Optionally inline critical CSS.
Expand Down
5 changes: 3 additions & 2 deletions packages/angular/ssr/src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,10 @@ export interface AngularAppManifest {

/**
* The bootstrap mechanism for the server application.
* A function that returns a reference to an NgModule or a function returning a promise that resolves to an ApplicationRef.
* A function that returns a promise that resolves to an `NgModule` or a function
* returning a promise that resolves to an `ApplicationRef`.
*/
readonly bootstrap: () => AngularBootstrap;
readonly bootstrap: () => Promise<AngularBootstrap>;

/**
* Indicates whether critical CSS should be inlined into the HTML.
Expand Down
2 changes: 1 addition & 1 deletion packages/angular/ssr/src/routes/ng-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ export async function extractRoutesAndCreateRouteTree(
const routeTree = new RouteTree();
const document = await new ServerAssets(manifest).getIndexServerHtml();
const { baseHref, routes } = await getRoutesFromAngularRouterConfig(
manifest.bootstrap(),
await manifest.bootstrap(),
document,
url,
);
Expand Down
2 changes: 1 addition & 1 deletion packages/angular/ssr/test/testing-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function setAngularAppTestingManifest(routes: Routes, baseHref = ''): voi
</html>`,
}),
),
bootstrap: () => () => {
bootstrap: async () => () => {
@Component({
standalone: true,
selector: 'app-root',
Expand Down
29 changes: 17 additions & 12 deletions tests/legacy-cli/e2e/tests/build/ssr/express-engine-csp-nonce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,25 +136,30 @@ export default async function () {
`,
});

async function ngDevSsr(): Promise<number> {
async function spawnServer(): Promise<number> {
const port = await findFreePort();
const useWebpackBuilder = !getGlobalVariable('argv')['esbuild'];
const validBundleRegEx = useWebpackBuilder ? /Compiled successfully\./ : /complete\./;

const runCommand = useWebpackBuilder ? 'serve:ssr' : 'serve:ssr:test-project';

await execAndWaitForOutputToMatch(
'ng',
[
'run',
`test-project:${useWebpackBuilder ? 'serve-ssr' : 'serve'}:production`,
'--port',
String(port),
],
validBundleRegEx,
'npm',
['run', runCommand],
/Node Express server listening on/,
{
'PORT': String(port),
},
);

return port;
}

const port = await ngDevSsr();
await ng('build');

if (useWebpackBuilder) {
// Build server code
await ng('run', 'test-project:server');
}

const port = await spawnServer();
await ng('e2e', `--base-url=http://localhost:${port}`, '--dev-server-target=');
}
30 changes: 18 additions & 12 deletions tests/legacy-cli/e2e/tests/build/ssr/express-engine-ngmodule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,28 +141,34 @@ export default async function () {
`,
});

async function ngDevSsr(): Promise<number> {
async function spawnServer(): Promise<number> {
const port = await findFreePort();
const validBundleRegEx = useWebpackBuilder ? /Compiled successfully\./ : /complete\./;

const runCommand = useWebpackBuilder ? 'serve:ssr' : `serve:ssr:test-project-two`;

await execAndWaitForOutputToMatch(
'ng',
[
'run',
`test-project-two:${useWebpackBuilder ? 'serve-ssr' : 'serve'}:production`,
'--port',
String(port),
],
validBundleRegEx,
'npm',
['run', runCommand],
/Node Express server listening on/,
{
'PORT': String(port),
},
);

return port;
}

const port = await ngDevSsr();
await ng('build', 'test-project-two');

if (useWebpackBuilder) {
// Build server code
await ng('run', `test-project-two:server`);
}

const port = await spawnServer();
await ng(
'e2e',
'test-project-two',
'--project=test-project-two',
`--base-url=http://localhost:${port}`,
'--dev-server-target=',
);
Expand Down
26 changes: 15 additions & 11 deletions tests/legacy-cli/e2e/tests/build/ssr/express-engine-standalone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,24 +106,28 @@ export default async function () {
`,
});

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

await execAndWaitForOutputToMatch(
'ng',
[
'run',
`test-project:${useWebpackBuilder ? 'serve-ssr' : 'serve'}:production`,
'--port',
String(port),
],
validBundleRegEx,
'npm',
['run', runCommand],
/Node Express server listening on/,
{
'PORT': String(port),
},
);

return port;
}

const port = await ngDevSsr();
await ng('build');
if (useWebpackBuilder) {
// Build server code
await ng('run', `test-project:server`);
}

const port = await spawnServer();
await ng('e2e', `--base-url=http://localhost:${port}`, '--dev-server-target=');
}

0 comments on commit e9c9e49

Please sign in to comment.