Skip to content

Commit 667f43a

Browse files
alan-agius4clydin
authored andcommitted
fix(@angular-devkit/build-angular): correctly resolve polyfills when baseUrl URL is not set to root
Prior to this change when `baseUrl` was set to non-root or not set polyfills were not correctly resolved. Internally Esbuild uses the `baseUrl` to resolve non relative imports. Closes: #25341
1 parent 863e142 commit 667f43a

File tree

2 files changed

+81
-44
lines changed

2 files changed

+81
-44
lines changed

packages/angular_devkit/build_angular/src/builders/application/tests/options/polyfills_spec.ts

Lines changed: 55 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,61 +9,78 @@
99
import { buildApplication } from '../../index';
1010
import { APPLICATION_BUILDER_INFO, BASE_OPTIONS, describeBuilder } from '../setup';
1111

12+
const testsVariants: [suitName: string, baseUrl: string | undefined][] = [
13+
['When "baseUrl" is set to "./"', './'],
14+
[`When "baseUrl" is not set`, undefined],
15+
[`When "baseUrl" is set to non root path`, './project/foo'],
16+
];
17+
1218
describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
13-
describe('Option: "polyfills"', () => {
14-
it('uses a provided TypeScript file', async () => {
15-
harness.useTarget('build', {
16-
...BASE_OPTIONS,
17-
polyfills: ['src/polyfills.ts'],
19+
for (const [suitName, baseUrl] of testsVariants) {
20+
describe(suitName, () => {
21+
beforeEach(async () => {
22+
await harness.modifyFile('tsconfig.json', (content) => {
23+
const tsconfig = JSON.parse(content);
24+
tsconfig.compilerOptions.baseUrl = baseUrl;
25+
26+
return JSON.stringify(tsconfig);
27+
});
1828
});
1929

20-
const { result } = await harness.executeOnce();
21-
22-
expect(result?.success).toBe(true);
30+
it('uses a provided TypeScript file', async () => {
31+
harness.useTarget('build', {
32+
...BASE_OPTIONS,
33+
polyfills: ['src/polyfills.ts'],
34+
});
2335

24-
harness.expectFile('dist/polyfills.js').toExist();
25-
});
36+
const { result } = await harness.executeOnce();
2637

27-
it('uses a provided JavaScript file', async () => {
28-
await harness.writeFile('src/polyfills.js', `console.log('main');`);
38+
expect(result?.success).toBe(true);
2939

30-
harness.useTarget('build', {
31-
...BASE_OPTIONS,
32-
polyfills: ['src/polyfills.js'],
40+
harness.expectFile('dist/polyfills.js').toExist();
3341
});
3442

35-
const { result } = await harness.executeOnce();
43+
it('uses a provided JavaScript file', async () => {
44+
await harness.writeFile('src/polyfills.js', `console.log('main');`);
3645

37-
expect(result?.success).toBe(true);
46+
harness.useTarget('build', {
47+
...BASE_OPTIONS,
48+
polyfills: ['src/polyfills.js'],
49+
});
3850

39-
harness.expectFile('dist/polyfills.js').content.toContain(`console.log("main")`);
40-
});
51+
const { result } = await harness.executeOnce();
4152

42-
it('fails and shows an error when file does not exist', async () => {
43-
harness.useTarget('build', {
44-
...BASE_OPTIONS,
45-
polyfills: ['src/missing.ts'],
53+
expect(result?.success).toBe(true);
54+
55+
harness.expectFile('dist/polyfills.js').content.toContain(`console.log("main")`);
4656
});
4757

48-
const { result, logs } = await harness.executeOnce({ outputLogsOnFailure: false });
58+
it('fails and shows an error when file does not exist', async () => {
59+
harness.useTarget('build', {
60+
...BASE_OPTIONS,
61+
polyfills: ['src/missing.ts'],
62+
});
4963

50-
expect(result?.success).toBe(false);
51-
expect(logs).toContain(
52-
jasmine.objectContaining({ message: jasmine.stringMatching('Could not resolve') }),
53-
);
64+
const { result, logs } = await harness.executeOnce({ outputLogsOnFailure: false });
5465

55-
harness.expectFile('dist/polyfills.js').toNotExist();
56-
});
66+
expect(result?.success).toBe(false);
67+
expect(logs).toContain(
68+
jasmine.objectContaining({ message: jasmine.stringMatching('Could not resolve') }),
69+
);
5770

58-
it('resolves module specifiers in array', async () => {
59-
harness.useTarget('build', {
60-
...BASE_OPTIONS,
61-
polyfills: ['zone.js', 'zone.js/testing'],
71+
harness.expectFile('dist/polyfills.js').toNotExist();
6272
});
6373

64-
const { result } = await harness.executeOnce();
65-
expect(result?.success).toBeTrue();
66-
harness.expectFile('dist/polyfills.js').toExist();
74+
it('resolves module specifiers in array', async () => {
75+
harness.useTarget('build', {
76+
...BASE_OPTIONS,
77+
polyfills: ['zone.js', 'zone.js/testing'],
78+
});
79+
80+
const { result } = await harness.executeOnce();
81+
expect(result?.success).toBeTrue();
82+
harness.expectFile('dist/polyfills.js').toExist();
83+
});
6784
});
68-
});
85+
}
6986
});

packages/angular_devkit/build_angular/src/tools/esbuild/application-code-bundle.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import type { BuildOptions } from 'esbuild';
1010
import assert from 'node:assert';
1111
import { createHash } from 'node:crypto';
1212
import { readFile } from 'node:fs/promises';
13-
import { join, relative } from 'node:path';
13+
import { extname, join, relative } from 'node:path';
1414
import type { NormalizedApplicationBuildOptions } from '../../builders/application/options';
1515
import { allowMangle } from '../../utils/environment-options';
1616
import { SourceFileCache, createCompilerPlugin } from './angular/compiler-plugin';
@@ -112,11 +112,31 @@ export function createBrowserCodeBundleOptions(
112112
buildOptions.plugins?.unshift(
113113
createVirtualModulePlugin({
114114
namespace,
115-
loadContent: () => ({
116-
contents: polyfills.map((file) => `import '${file.replace(/\\/g, '/')}';`).join('\n'),
117-
loader: 'js',
118-
resolveDir: workspaceRoot,
119-
}),
115+
loadContent: async (_, build) => {
116+
const polyfillPaths = await Promise.all(
117+
polyfills.map(async (path) => {
118+
if (path.startsWith('zone.js') || !extname(path)) {
119+
return path;
120+
}
121+
122+
const potentialPathRelative = './' + path;
123+
const result = await build.resolve(potentialPathRelative, {
124+
kind: 'import-statement',
125+
resolveDir: workspaceRoot,
126+
});
127+
128+
return result.path ? potentialPathRelative : path;
129+
}),
130+
);
131+
132+
return {
133+
contents: polyfillPaths
134+
.map((file) => `import '${file.replace(/\\/g, '/')}';`)
135+
.join('\n'),
136+
loader: 'js',
137+
resolveDir: workspaceRoot,
138+
};
139+
},
120140
}),
121141
);
122142
}

0 commit comments

Comments
 (0)