Skip to content

Commit

Permalink
fix(@angular-devkit/build-angular): allow multiple polyfills with esb…
Browse files Browse the repository at this point in the history
…uild-based builder

Previously when using the esbuild-based browser application, the `polyfills` option was
limited to only one entry. The option now can be used with multiple entries and has full
support for package resolution. This provides equivalent behavior to the current default
Webpack-based builder.
  • Loading branch information
clydin authored and angular-robot[bot] committed Mar 30, 2023
1 parent 50b9e59 commit cdfa7ca
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 18 deletions.
Expand Up @@ -313,6 +313,7 @@ function createCodeBundleOptions(
const {
workspaceRoot,
entryPoints,
polyfills,
optimizationOptions,
sourcemapOptions,
tsconfig,
Expand All @@ -327,7 +328,7 @@ function createCodeBundleOptions(
tailwindConfiguration,
} = options;

return {
const buildOptions: BuildOptions = {
absWorkingDir: workspaceRoot,
bundle: true,
format: 'esm',
Expand Down Expand Up @@ -391,6 +392,39 @@ function createCodeBundleOptions(
'ngJitMode': jit ? 'true' : 'false',
},
};

if (polyfills?.length) {
const namespace = 'angular:polyfills';
buildOptions.entryPoints = {
...buildOptions.entryPoints,
['polyfills']: namespace,
};

buildOptions.plugins?.unshift({
name: 'angular-polyfills',
setup(build) {
build.onResolve({ filter: /^angular:polyfills$/ }, (args) => {
if (args.kind !== 'entry-point') {
return null;
}

return {
path: 'entry',
namespace,
};
});
build.onLoad({ filter: /./, namespace }, () => {
return {
contents: polyfills.map((file) => `import '${file.replace(/\\/g, '/')}';`).join('\n'),
loader: 'js',
resolveDir: workspaceRoot,
};
});
},
});
}

return buildOptions;
}

/**
Expand Down
Expand Up @@ -12,7 +12,6 @@ import { createRequire } from 'node:module';
import path from 'node:path';
import { normalizeAssetPatterns, normalizeOptimization, normalizeSourceMaps } from '../../utils';
import { normalizeCacheOptions } from '../../utils/normalize-cache';
import { normalizePolyfills } from '../../utils/normalize-polyfills';
import { generateEntryPoints } from '../../utils/package-chunk-sort';
import { getIndexInputFile, getIndexOutputFile } from '../../utils/webpack-browser-config';
import { normalizeGlobalStyles } from '../../webpack/utils/helpers';
Expand Down Expand Up @@ -46,19 +45,6 @@ export async function normalizeOptions(
const cacheOptions = normalizeCacheOptions(projectMetadata, workspaceRoot);

const mainEntryPoint = path.join(workspaceRoot, options.main);

// 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);
Expand Down Expand Up @@ -133,9 +119,6 @@ export async function normalizeOptions(
const entryPoints: Record<string, string> = {
main: mainEntryPoint,
};
if (polyfillsEntryPoint) {
entryPoints['polyfills'] = polyfillsEntryPoint;
}

let indexHtmlOptions;
if (options.index) {
Expand All @@ -162,6 +145,7 @@ export async function normalizeOptions(
extractLicenses,
inlineStyleLanguage = 'css',
poll,
polyfills,
preserveSymlinks,
statsJson,
stylePreprocessorOptions,
Expand All @@ -182,6 +166,7 @@ export async function normalizeOptions(
inlineStyleLanguage,
jit: !aot,
stats: !!statsJson,
polyfills: polyfills === undefined || Array.isArray(polyfills) ? polyfills : [polyfills],
poll,
// If not explicitly set, default to the Node.js process argument
preserveSymlinks: preserveSymlinks ?? process.execArgv.includes('--preserve-symlinks'),
Expand Down
@@ -0,0 +1,69 @@
/**
* @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 { buildEsbuildBrowser } from '../../index';
import { BASE_OPTIONS, BROWSER_BUILDER_INFO, describeBuilder } from '../setup';

describeBuilder(buildEsbuildBrowser, BROWSER_BUILDER_INFO, (harness) => {
describe('Option: "polyfills"', () => {
it('uses a provided TypeScript file', async () => {
harness.useTarget('build', {
...BASE_OPTIONS,
polyfills: 'src/polyfills.ts',
});

const { result } = await harness.executeOnce();

expect(result?.success).toBe(true);

harness.expectFile('dist/polyfills.js').toExist();
});

it('uses a provided JavaScript file', async () => {
await harness.writeFile('src/polyfills.js', `console.log('main');`);

harness.useTarget('build', {
...BASE_OPTIONS,
polyfills: 'src/polyfills.js',
});

const { result } = await harness.executeOnce();

expect(result?.success).toBe(true);

harness.expectFile('dist/polyfills.js').content.toContain(`console.log("main")`);
});

it('fails and shows an error when file does not exist', async () => {
harness.useTarget('build', {
...BASE_OPTIONS,
polyfills: 'src/missing.ts',
});

const { result, logs } = await harness.executeOnce({ outputLogsOnFailure: false });

expect(result?.success).toBe(false);
expect(logs).toContain(
jasmine.objectContaining({ message: jasmine.stringMatching('Could not resolve') }),
);

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();
});
});
});

0 comments on commit cdfa7ca

Please sign in to comment.