Skip to content

Commit 3d133b3

Browse files
committed
fix(@angular/build): use distinct chunk naming for web worker bundles
Previously, `bundleWebWorker` inherited `chunkNames` from the main esbuild configuration while bundling web workers in a separate synchronous esbuild pass. When both the main application bundle and a web worker dynamically imported modules that shared a common dependency, esbuild generated a shared chunk with identical content and the same `chunk-[hash].js` filename in both builds. During post-build chunk optimization, Rolldown traced only the main application entrypoint, merged the shared chunk into new optimized chunks, and removed the original `chunk-[hash].js` file from the build outputs while the unoptimized web worker chunks still imported it. Setting `chunkNames` to `'worker-chunk-[hash]'` in `bundleWebWorker` ensures that chunks emitted for web workers have distinct filenames and do not collide with main bundle chunks during metafile merging or chunk optimization. Closes #34292
1 parent 00c2509 commit 3d133b3

2 files changed

Lines changed: 77 additions & 0 deletions

File tree

‎packages/angular/build/src/builders/application/tests/behavior/web-workers-application_spec.ts‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,81 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
5757
// Ensure built worker is referenced in the application code
5858
harness.expectFile('dist/browser/main.js').content.toMatch(REFERENCED_WORKER_REGEXP);
5959
});
60+
61+
it('should emit all chunks referenced by a web worker when chunk optimization is enabled', async () => {
62+
harness.useTarget('build', {
63+
...BASE_OPTIONS,
64+
optimization: true,
65+
});
66+
67+
await harness.writeFile(
68+
'src/app/shared.ts',
69+
`export function sharedHelper(value: string) { return [value, value.toUpperCase(), value.toLowerCase(), value.repeat(3)].join('-'); }`,
70+
);
71+
await harness.writeFile(
72+
'src/app/lazy-a.ts',
73+
`import { sharedHelper } from './shared'; export const a = () => sharedHelper('a');`,
74+
);
75+
await harness.writeFile(
76+
'src/app/lazy-b.ts',
77+
`import { sharedHelper } from './shared'; export const b = () => sharedHelper('b');`,
78+
);
79+
await harness.writeFile('src/app/lazy-c.ts', `export const c = () => 'c';`);
80+
81+
await harness.writeFile(
82+
'src/app/worker.ts',
83+
`
84+
addEventListener('message', async () => {
85+
const [{ a }, { b }] = await Promise.all([import('./lazy-a'), import('./lazy-b')]);
86+
postMessage(a() + b());
87+
});
88+
`,
89+
);
90+
91+
await harness.writeFile(
92+
'src/app/app.component.ts',
93+
`
94+
import { Component } from '@angular/core';
95+
@Component({
96+
selector: 'app-root',
97+
standalone: false,
98+
template: '<h1>Worker Test</h1>',
99+
})
100+
export class AppComponent {
101+
worker = new Worker(new URL('./worker', import.meta.url), { type: 'module' });
102+
constructor() {
103+
Promise.all([import('./lazy-a'), import('./lazy-b'), import('./lazy-c')]).then(console.log);
104+
}
105+
}
106+
`,
107+
);
108+
109+
const { result } = await harness.executeOnce();
110+
expect(result?.success).toBeTrue();
111+
expect(harness.hasFileMatch('dist/browser', /^worker-chunk-/)).toBeTrue();
112+
113+
const mainContent = harness.readFile('dist/browser/main.js');
114+
const workerMatch = mainContent.match(/(worker-[A-Z0-9]{8}\.js)/);
115+
expect(workerMatch).not.toBeNull();
116+
117+
const visited = new Set<string>();
118+
const queue = ['main.js', workerMatch![1]];
119+
while (queue.length > 0) {
120+
const file = queue.pop()!;
121+
if (visited.has(file)) {
122+
continue;
123+
}
124+
visited.add(file);
125+
126+
expect(harness.hasFile(`dist/browser/${file}`))
127+
.withContext(`Expected dist/browser/${file} to exist`)
128+
.toBeTrue();
129+
130+
const content = harness.readFile(`dist/browser/${file}`);
131+
for (const match of content.matchAll(/(?:from|import)\s*\(?["']\.\/([^"']+\.js)["']\)?/g)) {
132+
queue.push(match[1]);
133+
}
134+
}
135+
});
60136
});
61137
});

‎packages/angular/build/src/tools/esbuild/angular/compiler-plugin.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,7 @@ function bundleWebWorker(
727727
metafile: true,
728728
format: 'esm',
729729
entryNames: 'worker-[hash]',
730+
chunkNames: 'worker-chunk-[hash]',
730731
entryPoints: [workerFile],
731732
sourcemap: pluginOptions.sourcemap,
732733
// Zone.js is not used in Web workers so no need to disable

0 commit comments

Comments
 (0)