Skip to content

Commit

Permalink
fix(@angular/ssr): accurately calculate content length for static pag…
Browse files Browse the repository at this point in the history
…es with `\r\n`

JS engines convert `\r\n` to `\n` in template literals, potentially leading to incorrect byte length calculations. This fix ensures the correct content length is determined.

Closes #29567

(cherry picked from commit 414736b)
  • Loading branch information
alan-agius4 committed Feb 6, 2025
1 parent f4de3d2 commit f5d9745
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 3 deletions.
12 changes: 10 additions & 2 deletions packages/angular/build/src/utils/server-rendering/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -139,20 +140,27 @@ export function generateAngularServerAppManifest(
} {
const serverAssetsChunks: BuildOutputFile[] = [];
const serverAssets: Record<string, string> = {};

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)}`;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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',
Expand Down Expand Up @@ -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();

Expand Down

0 comments on commit f5d9745

Please sign in to comment.