diff --git a/goldens/public-api/angular_devkit/build_angular/index.md b/goldens/public-api/angular_devkit/build_angular/index.md index e47d0218f21c..96ccd68b1e32 100644 --- a/goldens/public-api/angular_devkit/build_angular/index.md +++ b/goldens/public-api/angular_devkit/build_angular/index.md @@ -53,7 +53,7 @@ export interface BrowserBuilderOptions { outputHashing?: OutputHashing; outputPath: string; poll?: number; - polyfills?: string; + polyfills?: Polyfills; preserveSymlinks?: boolean; progress?: boolean; resourcesOutputPath?: string; @@ -180,7 +180,7 @@ export interface KarmaBuilderOptions { karmaConfig: string; main: string; poll?: number; - polyfills?: string; + polyfills?: Polyfills_2; preserveSymlinks?: boolean; progress?: boolean; reporters?: string[]; diff --git a/packages/angular_devkit/build_angular/src/builders/browser-esbuild/options.ts b/packages/angular_devkit/build_angular/src/builders/browser-esbuild/options.ts index 3727079d833c..55433039db7c 100644 --- a/packages/angular_devkit/build_angular/src/builders/browser-esbuild/options.ts +++ b/packages/angular_devkit/build_angular/src/builders/browser-esbuild/options.ts @@ -9,6 +9,7 @@ import { BuilderContext } from '@angular-devkit/architect'; import * as path from 'path'; import { normalizeAssetPatterns, normalizeOptimization, normalizeSourceMaps } from '../../utils'; +import { normalizePolyfills } from '../../utils/normalize-polyfills'; import { Schema as BrowserBuilderOptions, OutputHashing } from '../browser/schema'; /** @@ -33,10 +34,21 @@ export async function normalizeOptions( workspaceRoot, (projectMetadata.sourceRoot as string | undefined) ?? 'src', ); - // Normalize options const mainEntryPoint = path.join(workspaceRoot, options.main); - const polyfillsEntryPoint = options.polyfills && path.join(workspaceRoot, options.polyfills); + + // Currently esbuild do not support multiple files per entry-point + const [polyfillsEntryPoint, ...remainingPolyfills] = normalizePolyfills( + options.polyfills, + workspaceRoot, + ); + + if (remainingPolyfills.length) { + context.logger.warn( + `The 'polyfills' option currently does not support multiple entries by this experimental builder. The first entry will be used.`, + ); + } + const tsconfig = path.join(workspaceRoot, options.tsConfig); const outputPath = path.join(workspaceRoot, options.outputPath); const optimizationOptions = normalizeOptimization(options.optimization); diff --git a/packages/angular_devkit/build_angular/src/builders/browser-esbuild/schema.json b/packages/angular_devkit/build_angular/src/builders/browser-esbuild/schema.json index ce8d5d1d510a..7ab3abf1d8a6 100644 --- a/packages/angular_devkit/build_angular/src/builders/browser-esbuild/schema.json +++ b/packages/angular_devkit/build_angular/src/builders/browser-esbuild/schema.json @@ -17,8 +17,22 @@ "description": "The full path for the main entry point to the app, relative to the current workspace." }, "polyfills": { - "type": "string", - "description": "The full path for the polyfills file, relative to the current workspace." + "description": "Polyfills to be included in the build.", + "oneOf": [ + { + "type": "array", + "description": "A list of polyfills to include in the build. Can be a full path for a file, relative to the current workspace or module specifier. Example: 'zone.js/testing'.", + "items": { + "type": "string", + "uniqueItems": true + }, + "default": [] + }, + { + "type": "string", + "description": "The full path for the polyfills file, relative to the current workspace or a module specifier. Example: 'zone.js/testing'." + } + ] }, "tsConfig": { "type": "string", diff --git a/packages/angular_devkit/build_angular/src/builders/browser/schema.json b/packages/angular_devkit/build_angular/src/builders/browser/schema.json index 35168f55a59d..dbc21a8ac265 100644 --- a/packages/angular_devkit/build_angular/src/builders/browser/schema.json +++ b/packages/angular_devkit/build_angular/src/builders/browser/schema.json @@ -17,8 +17,22 @@ "description": "The full path for the main entry point to the app, relative to the current workspace." }, "polyfills": { - "type": "string", - "description": "The full path for the polyfills file, relative to the current workspace." + "description": "Polyfills to be included in the build.", + "oneOf": [ + { + "type": "array", + "description": "A list of polyfills to include in the build. Can be a full path for a file, relative to the current workspace or module specifier. Example: 'zone.js/testing'.", + "items": { + "type": "string", + "uniqueItems": true + }, + "default": [] + }, + { + "type": "string", + "description": "The full path for the polyfills file, relative to the current workspace or a module specifier. Example: 'zone.js/testing'." + } + ] }, "tsConfig": { "type": "string", diff --git a/packages/angular_devkit/build_angular/src/builders/browser/tests/options/polyfills_spec.ts b/packages/angular_devkit/build_angular/src/builders/browser/tests/options/polyfills_spec.ts index e47b22531437..fbf3e5ad850a 100644 --- a/packages/angular_devkit/build_angular/src/builders/browser/tests/options/polyfills_spec.ts +++ b/packages/angular_devkit/build_angular/src/builders/browser/tests/options/polyfills_spec.ts @@ -54,5 +54,16 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { harness.expectFile('dist/polyfills.js').toNotExist(); }); + + it('resolves module specifiers in array', async () => { + harness.useTarget('build', { + ...BASE_OPTIONS, + polyfills: ['zone.js', 'zone.js/testing'], + }); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + harness.expectFile('dist/polyfills.js').toExist(); + }); }); }); diff --git a/packages/angular_devkit/build_angular/src/builders/karma/schema.json b/packages/angular_devkit/build_angular/src/builders/karma/schema.json index 033b4712ca90..4ec3aedc78e0 100644 --- a/packages/angular_devkit/build_angular/src/builders/karma/schema.json +++ b/packages/angular_devkit/build_angular/src/builders/karma/schema.json @@ -17,8 +17,22 @@ "description": "The name of the Karma configuration file." }, "polyfills": { - "type": "string", - "description": "The name of the polyfills file." + "description": "Polyfills to be included in the build.", + "oneOf": [ + { + "type": "array", + "description": "A list of polyfills to include in the build. Can be a full path for a file, relative to the current workspace or module specifier. Example: 'zone.js/testing'.", + "items": { + "type": "string", + "uniqueItems": true + }, + "default": [] + }, + { + "type": "string", + "description": "The full path for the polyfills file, relative to the current workspace or a module specifier. Example: 'zone.js/testing'." + } + ] }, "assets": { "type": "array", diff --git a/packages/angular_devkit/build_angular/src/utils/build-options.ts b/packages/angular_devkit/build_angular/src/utils/build-options.ts index a986b9786415..e98395ef7ef6 100644 --- a/packages/angular_devkit/build_angular/src/utils/build-options.ts +++ b/packages/angular_devkit/build_angular/src/utils/build-options.ts @@ -59,7 +59,7 @@ export interface BuildOptions { statsJson: boolean; hmr?: boolean; main: string; - polyfills?: string; + polyfills: string[]; budgets: Budget[]; assets: AssetPatternClass[]; scripts: ScriptElement[]; diff --git a/packages/angular_devkit/build_angular/src/utils/normalize-builder-schema.ts b/packages/angular_devkit/build_angular/src/utils/normalize-builder-schema.ts index 138b25bc4aaa..43189d3a370c 100644 --- a/packages/angular_devkit/build_angular/src/utils/normalize-builder-schema.ts +++ b/packages/angular_devkit/build_angular/src/utils/normalize-builder-schema.ts @@ -20,6 +20,7 @@ import { normalizeFileReplacements, } from './normalize-file-replacements'; import { NormalizedOptimizationOptions, normalizeOptimization } from './normalize-optimization'; +import { normalizePolyfills } from './normalize-polyfills'; import { normalizeSourceMaps } from './normalize-source-maps'; import { getSupportedBrowsers } from './supported-browsers'; @@ -32,6 +33,7 @@ export type NormalizedBrowserBuilderSchema = BrowserBuilderSchema & assets: AssetPatternClass[]; fileReplacements: NormalizedFileReplacement[]; optimization: NormalizedOptimizationOptions; + polyfills: string[]; }; export function normalizeBrowserSchema( @@ -42,8 +44,6 @@ export function normalizeBrowserSchema( metadata: json.JsonObject, logger: logging.LoggerApi, ): NormalizedBrowserBuilderSchema { - const normalizedSourceMapOptions = normalizeSourceMaps(options.sourceMap || false); - return { ...options, cache: normalizeCacheOptions(metadata, workspaceRoot), @@ -55,7 +55,8 @@ export function normalizeBrowserSchema( ), fileReplacements: normalizeFileReplacements(options.fileReplacements || [], workspaceRoot), optimization: normalizeOptimization(options.optimization), - sourceMap: normalizedSourceMapOptions, + sourceMap: normalizeSourceMaps(options.sourceMap || false), + polyfills: normalizePolyfills(options.polyfills, workspaceRoot), preserveSymlinks: options.preserveSymlinks === undefined ? process.execArgv.includes('--preserve-symlinks') diff --git a/packages/angular_devkit/build_angular/src/utils/normalize-polyfills.ts b/packages/angular_devkit/build_angular/src/utils/normalize-polyfills.ts new file mode 100644 index 000000000000..8eb75745b0b9 --- /dev/null +++ b/packages/angular_devkit/build_angular/src/utils/normalize-polyfills.ts @@ -0,0 +1,28 @@ +/** + * @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.io/license + */ + +import { existsSync } from 'fs'; +import { resolve } from 'path'; + +export function normalizePolyfills( + polyfills: string[] | string | undefined, + root: string, +): string[] { + if (!polyfills) { + return []; + } + + const polyfillsList = Array.isArray(polyfills) ? polyfills : [polyfills]; + + return polyfillsList.map((p) => { + const resolvedPath = resolve(root, p); + + // If file doesn't exist, let the bundle resolve it using node module resolution. + return existsSync(resolvedPath) ? resolvedPath : p; + }); +} diff --git a/packages/angular_devkit/build_angular/src/webpack/configs/common.ts b/packages/angular_devkit/build_angular/src/webpack/configs/common.ts index be04758a5bc6..2331e6955e77 100644 --- a/packages/angular_devkit/build_angular/src/webpack/configs/common.ts +++ b/packages/angular_devkit/build_angular/src/webpack/configs/common.ts @@ -80,7 +80,7 @@ export async function getCommonConfig(wco: WebpackConfigOptions): Promise