@@ -13,6 +13,7 @@ import fs from 'node:fs/promises';
1313import { createRequire } from 'node:module' ;
1414import os from 'node:os' ;
1515import path from 'node:path' ;
16+ import { pathToFileURL } from 'node:url' ;
1617
1718import type { BuildOutputFile } from '../../../dist/@angular/build/src/tools/esbuild/bundler-files.d.ts' ;
1819import type { LocaleInlineOptions } from '../../../dist/@angular/build/src/tools/esbuild/i18n-inliner.d.ts' ;
@@ -124,6 +125,15 @@ function calculateInputSizeBytes(files: BuildOutputFile[]): number {
124125 } , 0 ) ;
125126}
126127
128+ /**
129+ * Note on Worker Pool Lifecycle:
130+ * Each scenario's run() method instantiates and closes an I18nInliner per iteration.
131+ * This is designed as a macro benchmark to reflect the cold-start behavior of single-shot
132+ * CLI build invocations (including worker thread pool initialization, task dispatch,
133+ * inlining transformations, and thread pool shutdown). Warmup iterations warm up the
134+ * main-thread V8 isolate and runtime paths, while worker threads are initialized per iteration.
135+ */
136+
127137/**
128138 * 1. Standard App Scenario:
129139 * 1 main bundle (1 MB) + 20 route chunks (50 KB) = ~2 MB input JS, 8 locales, maps enabled.
@@ -350,15 +360,22 @@ export function createPersistentCacheWarmScenario(
350360
351361 // Prime the persistent cache out-of-process so cold worker thread allocations
352362 // do not inflate this process's RSS metrics.
363+ const scenariosUrl = pathToFileURL ( path . resolve ( import . meta. dirname , './scenarios.mts' ) ) . href ;
353364 const primerCode =
354- `import { primeCache } from ${ JSON . stringify ( path . resolve ( import . meta . dirname , './scenarios.mts' ) ) } ;\n` +
365+ `import { primeCache } from ${ JSON . stringify ( scenariosUrl ) } ;\n` +
355366 `await primeCache(${ JSON . stringify ( cacheDir ) } , ${ options . concurrency ?? 'undefined' } );\n` ;
356367
357- spawnSync (
368+ const primerProc = spawnSync (
358369 process . execPath ,
359370 [ '--no-warnings=ExperimentalWarning' , '--experimental-transform-types' , '-e' , primerCode ] ,
360371 { stdio : 'inherit' } ,
361372 ) ;
373+
374+ if ( primerProc . status !== 0 ) {
375+ throw new Error (
376+ `Failed to prime cache for persistent-cache-warm scenario: ${ primerProc . error ?. message ?? primerProc . status } ` ,
377+ ) ;
378+ }
362379 } ,
363380
364381 async run ( ) {
@@ -384,11 +401,60 @@ export function createPersistentCacheWarmScenario(
384401 } ;
385402}
386403
404+ /**
405+ * 6. Large Enterprise (10k translations) Scenario:
406+ * 1 main bundle (3 MB) + 100 chunks (50 KB), 32 locales, 10,000 translations, sourcemaps ON.
407+ * Maximum scale stress test for binary translation tables, memory retention, and multi-locale windows.
408+ */
409+ export function createLargeEnterpriseScenario (
410+ options : ScenarioFactoryOptions = { } ,
411+ ) : BenchmarkScenario {
412+ let workload : GeneratedWorkload | undefined ;
413+
414+ return {
415+ name : 'large-enterprise-10k' ,
416+ description :
417+ 'Large Enterprise (10k msgs): 1 main (3 MB) + 100 chunks (50 KB), 32 locales, 10,000 translations' ,
418+ get inputSizeBytes ( ) {
419+ return workload ?. totalInputSizeBytes ?? 0 ;
420+ } ,
421+ get localeCount ( ) {
422+ return DEFAULT_LOCALES_32 . length ;
423+ } ,
424+ async setup ( ) {
425+ await initializeFixtures ( ) ;
426+ const files = createBundleSet ( 3 * 1024 * 1024 , 100 , 50 * 1024 , 10000 , true ) ;
427+ const locales = generateTranslations ( DEFAULT_LOCALES_32 , 10000 ) ;
428+
429+ workload = {
430+ files,
431+ locales,
432+ totalInputSizeBytes : calculateInputSizeBytes ( files ) ,
433+ } ;
434+ } ,
435+ async run ( ) {
436+ if ( ! workload ) {
437+ return ;
438+ }
439+ const inliner = new I18nInliner ( {
440+ missingTranslation : 'warning' ,
441+ maxConcurrency : options . concurrency ,
442+ } ) ;
443+ try {
444+ await inliner . inlineAll ( workload . files , workload . locales ) ;
445+ } finally {
446+ await inliner . close ( ) ;
447+ }
448+ } ,
449+ } ;
450+ }
451+
387452export function getAllScenarios ( options : ScenarioFactoryOptions = { } ) : BenchmarkScenario [ ] {
388453 return [
389454 createStandardAppScenario ( options ) ,
390455 createStandardAppNoMapsScenario ( options ) ,
391456 createEnterpriseScenario ( options ) ,
457+ createLargeEnterpriseScenario ( options ) ,
392458 createMonolithicScenario ( options ) ,
393459 createPersistentCacheWarmScenario ( options ) ,
394460 ] ;
0 commit comments