diff --git a/packages/angular_devkit/build_angular/src/builders/application/options.ts b/packages/angular_devkit/build_angular/src/builders/application/options.ts index 3679e3d5f2..14475a434a 100644 --- a/packages/angular_devkit/build_angular/src/builders/application/options.ts +++ b/packages/angular_devkit/build_angular/src/builders/application/options.ts @@ -58,7 +58,7 @@ interface InternalOptions { * Indicates whether all node packages should be marked as external. * Currently used by the dev-server to support prebundling. */ - externalPackages?: boolean; + externalPackages?: boolean | { exclude: string[] }; /** * Forces the output from the localize post-processing to not create nested directories per locale output. diff --git a/packages/angular_devkit/build_angular/src/tools/esbuild/application-code-bundle.ts b/packages/angular_devkit/build_angular/src/tools/esbuild/application-code-bundle.ts index 7b5a08cdcd..1ddfdd5926 100644 --- a/packages/angular_devkit/build_angular/src/tools/esbuild/application-code-bundle.ts +++ b/packages/angular_devkit/build_angular/src/tools/esbuild/application-code-bundle.ts @@ -66,9 +66,17 @@ export function createBrowserCodeBundleOptions( if (options.externalPackages) { // Package files affected by a customized loader should not be implicitly marked as external - if (options.loaderExtensions || options.plugins) { + if ( + options.loaderExtensions || + options.plugins || + typeof options.externalPackages === 'object' + ) { // Plugin must be added after custom plugins to ensure any added loader options are considered - buildOptions.plugins?.push(createExternalPackagesPlugin()); + buildOptions.plugins?.push( + createExternalPackagesPlugin( + options.externalPackages !== true ? options.externalPackages : undefined, + ), + ); } else { // Safe to use the packages external option directly buildOptions.packages = 'external'; diff --git a/packages/angular_devkit/build_angular/src/tools/esbuild/external-packages-plugin.ts b/packages/angular_devkit/build_angular/src/tools/esbuild/external-packages-plugin.ts index a090503b3e..fda108b984 100644 --- a/packages/angular_devkit/build_angular/src/tools/esbuild/external-packages-plugin.ts +++ b/packages/angular_devkit/build_angular/src/tools/esbuild/external-packages-plugin.ts @@ -18,21 +18,23 @@ const EXTERNAL_PACKAGE_RESOLUTION = Symbol('EXTERNAL_PACKAGE_RESOLUTION'); * * @returns An esbuild plugin. */ -export function createExternalPackagesPlugin(): Plugin { +export function createExternalPackagesPlugin(options?: { exclude?: string[] }): Plugin { + const exclusions = options?.exclude?.length ? new Set(options.exclude) : undefined; + return { name: 'angular-external-packages', setup(build) { - // Safe to use native packages external option if no loader options present - if ( - build.initialOptions.loader === undefined || - Object.keys(build.initialOptions.loader).length === 0 - ) { + const loaderOptionKeys = + build.initialOptions.loader && Object.keys(build.initialOptions.loader); + + // Safe to use native packages external option if no loader options or exclusions present + if (!exclusions && !loaderOptionKeys?.length) { build.initialOptions.packages = 'external'; return; } - const loaderFileExtensions = new Set(Object.keys(build.initialOptions.loader)); + const loaderFileExtensions = new Set(loaderOptionKeys); // Only attempt resolve of non-relative and non-absolute paths build.onResolve({ filter: /^[^./]/ }, async (args) => { @@ -40,6 +42,10 @@ export function createExternalPackagesPlugin(): Plugin { return null; } + if (exclusions?.has(args.path)) { + return null; + } + const { importer, kind, resolveDir, namespace, pluginData = {} } = args; pluginData[EXTERNAL_PACKAGE_RESOLUTION] = true;