Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions goldens/circular-deps/packages.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"packages/angular/build/src/tools/esbuild/angular/component-stylesheets.ts",
"packages/angular/build/src/tools/esbuild/bundler-context.ts",
"packages/angular/build/src/tools/esbuild/utils.ts",
"packages/angular/build/src/utils/server-rendering/manifest.ts",
"packages/angular/build/src/tools/esbuild/bundler-execution-result.ts"
],
[
Expand All @@ -17,16 +16,18 @@
[
"packages/angular/build/src/tools/esbuild/bundler-context.ts",
"packages/angular/build/src/tools/esbuild/utils.ts",
"packages/angular/build/src/utils/server-rendering/manifest.ts"
"packages/angular/build/src/tools/esbuild/bundler-execution-result.ts"
],
[
"packages/angular/build/src/tools/esbuild/bundler-context.ts",
"packages/angular/build/src/tools/esbuild/utils.ts",
"packages/angular/build/src/utils/server-rendering/manifest.ts",
"packages/angular/build/src/tools/esbuild/bundler-execution-result.ts"
"packages/angular/build/src/utils/server-rendering/manifest.ts"
],
[
"packages/angular/build/src/tools/esbuild/bundler-execution-result.ts",
"packages/angular/build/src/tools/esbuild/utils.ts"
],
[
"packages/angular/build/src/tools/esbuild/utils.ts",
"packages/angular/build/src/utils/server-rendering/manifest.ts"
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { OutputMode } from './schema';
* @param initialFiles A map containing initial file information for the executed build.
* @param locale A language locale to insert in the index.html.
*/
// eslint-disable-next-line max-lines-per-function
export async function executePostBundleSteps(
options: NormalizedApplicationBuildOptions,
outputFiles: BuildOutputFile[],
Expand Down Expand Up @@ -107,16 +108,19 @@ export async function executePostBundleSteps(

// Create server manifest
if (serverEntryPoint) {
const { manifestContent, serverAssetsChunks } = generateAngularServerAppManifest(
additionalHtmlOutputFiles,
outputFiles,
optimizationOptions.styles.inlineCritical ?? false,
undefined,
locale,
);

additionalOutputFiles.push(
...serverAssetsChunks,
createOutputFile(
SERVER_APP_MANIFEST_FILENAME,
generateAngularServerAppManifest(
additionalHtmlOutputFiles,
outputFiles,
optimizationOptions.styles.inlineCritical ?? false,
undefined,
locale,
),
manifestContent,
BuildOutputFileType.ServerApplication,
),
);
Expand Down Expand Up @@ -194,15 +198,24 @@ export async function executePostBundleSteps(
const manifest = additionalOutputFiles.find((f) => f.path === SERVER_APP_MANIFEST_FILENAME);
assert(manifest, `${SERVER_APP_MANIFEST_FILENAME} was not found in output files.`);

manifest.contents = new TextEncoder().encode(
generateAngularServerAppManifest(
additionalHtmlOutputFiles,
outputFiles,
optimizationOptions.styles.inlineCritical ?? false,
serializableRouteTreeNodeForManifest,
locale,
),
const { manifestContent, serverAssetsChunks } = generateAngularServerAppManifest(
additionalHtmlOutputFiles,
outputFiles,
optimizationOptions.styles.inlineCritical ?? false,
serializableRouteTreeNodeForManifest,
locale,
);

for (const chunk of serverAssetsChunks) {
const idx = additionalOutputFiles.findIndex(({ path }) => path === chunk.path);
if (idx === -1) {
additionalOutputFiles.push(chunk);
} else {
additionalOutputFiles[idx] = chunk;
}
}

manifest.contents = new TextEncoder().encode(manifestContent);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import assert from 'node:assert';
import { readFile } from 'node:fs/promises';
import { basename, dirname, join, relative } from 'node:path';
import { dirname, join, relative } from 'node:path';
import type { Plugin } from 'vite';
import { loadEsmModule } from '../../../utils/load-esm';
import { AngularMemoryOutputFiles } from '../utils';
Expand All @@ -24,8 +24,6 @@ export async function createAngularMemoryPlugin(
): Promise<Plugin> {
const { virtualProjectRoot, outputFiles, external } = options;
const { normalizePath } = await loadEsmModule<typeof import('vite')>('vite');
// See: https://github.com/vitejs/vite/blob/a34a73a3ad8feeacc98632c0f4c643b6820bbfda/packages/vite/src/node/server/pluginContainer.ts#L331-L334
const defaultImporter = join(virtualProjectRoot, 'index.html');

return {
name: 'vite:angular-memory',
Expand All @@ -40,16 +38,10 @@ export async function createAngularMemoryPlugin(
}

if (importer) {
let normalizedSource: string | undefined;
if (source[0] === '.' && normalizePath(importer).startsWith(virtualProjectRoot)) {
// Remove query if present
const [importerFile] = importer.split('?', 1);
normalizedSource = join(dirname(relative(virtualProjectRoot, importerFile)), source);
} else if (source[0] === '/' && importer === defaultImporter) {
normalizedSource = basename(source);
}
if (normalizedSource) {
source = '/' + normalizePath(normalizedSource);
source = '/' + join(dirname(relative(virtualProjectRoot, importerFile)), source);
}
}

Expand Down
33 changes: 25 additions & 8 deletions packages/angular/build/src/utils/server-rendering/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import {
NormalizedApplicationBuildOptions,
getLocaleBaseHref,
} from '../../builders/application/options';
import type { BuildOutputFile } from '../../tools/esbuild/bundler-context';
import type { PrerenderedRoutesRecord } from '../../tools/esbuild/bundler-execution-result';
import { type BuildOutputFile, BuildOutputFileType } from '../../tools/esbuild/bundler-context';
import { createOutputFile } from '../../tools/esbuild/utils';

export const SERVER_APP_MANIFEST_FILENAME = 'angular-app-manifest.mjs';
export const SERVER_APP_ENGINE_MANIFEST_FILENAME = 'angular-app-engine-manifest.mjs';
Expand Down Expand Up @@ -104,22 +104,39 @@ export default {
* @param locale - An optional string representing the locale or language code to be used for
* the application, helping with localization and rendering content specific to the locale.
*
* @returns A string representing the content of the SSR server manifest for the Node.js
* environment.
* @returns An object containing:
* - `manifestContent`: A string of the SSR manifest content.
* - `serverAssetsChunks`: An array of build output files containing the generated assets for the server.
*/
export function generateAngularServerAppManifest(
additionalHtmlOutputFiles: Map<string, BuildOutputFile>,
outputFiles: BuildOutputFile[],
inlineCriticalCss: boolean,
routes: readonly unknown[] | undefined,
locale: string | undefined,
): string {
): {
manifestContent: string;
serverAssetsChunks: BuildOutputFile[];
} {
const serverAssetsChunks: BuildOutputFile[] = [];
const serverAssetsContent: 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 \`${escapedContent}\`;`,
BuildOutputFileType.ServerApplication,
),
);

const contentLength = Buffer.byteLength(escapedContent);
serverAssetsContent.push(
`['${file.path}', { size: ${file.size}, hash: '${file.hash}', text: async () => \`${escapeUnsafeChars(file.text)}\`}]`,
`['${file.path}', {size: ${contentLength}, hash: '${file.hash}', text: () => import('./${jsChunkFilePath}').then(m => m.default)}]`,
);
}
}
Expand All @@ -129,10 +146,10 @@ export default {
bootstrap: () => import('./main.server.mjs').then(m => m.default),
inlineCriticalCss: ${inlineCriticalCss},
routes: ${JSON.stringify(routes, undefined, 2)},
assets: new Map([${serverAssetsContent.join(', \n')}]),
assets: new Map([\n${serverAssetsContent.join(', \n')}\n]),
locale: ${locale !== undefined ? `'${locale}'` : undefined},
};
`;

return manifestContent;
return { manifestContent, serverAssetsChunks };
}