@@ -99,7 +99,6 @@ async function serializeTranslation(
9999 */
100100export interface I18nInlinerOptions {
101101 missingTranslation : 'error' | 'warning' | 'ignore' ;
102- outputFiles : BuildOutputFile [ ] ;
103102 persistentCachePath ?: string ;
104103 localizeVersion ?: string ;
105104}
@@ -165,19 +164,36 @@ export class I18nInliner {
165164 #transformedFileCache: Cache < TransformedFileResult > | undefined ;
166165 #translationCache: Cache < Uint8Array > | undefined ;
167166 #generation = 0 ;
168- readonly #localizeFiles: ReadonlyMap < string , BuildOutputFile > ;
169- readonly #unmodifiedFiles: Array < BuildOutputFile > ;
170167
171168 constructor (
172169 private readonly options : I18nInlinerOptions ,
173170 maxThreads ?: number ,
174171 ) {
175- this . #unmodifiedFiles = [ ] ;
176- const { outputFiles, missingTranslation } = options ;
177- const files = new Map < string , BuildOutputFile > ( ) ;
172+ const { missingTranslation } = options ;
173+
174+ this . #workerPool = new WorkerPool ( {
175+ filename : require . resolve ( './i18n-inliner-worker' ) ,
176+ maxThreads,
177+ // Extract options to ensure only the named options are serialized and sent to the worker
178+ workerData : {
179+ missingTranslation,
180+ } ,
181+ } ) ;
182+ }
178183
179- const pendingMaps = [ ] ;
180- for ( const file of outputFiles ) {
184+ #partitionFiles( files : Iterable < BuildOutputFile > ) : {
185+ filenames : string [ ] ;
186+ localizeFiles : Map < string , BuildOutputFile > ;
187+ localizeMaps : Map < string , BuildOutputFile > ;
188+ unmodifiedFiles : BuildOutputFile [ ] ;
189+ } {
190+ const filenames : string [ ] = [ ] ;
191+ const localizeFiles = new Map < string , BuildOutputFile > ( ) ;
192+ const localizeMaps = new Map < string , BuildOutputFile > ( ) ;
193+ const unmodifiedFiles : BuildOutputFile [ ] = [ ] ;
194+
195+ const pendingMaps : BuildOutputFile [ ] = [ ] ;
196+ for ( const file of files ) {
181197 if ( file . type === BuildOutputFileType . Root || file . type === BuildOutputFileType . ServerRoot ) {
182198 // Skip also the server entry-point.
183199 // Skip stats and similar files.
@@ -193,7 +209,8 @@ export class I18nInliner {
193209 const hasLocalize = contentBuffer . includes ( LOCALIZE_KEYWORD ) ;
194210
195211 if ( hasLocalize ) {
196- files . set ( file . path , file ) ;
212+ localizeFiles . set ( file . path , file ) ;
213+ filenames . push ( file . path ) ;
197214
198215 continue ;
199216 }
@@ -204,28 +221,20 @@ export class I18nInliner {
204221 continue ;
205222 }
206223
207- this . # unmodifiedFiles. push ( file ) ;
224+ unmodifiedFiles . push ( file ) ;
208225 }
209226
210227 // Check if any pending map files should be processed by checking if the parent JS file is present
211228 for ( const file of pendingMaps ) {
212- if ( files . has ( file . path . slice ( 0 , - 4 ) ) ) {
213- files . set ( file . path , file ) ;
229+ const jsPath = file . path . slice ( 0 , - 4 ) ;
230+ if ( localizeFiles . has ( jsPath ) ) {
231+ localizeMaps . set ( jsPath , file ) ;
214232 } else {
215- this . # unmodifiedFiles. push ( file ) ;
233+ unmodifiedFiles . push ( file ) ;
216234 }
217235 }
218236
219- this . #localizeFiles = files ;
220-
221- this . #workerPool = new WorkerPool ( {
222- filename : require . resolve ( './i18n-inliner-worker' ) ,
223- maxThreads,
224- // Extract options to ensure only the named options are serialized and sent to the worker
225- workerData : {
226- missingTranslation,
227- } ,
228- } ) ;
237+ return { filenames, localizeFiles, localizeMaps, unmodifiedFiles } ;
229238 }
230239
231240 /**
@@ -234,10 +243,12 @@ export class I18nInliner {
234243 * An adaptive 2D task-partitioning algorithm distributes (files x locales) work units
235244 * across all worker threads while caching AST metadata and sourcemaps in worker memory.
236245 *
246+ * @param files The build output files to transform.
237247 * @param locales The locales and translations to inline.
238248 * @returns A map of locale names to their inlined output files and diagnostics.
239249 */
240250 async inlineAll (
251+ files : Iterable < BuildOutputFile > ,
241252 locales : Iterable < LocaleInlineOptions > ,
242253 ) : Promise < Map < string , LocaleInlineResult > > {
243254 await this . initCache ( ) ;
@@ -250,16 +261,14 @@ export class I18nInliner {
250261 return new Map ( ) ;
251262 }
252263
264+ const { filenames, localizeFiles, localizeMaps, unmodifiedFiles } = this . #partitionFiles( files ) ;
265+
253266 const fileResultsByLocale = new Map < string , Map < string , TransformedFileResult > > ( ) ;
254267 for ( const { locale } of localeList ) {
255268 assert ( ! fileResultsByLocale . has ( locale ) , 'Duplicate locale provided to inliner: ' + locale ) ;
256269 fileResultsByLocale . set ( locale , new Map ( ) ) ;
257270 }
258271
259- const filenames = Array . from ( this . #localizeFiles. keys ( ) ) . filter (
260- ( name ) => ! name . endsWith ( '.map' ) ,
261- ) ;
262-
263272 // Process locales in sliding windows to cap peak worker memory.
264273 // Ensure the window has at least enough locales to saturate all available workers on high-core machines.
265274 const windowSize = Math . max ( DEFAULT_LOCALE_WINDOW_SIZE , this . #workerPool. maxThreads || 1 ) ;
@@ -304,7 +313,7 @@ export class I18nInliner {
304313 const cacheChecks : Promise < void > [ ] = [ ] ;
305314
306315 for ( const filename of filenames ) {
307- const file = this . # localizeFiles. get ( filename ) ;
316+ const file = localizeFiles . get ( filename ) ;
308317 assert ( file !== undefined , 'Localize file must exist: ' + filename ) ;
309318
310319 const fileEntriesPromises = windowLocales . map (
@@ -361,6 +370,8 @@ export class I18nInliner {
361370 // Adaptive 2D Sharding for uncached tasks in this window
362371 if ( uncachedByFile . size > 0 ) {
363372 await this . #processUncachedBatches(
373+ localizeFiles ,
374+ localizeMaps ,
364375 uncachedByFile ,
365376 fileResultsByLocale ,
366377 activeLocales ,
@@ -381,7 +392,7 @@ export class I18nInliner {
381392
382393 if ( fileResults ) {
383394 for ( const filename of filenames ) {
384- const originalFile = this . # localizeFiles. get ( filename ) ;
395+ const originalFile = localizeFiles . get ( filename ) ;
385396 assert ( originalFile !== undefined , 'Localize file must exist: ' + filename ) ;
386397
387398 const fileResult = fileResults . get ( filename ) ;
@@ -396,7 +407,7 @@ export class I18nInliner {
396407 outputFiles . push ( originalFile . clone ( ) ) ;
397408 }
398409
399- const originalMap = this . #localizeFiles . get ( filename + '.map' ) ;
410+ const originalMap = localizeMaps . get ( filename ) ;
400411 if ( fileResult . map !== undefined ) {
401412 outputFiles . push ( createOutputFile ( filename + '.map' , fileResult . map , type ) ) ;
402413 } else if ( originalMap !== undefined ) {
@@ -414,7 +425,7 @@ export class I18nInliner {
414425 }
415426
416427 // Include cloned unmodified files for every locale
417- outputFiles . push ( ...this . # unmodifiedFiles. map ( ( file ) => file . clone ( ) ) ) ;
428+ outputFiles . push ( ...unmodifiedFiles . map ( ( file ) => file . clone ( ) ) ) ;
418429
419430 resultsByLocale . set ( locale , {
420431 outputFiles,
@@ -427,6 +438,8 @@ export class I18nInliner {
427438 }
428439
429440 async #processUncachedBatches(
441+ localizeFiles : Map < string , BuildOutputFile > ,
442+ localizeMaps : Map < string , BuildOutputFile > ,
430443 uncachedByFile : Map < string , UncachedLocaleEntry [ ] > ,
431444 fileResultsByLocale : Map < string , Map < string , TransformedFileResult > > ,
432445 activeLocales ?: string [ ] ,
@@ -438,7 +451,7 @@ export class I18nInliner {
438451 // Extract file data and identify the heaviest file size in a single pass
439452 let maxFileSize = 0 ;
440453 const sortedFiles = Array . from ( uncachedByFile , ( [ filename , entries ] ) => {
441- const codeFile = this . # localizeFiles. get ( filename ) ;
454+ const codeFile = localizeFiles . get ( filename ) ;
442455 assert ( codeFile !== undefined , 'Localize file must exist: ' + filename ) ;
443456 const fileSize = codeFile . contents . byteLength ;
444457 if ( fileSize > maxFileSize ) {
@@ -456,7 +469,7 @@ export class I18nInliner {
456469 const workerTasks : Promise < void > [ ] = [ ] ;
457470
458471 for ( const { filename, entries, codeFile, fileSize } of sortedFiles ) {
459- const mapFile = this . #localizeFiles . get ( filename + '.map' ) ;
472+ const mapFile = localizeMaps . get ( filename ) ;
460473 const codeBlob = new Blob ( [ codeFile . contents ] ) ;
461474 const mapBlob = mapFile ? new Blob ( [ mapFile . contents ] ) : undefined ;
462475
@@ -548,20 +561,21 @@ export class I18nInliner {
548561 }
549562
550563 /**
551- * Performs inlining of translations for the provided locale and translations. The files that
552- * are processed originate from the files passed to the class constructor and filter by presence
553- * of the localize function keyword .
564+ * Performs inlining of translations for the provided locale and translations.
565+ *
566+ * @param files The build output files to transform .
554567 * @param locale The string representing the locale to inline.
555568 * @param translation The translation messages to use when inlining.
556569 * @param translationIntegrity An optional integrity value for the translation messages to use for caching.
557570 * @returns A promise that resolves to an array of OutputFiles representing a translated result.
558571 */
559572 async inlineForLocale (
573+ files : Iterable < BuildOutputFile > ,
560574 locale : string ,
561575 translation : Record < string , ɵParsedTranslation > | undefined ,
562576 translationIntegrity ?: string ,
563577 ) : Promise < LocaleInlineResult > {
564- const results = await this . inlineAll ( [ { locale, translation, translationIntegrity } ] ) ;
578+ const results = await this . inlineAll ( files , [ { locale, translation, translationIntegrity } ] ) ;
565579 const result = results . get ( locale ) ;
566580 assert ( result !== undefined , `Result for locale '${ locale } ' should be present.` ) ;
567581
0 commit comments