Skip to content
Open
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
27 changes: 27 additions & 0 deletions .idea/copilotDiffState.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
} from '../../utils/server-rendering/models';
import { prerenderPages } from '../../utils/server-rendering/prerender';
import { augmentAppWithServiceWorkerEsbuild } from '../../utils/service-worker';
import { injectDebugIds } from './inject-debug-ids';
import { INDEX_HTML_CSR, INDEX_HTML_SERVER, NormalizedApplicationBuildOptions } from './options';
import { OutputMode } from './schema';

Expand Down Expand Up @@ -79,6 +80,14 @@ export async function executePostBundleSteps(
partialSSRBuild,
} = options;

// Embed ECMA-426 Debug IDs into JS/source-map pairs before any consumer reads the bytes (in
// particular `generateIndexHtml` below, which computes SRI hashes from the on-disk content).
// Doing this here also covers the i18n path, where this function is invoked once per locale
// with locale-specific output files. Files without a source map sibling are skipped.
if (sourcemapOptions.scripts) {
injectDebugIds(outputFiles);
}

// Index HTML content without CSS inlining to be used for server rendering (AppShell, SSG and SSR).
// NOTE: Critical CSS inlining is deliberately omitted here, as it will be handled during server rendering.
// Additionally, when using prerendering or AppShell, the index HTML file may be regenerated.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/

import { BuildOutputFile, BuildOutputFileType } from '../../tools/esbuild/bundler-context';
import {
generateDebugId,
injectDebugIdIntoJs,
injectDebugIdIntoSourceMap,
} from '../../utils/debug-id';

/**
* Embeds an ECMA-426 Debug ID into every browser JavaScript output that has a
* matching source map sibling.
*
* The Debug ID is derived deterministically (UUIDv5) from the source map bytes
* so rebuilds of the same source produce the same ID. The JS file gets a
* `//# debugId=<uuid>` comment placed above any existing
* `//# sourceMappingURL=` line and the source map JSON gets a top-level
* `"debugId"` field. Together they make build artifacts self-identifying as
* proposed by https://github.com/tc39/ecma426/blob/main/proposals/debug-id.md.
*/
export function injectDebugIds(outputFiles: BuildOutputFile[]): void {
const filesByPath = new Map<string, BuildOutputFile>();
for (const file of outputFiles) {
filesByPath.set(file.path, file);
}

const encoder = new TextEncoder();

for (const file of outputFiles) {
if (file.type !== BuildOutputFileType.Browser || !file.path.endsWith('.js')) {
continue;
}

const map = filesByPath.get(`${file.path}.map`);
if (!map) {
continue;
}

const id = generateDebugId(map.contents);
file.contents = encoder.encode(injectDebugIdIntoJs(file.text, id));
map.contents = encoder.encode(injectDebugIdIntoSourceMap(map.text, id));
}
}
Loading
Loading