diff --git a/packages/angular/build/src/tools/esbuild/angular-localize-init-warning-plugin.ts b/packages/angular/build/src/tools/esbuild/angular-localize-init-warning-plugin.ts new file mode 100644 index 000000000000..341d40b00541 --- /dev/null +++ b/packages/angular/build/src/tools/esbuild/angular-localize-init-warning-plugin.ts @@ -0,0 +1,53 @@ +/** + * @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 type { Plugin } from 'esbuild'; + +const NG_LOCALIZE_RESOLUTION = Symbol('NG_LOCALIZE_RESOLUTION'); + +/** + * This plugin addresses an issue where '@angular/localize/init' is directly imported, + * potentially resulting in undefined behavior. By detecting such imports, the plugin + * issues a warning and suggests including '@angular/localize/init' as a polyfill. + * + * @returns An esbuild plugin. + */ +export function createAngularLocalizeInitWarningPlugin(): Plugin { + return { + name: 'angular-localize-init-warning', + setup(build) { + build.onResolve({ filter: /^@angular\/localize\/init/ }, async (args) => { + if (args.pluginData?.[NG_LOCALIZE_RESOLUTION]) { + return null; + } + + const { importer, kind, resolveDir, namespace, pluginData = {} } = args; + pluginData[NG_LOCALIZE_RESOLUTION] = true; + + const result = await build.resolve(args.path, { + importer, + kind, + namespace, + pluginData, + resolveDir, + }); + + return { + ...result, + warnings: [ + ...result.warnings, + { + text: `Direct import of '@angular/localize/init' detected. This may lead to undefined behavior.`, + notes: [{ text: `Include '@angular/localize/init' as a polyfill instead.` }], + }, + ], + }; + }); + }, + }; +} diff --git a/packages/angular/build/src/tools/esbuild/application-code-bundle.ts b/packages/angular/build/src/tools/esbuild/application-code-bundle.ts index db2581eb9b00..281522d9259a 100644 --- a/packages/angular/build/src/tools/esbuild/application-code-bundle.ts +++ b/packages/angular/build/src/tools/esbuild/application-code-bundle.ts @@ -20,6 +20,7 @@ import { import { createCompilerPlugin } from './angular/compiler-plugin'; import { ComponentStylesheetBundler } from './angular/component-stylesheets'; import { SourceFileCache } from './angular/source-file-cache'; +import { createAngularLocalizeInitWarningPlugin } from './angular-localize-init-warning-plugin'; import { BundlerOptionsFactory } from './bundler-context'; import { createCompilerPluginOptions } from './compiler-plugin-options'; import { createExternalPackagesPlugin } from './external-packages-plugin'; @@ -68,6 +69,7 @@ export function createBrowserCodeBundleOptions( createLoaderImportAttributePlugin(), createWasmPlugin({ allowAsync: zoneless, cache: loadCache }), createSourcemapIgnorelistPlugin(), + createAngularLocalizeInitWarningPlugin(), createCompilerPlugin( // JS/TS options pluginOptions, @@ -288,6 +290,7 @@ export function createServerMainCodeBundleOptions( plugins: [ createWasmPlugin({ allowAsync: zoneless, cache: loadResultCache }), createSourcemapIgnorelistPlugin(), + createAngularLocalizeInitWarningPlugin(), createCompilerPlugin( // JS/TS options { ...pluginOptions, noopTypeScriptCompilation: true }, @@ -424,6 +427,7 @@ export function createSsrEntryCodeBundleOptions( supported: getFeatureSupport(target, true), plugins: [ createSourcemapIgnorelistPlugin(), + createAngularLocalizeInitWarningPlugin(), createCompilerPlugin( // JS/TS options { ...pluginOptions, noopTypeScriptCompilation: true },