Skip to content

Commit e93d9cb

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 508d6a2 commit e93d9cb

2 files changed

Lines changed: 74 additions & 0 deletions

File tree

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

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,78 @@ 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 filesToCheck = new Set(['main.js']);
118+
if (workerMatch) {
119+
filesToCheck.add(workerMatch[1]);
120+
}
121+
122+
for (const file of filesToCheck) {
123+
expect(harness.hasFile(`dist/browser/${file}`))
124+
.withContext(`Expected dist/browser/${file} to exist`)
125+
.toBeTrue();
126+
127+
const content = harness.readFile(`dist/browser/${file}`);
128+
for (const match of content.matchAll(/(?:from|import)\s*\(?["']\.\/([^"']+\.js)["']\)?/g)) {
129+
filesToCheck.add(match[1]);
130+
}
131+
}
132+
});
60133
});
61134
});

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

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

0 commit comments

Comments
 (0)