diff --git a/packages/angular/build/src/utils/server-rendering/manifest.ts b/packages/angular/build/src/utils/server-rendering/manifest.ts index a8be8d833efa..4d1459e221c2 100644 --- a/packages/angular/build/src/utils/server-rendering/manifest.ts +++ b/packages/angular/build/src/utils/server-rendering/manifest.ts @@ -8,6 +8,7 @@ import type { Metafile } from 'esbuild'; import { extname } from 'node:path'; +import { runInThisContext } from 'node:vm'; import { NormalizedApplicationBuildOptions } from '../../builders/application/options'; import { type BuildOutputFile, BuildOutputFileType } from '../../tools/esbuild/bundler-context'; import { createOutputFile } from '../../tools/esbuild/utils'; @@ -139,20 +140,27 @@ export function generateAngularServerAppManifest( } { const serverAssetsChunks: BuildOutputFile[] = []; const serverAssets: Record = {}; + for (const file of [...additionalHtmlOutputFiles.values(), ...outputFiles]) { const extension = extname(file.path); if (extension === '.html' || (inlineCriticalCss && extension === '.css')) { const jsChunkFilePath = `assets-chunks/${file.path.replace(/[./]/g, '_')}.mjs`; + const escapedContent = escapeUnsafeChars(file.text); + serverAssetsChunks.push( createOutputFile( jsChunkFilePath, - `export default \`${escapeUnsafeChars(file.text)}\`;`, + `export default \`${escapedContent}\`;`, BuildOutputFileType.ServerApplication, ), ); + // This is needed because JavaScript engines script parser convert `\r\n` to `\n` in template literals, + // which can result in an incorrect byte length. + const size = runInThisContext(`new TextEncoder().encode(\`${escapedContent}\`).byteLength`); + serverAssets[file.path] = - `{size: ${file.size}, hash: '${file.hash}', text: () => import('./${jsChunkFilePath}').then(m => m.default)}`; + `{size: ${size}, hash: '${file.hash}', text: () => import('./${jsChunkFilePath}').then(m => m.default)}`; } } diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server.ts index 2d14c0ceecbb..822b9ea9bb7e 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server.ts @@ -1,7 +1,7 @@ import { join } from 'node:path'; import { existsSync } from 'node:fs'; import assert from 'node:assert'; -import { expectFileToMatch, writeFile } from '../../../utils/fs'; +import { expectFileToMatch, readFile, replaceInFile, writeFile } from '../../../utils/fs'; import { execAndWaitForOutputToMatch, ng, noSilentNg, silentNg } from '../../../utils/process'; import { installWorkspacePackages, uninstallPackage } from '../../../utils/packages'; import { useSha } from '../../../utils/project'; @@ -20,6 +20,12 @@ export default async function () { await useSha(); await installWorkspacePackages(); + // Test scenario to verify that the content length, including \r\n, is accurate + await replaceInFile('src/app/app.component.ts', "title = '", "title = 'Title\\r\\n"); + + // Ensure text has been updated. + assert.match(await readFile('src/app/app.component.ts'), /title = 'Title/); + // Add routes await writeFile( 'src/app/app.routes.ts', @@ -165,6 +171,7 @@ export default async function () { const port = await spawnServer(); for (const [pathname, { content, headers, serverContext }] of Object.entries(responseExpects)) { + // NOTE: A global 'UND_ERR_SOCKET' may occur due to an incorrect Content-Length header value. const res = await fetch(`http://localhost:${port}${pathname}`); const text = await res.text();