Repository navigation
perf(@angular/build): avoid duplicate Domino bundling in server bundles #34527
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
alan-agius4
merged 3 commits into
angular:main
from
alan-agius4:perf-server-domino-dedup
Oct 26, 2026
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
58f0583
perf(@angular/build): avoid duplicate Domino bundling in server bundles
alan-agius4 5c31f57
fixup! perf(@angular/build): avoid duplicate Domino bundling in serve…
alan-agius4 d9ad768
fixup! perf(@angular/build): avoid duplicate Domino bundling in serve…
alan-agius4 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
94 changes: 94 additions & 0 deletions
94
...ages/angular/build/src/builders/application/tests/behavior/platform-server-domino_spec.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| /** | ||
| * @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.dev/license | ||
| */ | ||
|
|
||
| import { buildApplication } from '../../index'; | ||
| import { OutputMode, Platform } from '../../schema'; | ||
| import { APPLICATION_BUILDER_INFO, BASE_OPTIONS, describeBuilder } from '../setup'; | ||
|
|
||
| /** | ||
| * `@angular/platform-server/init` (bundled into the server polyfills) and | ||
| * `@angular/platform-server` (bundled into the main server bundle) both import this file. | ||
| */ | ||
| const DOMINO_BUNDLE = 'third_party/domino/bundled-domino.mjs'; | ||
|
|
||
| /** The property on globalThis where the Domino instance is stored. */ | ||
| const DOMINO_GLOBAL_PROPERTY = /(?:ɵ|\\u0275)domino/; | ||
|
|
||
| describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => { | ||
| describe('Behavior: "Domino is bundled once for the server"', () => { | ||
| beforeEach(async () => { | ||
| await harness.modifyFile('src/tsconfig.app.json', (content) => { | ||
| const tsConfig = JSON.parse(content); | ||
| tsConfig.files ??= []; | ||
| tsConfig.files.push('main.server.ts', 'server.ts'); | ||
|
|
||
| return JSON.stringify(tsConfig); | ||
| }); | ||
|
|
||
| await harness.writeFile('src/server.ts', `console.log('Hello!');`); | ||
|
|
||
| // The default template uses i18n which requires `@angular/localize` to prerender. | ||
| await harness.writeFile('src/app/app.component.html', `<p>Hello!</p>`); | ||
| }); | ||
|
|
||
| it('shares the server polyfills copy of Domino with the main server bundle', async () => { | ||
| harness.useTarget('build', { | ||
| ...BASE_OPTIONS, | ||
| server: 'src/main.server.ts', | ||
| ssr: true, | ||
| }); | ||
|
|
||
| const { result } = await harness.executeOnce(); | ||
| expect(result?.success).toBeTrue(); | ||
|
|
||
| harness.expectFile('dist/server/polyfills.server.mjs').content.toContain(DOMINO_BUNDLE); | ||
| harness | ||
| .expectFile('dist/server/polyfills.server.mjs') | ||
| .content.toMatch(DOMINO_GLOBAL_PROPERTY); | ||
|
|
||
| harness.expectFile('dist/server/main.server.mjs').content.not.toContain(DOMINO_BUNDLE); | ||
| harness.expectFile('dist/server/main.server.mjs').content.toMatch(DOMINO_GLOBAL_PROPERTY); | ||
| }); | ||
|
|
||
| it('shares the server polyfills copy of Domino when "outputMode" is "server"', async () => { | ||
| harness.useTarget('build', { | ||
| ...BASE_OPTIONS, | ||
| server: 'src/main.server.ts', | ||
| ssr: { entry: 'src/server.ts' }, | ||
| outputMode: OutputMode.Server, | ||
| }); | ||
|
|
||
| const { result } = await harness.executeOnce(); | ||
| expect(result?.success).toBeTrue(); | ||
|
|
||
| harness.expectFile('dist/server/polyfills.server.mjs').content.toContain(DOMINO_BUNDLE); | ||
| harness.expectFile('dist/server/main.server.mjs').content.not.toContain(DOMINO_BUNDLE); | ||
| harness.expectFile('dist/server/main.server.mjs').content.toMatch(DOMINO_GLOBAL_PROPERTY); | ||
| harness.expectFile('dist/server/server.mjs').content.not.toContain(DOMINO_BUNDLE); | ||
| }); | ||
|
|
||
| it('shares the server polyfills copy of Domino when "ssr.platform" is "neutral"', async () => { | ||
| harness.useTarget('build', { | ||
| ...BASE_OPTIONS, | ||
| server: 'src/main.server.ts', | ||
| ssr: { entry: 'src/server.ts', platform: Platform.Neutral }, | ||
| outputMode: OutputMode.Server, | ||
| }); | ||
|
|
||
| const { result } = await harness.executeOnce(); | ||
| expect(result?.success).toBeTrue(); | ||
|
|
||
| harness.expectFile('dist/server/polyfills.server.mjs').content.toContain(DOMINO_BUNDLE); | ||
| harness | ||
| .expectFile('dist/server/polyfills.server.mjs') | ||
| .content.toMatch(DOMINO_GLOBAL_PROPERTY); | ||
| harness.expectFile('dist/server/main.server.mjs').content.not.toContain(DOMINO_BUNDLE); | ||
| harness.expectFile('dist/server/main.server.mjs').content.toMatch(DOMINO_GLOBAL_PROPERTY); | ||
| }); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 90 additions & 0 deletions
90
packages/angular/build/src/tools/esbuild/platform-server-domino-plugin.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| /** | ||
| * @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.dev/license | ||
| */ | ||
|
|
||
| import type { Plugin } from 'esbuild'; | ||
| import { toPosixPath } from '../../utils/path'; | ||
|
|
||
| /** | ||
| * Filter matching the Domino bundle import inside `@angular/platform-server`. | ||
| */ | ||
| const BUNDLED_DOMINO_FILTER = /[\\/]third_party[\\/]domino[\\/]bundled-domino\.mjs$/; | ||
|
|
||
| /** | ||
| * The property name on `globalThis` where the Domino instance is stored. | ||
| */ | ||
| const DOMINO_GLOBAL_PROPERTY = 'ɵdomino'; | ||
|
|
||
| /** | ||
| * Creates an esbuild plugin that attaches the Domino instance to `globalThis` when | ||
| * `@angular/platform-server/init` is bundled into the server polyfills bundle. | ||
| * | ||
| * @returns An esbuild plugin. | ||
| */ | ||
| export function createPlatformServerDominoExposePlugin(): Plugin { | ||
| return { | ||
| name: 'angular-platform-server-domino-expose', | ||
| setup(build) { | ||
| build.onResolve({ filter: BUNDLED_DOMINO_FILTER }, (args) => | ||
| args.namespace !== 'angular:domino-expose' && args.kind === 'import-statement' | ||
| ? { | ||
| path: 'angular:platform-server-domino', | ||
| namespace: 'angular:domino-expose', | ||
| pluginData: { | ||
| importPath: args.path, | ||
| resolveDir: args.resolveDir, | ||
| }, | ||
| } | ||
| : null, | ||
| ); | ||
|
|
||
| build.onLoad({ filter: /./, namespace: 'angular:domino-expose' }, (args) => { | ||
| const { importPath, resolveDir } = args.pluginData as { | ||
| importPath: string; | ||
| resolveDir: string; | ||
| }; | ||
|
|
||
| return { | ||
| contents: [ | ||
| `import domino from ${JSON.stringify(toPosixPath(importPath))};`, | ||
| `globalThis['${DOMINO_GLOBAL_PROPERTY}'] = domino;`, | ||
| `export default domino;`, | ||
| ].join('\n'), | ||
| loader: 'js', | ||
| resolveDir, | ||
| }; | ||
| }); | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Creates an esbuild plugin that redirects imports of Domino in `@angular/platform-server` | ||
| * to the single copy attached to `globalThis` by the server polyfills bundle. | ||
| * | ||
| * @returns An esbuild plugin. | ||
| */ | ||
| export function createPlatformServerDominoRedirectPlugin(): Plugin { | ||
| return { | ||
| name: 'angular-platform-server-domino-redirect', | ||
| setup(build) { | ||
| build.onResolve({ filter: BUNDLED_DOMINO_FILTER }, (args) => | ||
| args.kind === 'import-statement' | ||
| ? { | ||
| path: 'angular:platform-server-domino', | ||
| namespace: 'angular:platform-server-domino', | ||
| } | ||
| : null, | ||
| ); | ||
|
|
||
| build.onLoad({ filter: /./, namespace: 'angular:platform-server-domino' }, () => ({ | ||
| contents: `export default globalThis['${DOMINO_GLOBAL_PROPERTY}'];`, | ||
| loader: 'js', | ||
| })); | ||
| }, | ||
| }; | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.