Skip to content

Commit 0c94254

Browse files
clydinangular-robot[bot]
authored andcommitted
fix(@angular-devkit/build-angular): fully remove third-party sourcemaps when disabled in esbuild builder
When using the esbuild-based browser application builder, third-party sourcemaps will now be fully removed when the `sourcemap` option's `vendor` sub-option is disabled. The `vendor` sub-option is disabled by default and is only enabled if explicitly enabled within the project's configuration. Sourcemaps are considered third-party if their referencing code is contained within a `node_modules` directory. Previously, sourcemap URL comments may have been unintentionally left intact when processing third-party code via the Babel-based JavaScript transformer. These sourcemap URL comments are now removed correctly when code is both transformed and return directly when no transformation is required.
1 parent 8cf1254 commit 0c94254

File tree

3 files changed

+143
-3
lines changed

3 files changed

+143
-3
lines changed

packages/angular_devkit/build_angular/src/builders/browser-esbuild/javascript-transformer-worker.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ async function transformWithBabel({
6969
const result = await transformAsync(data, {
7070
filename,
7171
inputSourceMap: (useInputSourcemap ? undefined : false) as undefined,
72-
sourceMaps: options.sourcemap ? 'inline' : false,
72+
sourceMaps: useInputSourcemap ? 'inline' : false,
7373
compact: false,
7474
configFile: false,
7575
babelrc: false,
@@ -94,5 +94,11 @@ async function transformWithBabel({
9494
],
9595
});
9696

97-
return result?.code ?? data;
97+
const outputCode = result?.code ?? data;
98+
99+
// Strip sourcemaps if they should not be used.
100+
// Babel will keep the original comments even if sourcemaps are disabled.
101+
return useInputSourcemap
102+
? outputCode
103+
: outputCode.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, '');
98104
}

packages/angular_devkit/build_angular/src/builders/browser-esbuild/javascript-transformer.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,14 @@ export class JavaScriptTransformer {
8585
forceAsyncTransformation = data.includes('async') && /async(?:\s+function)?\s*\*/.test(data);
8686

8787
if (!forceAsyncTransformation) {
88-
return Buffer.from(data, 'utf-8');
88+
const keepSourcemap =
89+
this.#commonOptions.sourcemap &&
90+
(!!this.#commonOptions.thirdPartySourcemaps || !/[\\/]node_modules[\\/]/.test(filename));
91+
92+
return Buffer.from(
93+
keepSourcemap ? data : data.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, ''),
94+
'utf-8',
95+
);
8996
}
9097
}
9198

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import { buildEsbuildBrowser } from '../../index';
10+
import { BASE_OPTIONS, BROWSER_BUILDER_INFO, describeBuilder } from '../setup';
11+
12+
describeBuilder(buildEsbuildBrowser, BROWSER_BUILDER_INFO, (harness) => {
13+
describe('Option: "sourceMap"', () => {
14+
it('should not generate script sourcemap files by default', async () => {
15+
harness.useTarget('build', {
16+
...BASE_OPTIONS,
17+
sourceMap: undefined,
18+
});
19+
20+
const { result } = await harness.executeOnce();
21+
22+
expect(result?.success).toBe(true);
23+
24+
harness.expectFile('dist/main.js.map').toNotExist();
25+
});
26+
27+
it('should not generate script sourcemap files when false', async () => {
28+
harness.useTarget('build', {
29+
...BASE_OPTIONS,
30+
sourceMap: false,
31+
});
32+
33+
const { result } = await harness.executeOnce();
34+
35+
expect(result?.success).toBe(true);
36+
37+
harness.expectFile('dist/main.js.map').toNotExist();
38+
});
39+
40+
it('should not generate script sourcemap files when scripts suboption is false', async () => {
41+
harness.useTarget('build', {
42+
...BASE_OPTIONS,
43+
sourceMap: { scripts: false },
44+
});
45+
46+
const { result } = await harness.executeOnce();
47+
48+
expect(result?.success).toBe(true);
49+
50+
harness.expectFile('dist/main.js.map').toNotExist();
51+
});
52+
53+
it('should generate script sourcemap files when true', async () => {
54+
harness.useTarget('build', {
55+
...BASE_OPTIONS,
56+
sourceMap: true,
57+
});
58+
59+
const { result } = await harness.executeOnce();
60+
61+
expect(result?.success).toBe(true);
62+
63+
harness.expectFile('dist/main.js.map').toExist();
64+
});
65+
66+
it('should generate script sourcemap files when scripts suboption is true', async () => {
67+
harness.useTarget('build', {
68+
...BASE_OPTIONS,
69+
sourceMap: { scripts: true },
70+
});
71+
72+
const { result } = await harness.executeOnce();
73+
74+
expect(result?.success).toBe(true);
75+
76+
harness.expectFile('dist/main.js.map').toExist();
77+
});
78+
79+
it('should not include third-party sourcemaps when true', async () => {
80+
await harness.writeFile('src/polyfills.js', `console.log('main');`);
81+
82+
harness.useTarget('build', {
83+
...BASE_OPTIONS,
84+
sourceMap: true,
85+
});
86+
87+
const { result } = await harness.executeOnce();
88+
89+
expect(result?.success).toBe(true);
90+
91+
harness.expectFile('dist/main.js.map').content.not.toContain('/core/index.ts');
92+
harness.expectFile('dist/main.js.map').content.not.toContain('/common/index.ts');
93+
});
94+
95+
it('should not include third-party sourcemaps when vendor suboption is false', async () => {
96+
await harness.writeFile('src/polyfills.js', `console.log('main');`);
97+
98+
harness.useTarget('build', {
99+
...BASE_OPTIONS,
100+
sourceMap: { scripts: true, vendor: false },
101+
});
102+
103+
const { result } = await harness.executeOnce();
104+
105+
expect(result?.success).toBe(true);
106+
107+
harness.expectFile('dist/main.js.map').content.not.toContain('/core/index.ts');
108+
harness.expectFile('dist/main.js.map').content.not.toContain('/common/index.ts');
109+
});
110+
111+
it('should include third-party sourcemaps when vendor suboption is true', async () => {
112+
await harness.writeFile('src/polyfills.js', `console.log('main');`);
113+
114+
harness.useTarget('build', {
115+
...BASE_OPTIONS,
116+
sourceMap: { scripts: true, vendor: true },
117+
});
118+
119+
const { result } = await harness.executeOnce();
120+
121+
expect(result?.success).toBe(true);
122+
123+
harness.expectFile('dist/main.js.map').content.toContain('/core/index.ts');
124+
harness.expectFile('dist/main.js.map').content.toContain('/common/index.ts');
125+
});
126+
});
127+
});

0 commit comments

Comments
 (0)