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
1 change: 1 addition & 0 deletions packages/angular/build/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ ts_project(
":node_modules/vite",
":node_modules/vitest",
":node_modules/watchpack",
":node_modules/xxhash-wasm",
"//:node_modules/@angular/common",
"//:node_modules/@angular/compiler",
"//:node_modules/@angular/compiler-cli",
Expand Down
3 changes: 2 additions & 1 deletion packages/angular/build/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"source-map-support": "0.5.21",
"tinyglobby": "0.2.17",
"vite": "8.2.0",
"watchpack": "2.5.2"
"watchpack": "2.5.2",
"xxhash-wasm": "1.1.0"
},
"optionalDependencies": {
"lmdb": "3.5.6"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { shutdownSassWorkerPool } from '../../tools/esbuild/stylesheets/sass-lan
import { logMessages, withNoProgress, withSpinner } from '../../tools/esbuild/utils';
import { ChangedFiles } from '../../tools/esbuild/watcher';
import { shouldWatchRoot } from '../../utils/environment-options';
import { initializeHash } from '../../utils/hash';
import { NormalizedCachedOptions } from '../../utils/normalize-cache';
import { toPosixPath } from '../../utils/path';
import { NormalizedApplicationBuildOptions, NormalizedOutputOptions } from './options';
Expand Down Expand Up @@ -78,6 +79,8 @@ export async function* runEsBuildBuildAction(
incrementalResults,
} = options;

await initializeHash();

const withProgress: typeof withSpinner = progress ? withSpinner : withNoProgress;

// Initial build
Expand Down
2 changes: 2 additions & 0 deletions packages/angular/build/src/builders/dev-server/vite/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
} from '../../../tools/vite/plugins';
import { RolldownLoaderOption, getDepOptimizationConfig } from '../../../tools/vite/utils';
import { loadProxyConfiguration } from '../../../utils';
import { initializeHash } from '../../../utils/hash';
import { type ApplicationBuilderInternalOptions, JavaScriptTransformer } from '../internal';
import type { NormalizedDevServerOptions } from '../options';
import { DevServerExternalResultMetadata, OutputAssetRecord, OutputFileRecord } from './utils';
Expand Down Expand Up @@ -147,6 +148,7 @@ export async function setupServer(
indexHtmlTransformer?: (content: string) => Promise<string>,
thirdPartySourcemaps = false,
): Promise<Vite.InlineConfig> {
await initializeHash();
const { normalizePath } = (await import('vite' as string)) as typeof Vite;

// Path will not exist on disk and only used to provide separate path for Vite requests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
* found in the LICENSE file at https://angular.dev/license
*/

import { createHash } from 'node:crypto';
import { type PathLike, constants, promises as fs } from 'node:fs';
import os from 'node:os';
import { basename, dirname, extname, isAbsolute, join, relative } from 'node:path';
import { glob, isDynamicPattern } from 'tinyglobby';
import { calculateHash, initializeHash } from '../../utils/hash';
import { toPosixPath } from '../../utils/path';

/**
Expand Down Expand Up @@ -41,6 +41,7 @@ export async function findTests(
workspaceRoot: string,
projectSourceRoot: string,
): Promise<string[]> {
await initializeHash();
const resolvedTestFiles = new Set<string>();
const dynamicPatterns: string[] = [];

Expand Down Expand Up @@ -194,7 +195,7 @@ function truncateName(name: string, originalPath: string): string {
return name;
}

const hash = createHash('sha256').update(originalPath).digest('hex').substring(0, 8);
const hash = calculateHash(originalPath).substring(0, 8);
const availableLength = MAX_FILENAME_LENGTH - hash.length - 2; // 2 for '-' separators
const prefixLength = Math.floor(availableLength / 2);
const suffixLength = availableLength - prefixLength;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@
* found in the LICENSE file at https://angular.dev/license
*/

import { initializeHash } from '../../utils/hash';
import { generateNameFromPath, getTestEntrypoints } from './test-discovery';

describe('getTestEntrypoints', () => {
beforeAll(async () => {
await initializeHash();
});

const workspaceRoot = '/project';
const projectSourceRoot = '/project/src';
const options = { workspaceRoot, projectSourceRoot };
Expand Down Expand Up @@ -81,6 +86,10 @@ describe('getTestEntrypoints', () => {
});

describe('generateNameFromPath', () => {
beforeAll(async () => {
await initializeHash();
});

const roots = ['/project/src/', '/project/'];

it('should generate a dash-cased name from a simple path', () => {
Expand Down Expand Up @@ -127,7 +136,7 @@ describe('generateNameFromPath', () => {

expect(result.length).toBeLessThanOrEqual(128);
expect(result).toBe(
'a-very-long-path-that-definitely-exceeds-the-maximum-allowe-9cf40291-me-in-order-to-trigger-the-truncation-logic-in-the-function',
'a-very-long-path-that-definitely-exceeds-the-maximum-allowe-4af8113d-me-in-order-to-trigger-the-truncation-logic-in-the-function',
); // eslint-disable-line max-len
});

Expand Down
6 changes: 3 additions & 3 deletions packages/angular/build/src/tools/angular/angular-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

import type * as ng from '@angular/compiler-cli';
import assert from 'node:assert';
import { createHash } from 'node:crypto';
import nodePath from 'node:path';
import type ts from 'typescript';
import { calculateHash } from '../../utils/hash';

export type AngularCompilerOptions = ng.CompilerOptions;
export type AngularCompilerHost = ng.CompilerHost;
Expand Down Expand Up @@ -46,7 +46,7 @@ export function ensureSourceFileVersions(program: ts.Program): void {

for (const file of files) {
if (file.version === undefined) {
file.version = createHash('sha256').update(file.text).digest('hex');
file.version = calculateHash(file.text);
}
}

Expand Down Expand Up @@ -227,7 +227,7 @@ export function createAngularCompilerHost(
// For external stylesheets, create a unique identifier and store the mapping
let externalId = hostOptions.externalStylesheets.get(resolvedPath);
if (externalId === undefined) {
externalId = createHash('sha256').update(resolvedPath).digest('hex');
externalId = calculateHash(resolvedPath);
hostOptions.externalStylesheets.set(resolvedPath, externalId);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { PartialMessage } from 'esbuild';
import assert from 'node:assert';
import { randomUUID } from 'node:crypto';
import { type MessagePort, receiveMessageOnPort } from 'node:worker_threads';
import { initializeHash } from '../../../utils/hash';
import { SourceFileCache } from '../../esbuild/angular/source-file-cache';
import { getAndClearCumulativeDurations } from '../../esbuild/profiling';
import type { AngularCompilation, DiagnosticModes } from './angular-compilation';
Expand All @@ -33,6 +34,7 @@ let compilation: AngularCompilation | undefined;
const sourceFileCache = new SourceFileCache();

export async function initialize(request: InitRequest) {
await initializeHash();
compilation ??= request.jit
? new JitCompilation(request.browserOnlyBuild)
: new AotCompilation(request.browserOnlyBuild);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ import type {
PluginBuild,
} from 'esbuild';
import assert from 'node:assert';
import { createHash } from 'node:crypto';
import { readFile } from 'node:fs/promises';
import * as path from 'node:path';
import { maxWorkers, useTypeChecking } from '../../../utils/environment-options';
import { calculateHash, initializeHash } from '../../../utils/hash';
import { AngularHostOptions } from '../../angular/angular-host';
import { AngularCompilation, DiagnosticModes, NoopCompilation } from '../../angular/compilation';
import { type PersistentCacheStore, createPersistentCacheStore } from '../cache';
Expand Down Expand Up @@ -149,6 +149,7 @@ export function createCompilerPlugin(

// eslint-disable-next-line max-lines-per-function
build.onStart(async () => {
await initializeHash();
angularCompilationContext.markAsInProgress();

const result: OnStartResult = {
Expand Down Expand Up @@ -205,11 +206,7 @@ export function createCompilerPlugin(
// invalid the output and force a full page reload for HMR cases. The containing file and order
// of the style within the containing file is used.
pluginOptions.externalRuntimeStyles
? createHash('sha256')
.update(containingFile)
.update((order ?? 0).toString())
.update(className ?? '')
.digest('hex')
? calculateHash(`${containingFile}${order ?? 0}${className ?? ''}`)
: undefined,
);
// Adjust result source for inline styles.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
*/

import assert from 'node:assert';
import { createHash } from 'node:crypto';
import path from 'node:path';
import { createContentHash } from '../../../utils/hash';
import { BundleContextResult, BundlerContext } from '../bundler-context';
import { type BuildOutputFile, BuildOutputFileType } from '../bundler-files';
import { MemoryCache } from '../cache';
Expand Down Expand Up @@ -103,11 +103,10 @@ export class ComponentStylesheetBundler {
): Promise<ComponentStylesheetResult> {
// Use a hash of the inline stylesheet content to ensure a consistent identifier. External stylesheets will resolve
// to the actual stylesheet file path.
// TODO: Consider xxhash instead for hashing
const id = createHash('sha256')
.update(data)
.update(externalId ?? '')
.digest('hex');
const hasher = createContentHash();
hasher.update(data);
hasher.update(externalId ?? '');
const id = hasher.digest();
const entry = [language, id, filename].join(';');

const bundlerContext = await this.#inlineContexts.getOrCreate(entry, () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@

import type { BuildOptions, Plugin } from 'esbuild';
import assert from 'node:assert';
import { createHash } from 'node:crypto';
import { extname, relative } from 'node:path';
import type { NormalizedApplicationBuildOptions } from '../../builders/application/options';
import { Platform } from '../../builders/application/schema';
import { allowMangle } from '../../utils/environment-options';
import { calculateHash } from '../../utils/hash';
import { toPosixPath } from '../../utils/path';
import {
SERVER_APP_ENGINE_MANIFEST_FILENAME,
Expand Down Expand Up @@ -566,7 +566,7 @@ function getEsBuildCommonOptions(options: NormalizedApplicationBuildOptions): Bu
'',
);

footer = { js: `/**i18n:${createHash('sha256').update(i18nHash).digest('hex')}*/` };
footer = { js: `/**i18n:${calculateHash(i18nHash)}*/` };
}

// Core conditions that are always included
Expand Down
8 changes: 3 additions & 5 deletions packages/angular/build/src/tools/esbuild/bundler-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import type { OutputFile } from 'esbuild';
import { createHash } from 'node:crypto';
import { calculateHash } from '../../utils/hash';

export interface InitialFileRecord {
entrypoint: boolean;
Expand Down Expand Up @@ -63,9 +63,7 @@ export function createOutputFile(
return this.contents.byteLength;
},
get hash(): string {
cachedHash ??= createHash('sha256')
.update(cachedText ?? this.contents)
.digest('hex');
cachedHash ??= calculateHash(cachedText ?? this.contents);

return cachedHash;
},
Expand Down Expand Up @@ -97,7 +95,7 @@ export function createOutputFile(
return cachedText;
},
get hash(): string {
cachedHash ??= createHash('sha256').update(this.contents).digest('hex');
cachedHash ??= calculateHash(this.contents);

return cachedHash;
},
Expand Down
21 changes: 10 additions & 11 deletions packages/angular/build/src/tools/esbuild/i18n-inliner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
*/

import assert from 'node:assert';
import { createHash } from 'node:crypto';
import { extname, join } from 'node:path';
import { serialize } from 'node:v8';
import { calculateHash, createContentHash } from '../../utils/hash';
import { WorkerPool } from '../../utils/worker-pool';
import { type BuildOutputFile, BuildOutputFileType, createOutputFile } from './bundler-files';
import { type PersistentCacheStore, createPersistentCacheStore } from './cache';
Expand Down Expand Up @@ -147,7 +147,7 @@ export class I18nInliner {
// Request inlining for each file that contains localize calls
const requests = [];

let fileCacheKeyBase: Uint8Array | undefined;
let fileCacheKeyBase: string | undefined;

for (const [filename, file] of this.#localizeFiles) {
let cacheKey: string | undefined;
Expand All @@ -160,17 +160,16 @@ export class I18nInliner {
// The options are digested here so that each file's key is derived from a fixed number
// of bytes. Hashing the options directly would re-hash the full set of messages, which
// can be several megabytes, once for every file.
fileCacheKeyBase ??= createHash('sha256')
.update(JSON.stringify({ locale, translation, missingTranslation, shouldOptimize }))
.digest();
fileCacheKeyBase ??= calculateHash(
JSON.stringify({ locale, translation, missingTranslation, shouldOptimize }),
);

// NOTE: If additional options are added, this may need to be updated.
// TODO: Consider xxhash or similar instead of SHA256
cacheKey = createHash('sha256')
.update(file.hash)
.update(filename)
.update(fileCacheKeyBase)
.digest('hex');
const hasher = createContentHash();
hasher.update(file.hash);
hasher.update(filename);
hasher.update(fileCacheKeyBase);
cacheKey = hasher.digest();

// Failure to get the value should not fail the transform
cacheResultPromise = this.#cache.get(cacheKey).catch(() => null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.dev/license
*/

import { createHash } from 'node:crypto';
import { readFile } from 'node:fs/promises';
import { createContentHash } from '../../utils/hash';
import { IMPORT_EXEC_ARGV } from '../../utils/server-rendering/esm-in-memory-loader/utils';
import { removeSourceMappingURL } from '../../utils/source-map';
import { WorkerPool, WorkerPoolOptions } from '../../utils/worker-pool';
Expand Down Expand Up @@ -138,11 +138,11 @@ export class JavaScriptTransformer {
if (this.cache) {
// Create a cache key from the file data and options that effect the output.
// NOTE: If additional options are added, this may need to be updated.
const hash = createHash('sha256');
hash.update(`${!!skipLinker}--${!!sideEffects}`);
hash.update(data);
hash.update(this.#fileCacheKeyBase);
cacheKey = hash.digest('hex');
const hasher = createContentHash();
hasher.update(`${!!skipLinker}--${!!sideEffects}`);
hasher.update(data);
hasher.update(this.#fileCacheKeyBase);
cacheKey = hasher.digest();

try {
const cached = await this.cache.get(cacheKey);
Expand Down
Loading