refactor(@angular-devkit/build-angular): allow internal externalPackages option to exclude packages

To support future use cases, the internal `externalPackages` application builder option now also
accepts an object form with an `exclude` array field in addition to the existing boolean value.
The `exclude` capability allows for specifying individual packages that should always be bundled
when using the external packages functionality. Currently the external packages functionality is
only used by the Vite-based development server.
This commit is contained in:
Charles Lyding 2024-01-31 12:27:40 -05:00 committed by Charles
parent 2a25e06eae
commit 83000bdef2
3 changed files with 24 additions and 10 deletions

View File

@ -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.

View File

@ -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';

View File

@ -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;