From e17d42d8736e83742287139bb3c9d4921d0f1f5d Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Thu, 26 Feb 2026 10:32:31 +0000 Subject: [PATCH] perf(@angular/build): fix memory leak in `ng serve` with i18n When running `ng serve` with i18n configured, every file save (.html files) triggers a rebuild that creates a new piscina `ThreadPool` for `i18n-inline-worker.js`. Old pools and their worker threads were not properly cleaned up because the inliner was closed prematurely, causing unbounded memory growth. This commit ensures that the inliner is kept open while processing template updates, resolving the issue where orphaned threads were created. Closes #32584 --- .../build/src/builders/application/i18n.ts | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/packages/angular/build/src/builders/application/i18n.ts b/packages/angular/build/src/builders/application/i18n.ts index ae37efa674e4..081be50e7a9f 100644 --- a/packages/angular/build/src/builders/application/i18n.ts +++ b/packages/angular/build/src/builders/application/i18n.ts @@ -123,48 +123,48 @@ export async function inlineI18n( inlineResult.prerenderedRoutes = { ...inlineResult.prerenderedRoutes, ...generatedRoutes }; updatedOutputFiles.push(...localeOutputFiles); } - } finally { - await inliner.close(); - } - // Update the result with all localized files. - executionResult.outputFiles = [ - // Root and SSR entry files are not modified. - ...unModifiedOutputFiles, - // Updated files for each locale. - ...updatedOutputFiles, - ]; - - // Assets are only changed if not using the flat output option - if (!i18nOptions.flatOutput) { - executionResult.assetFiles = updatedAssetFiles; - } - - // Inline any template updates if present - if (executionResult.templateUpdates?.size) { - // The development server only allows a single locale but issue a warning if used programmatically (experimental) - // with multiple locales and template HMR. - if (i18nOptions.inlineLocales.size > 1) { - inlineResult.warnings.push( - `Component HMR updates can only be inlined with a single locale. The first locale will be used.`, - ); + // Update the result with all localized files. + executionResult.outputFiles = [ + // Root and SSR entry files are not modified. + ...unModifiedOutputFiles, + // Updated files for each locale. + ...updatedOutputFiles, + ]; + + // Assets are only changed if not using the flat output option + if (!i18nOptions.flatOutput) { + executionResult.assetFiles = updatedAssetFiles; } - const firstLocale = [...i18nOptions.inlineLocales][0]; - - for (const [id, content] of executionResult.templateUpdates) { - const templateUpdateResult = await inliner.inlineTemplateUpdate( - firstLocale, - i18nOptions.locales[firstLocale].translation, - content, - id, - ); - executionResult.templateUpdates.set(id, templateUpdateResult.code); - inlineResult.errors.push(...templateUpdateResult.errors); - inlineResult.warnings.push(...templateUpdateResult.warnings); + + // Inline any template updates if present + if (executionResult.templateUpdates?.size) { + // The development server only allows a single locale but issue a warning if used programmatically (experimental) + // with multiple locales and template HMR. + if (i18nOptions.inlineLocales.size > 1) { + inlineResult.warnings.push( + `Component HMR updates can only be inlined with a single locale. The first locale will be used.`, + ); + } + const firstLocale = [...i18nOptions.inlineLocales][0]; + + for (const [id, content] of executionResult.templateUpdates) { + const templateUpdateResult = await inliner.inlineTemplateUpdate( + firstLocale, + i18nOptions.locales[firstLocale].translation, + content, + id, + ); + executionResult.templateUpdates.set(id, templateUpdateResult.code); + inlineResult.errors.push(...templateUpdateResult.errors); + inlineResult.warnings.push(...templateUpdateResult.warnings); + } } - } - return inlineResult; + return inlineResult; + } finally { + await inliner.close(); + } } /**