Skip to content

Commit fcc7b60

Browse files
committed
refactor(@angular/build): migrate to optimizeDeps.rolldownOptions in Vite config
Vite now uses Rolldown as the underlying engine to optimize dependencies, rendering `optimizeDeps.esbuildOptions` deprecated. This commit refactors the Vite dev-server config in `@angular/build` to use `optimizeDeps.rolldownOptions` instead. It also updates the custom dependency optimization plugin from the esbuild-specific format to a native Rolldown plugin using the `load` hook, and aligns browser target lowering by pushing `es2016` when Zone.js is present (since Rolldown lacks esbuild-style feature-support flags).
1 parent 7ef9ed2 commit fcc7b60

4 files changed

Lines changed: 67 additions & 72 deletions

File tree

packages/angular/build/src/builders/dev-server/vite/index.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import type * as Vite from 'vite' with {
1515
};
1616
import type { ComponentStyleRecord } from '../../../tools/vite/middlewares';
1717
import { ServerSsrMode } from '../../../tools/vite/plugins';
18-
import { EsbuildLoaderOption, updateExternalMetadata } from '../../../tools/vite/utils';
18+
import { updateExternalMetadata } from '../../../tools/vite/utils';
1919
import { normalizeSourceMaps } from '../../../utils';
2020
import { useComponentStyleHmr, useComponentTemplateHmr } from '../../../utils/environment-options';
2121
import { Result, ResultKind } from '../../application/results';
@@ -384,12 +384,17 @@ export async function* serveWithVite(
384384
const projectRoot = join(context.workspaceRoot, root as string);
385385
const browsers = getSupportedBrowsers(projectRoot, context.logger);
386386

387-
const target = transformSupportedBrowsersToTargets(browsers);
388387
// Needed for browser-esbuild as polyfills can be a string.
389388
const polyfills = Array.isArray((browserOptions.polyfills ??= []))
390389
? browserOptions.polyfills
391390
: [browserOptions.polyfills];
392391

392+
const target = transformSupportedBrowsersToTargets(browsers);
393+
if (!isZonelessApp(polyfills)) {
394+
// Rolldown doesn't have an option to support Zone.js/async-await, so we need to support es2016.
395+
target.push('es2016');
396+
}
397+
393398
let ssrMode: ServerSsrMode = ServerSsrMode.NoSsr;
394399
if (
395400
browserOptions.outputMode &&
@@ -408,6 +413,16 @@ export async function* serveWithVite(
408413
});
409414
}
410415

416+
// Copy the loader and modify the file extension to be asset
417+
const loader = browserOptions.loader;
418+
if (loader) {
419+
for (const [key, value] of Object.entries(loader)) {
420+
if (value === 'file') {
421+
loader[key] = 'asset';
422+
}
423+
}
424+
}
425+
411426
// Setup server and start listening
412427
const serverConfiguration = await setupServer(
413428
serverOptions,
@@ -418,12 +433,12 @@ export async function* serveWithVite(
418433
ssrMode,
419434
prebundleTransformer,
420435
target,
421-
isZonelessApp(polyfills),
422436
componentStyles,
423437
templateUpdates,
424-
browserOptions.loader as EsbuildLoaderOption | undefined,
438+
loader,
425439
{
426440
...browserOptions.define,
441+
'ngServerMode': 'false',
427442
'ngJitMode': browserOptions.aot ? 'false' : 'true',
428443
'ngHmrMode': browserOptions.templateUpdates ? 'true' : 'false',
429444
},

packages/angular/build/src/builders/dev-server/vite/server.ts

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
createAngularSsrTransformPlugin,
2121
createRemoveIdPrefixPlugin,
2222
} from '../../../tools/vite/plugins';
23-
import { EsbuildLoaderOption, getDepOptimizationConfig } from '../../../tools/vite/utils';
23+
import { RolldownLoaderOption, getDepOptimizationConfig } from '../../../tools/vite/utils';
2424
import { loadProxyConfiguration } from '../../../utils';
2525
import { type ApplicationBuilderInternalOptions, JavaScriptTransformer } from '../internal';
2626
import type { NormalizedDevServerOptions } from '../options';
@@ -82,13 +82,16 @@ async function createServerConfig(
8282
},
8383
};
8484

85-
if (serverOptions.ssl) {
86-
if (serverOptions.sslCert && serverOptions.sslKey) {
87-
server.https = {
88-
cert: await readFile(serverOptions.sslCert),
89-
key: await readFile(serverOptions.sslKey),
90-
};
91-
}
85+
if (serverOptions.ssl && serverOptions.sslCert && serverOptions.sslKey) {
86+
const [cert, key] = await Promise.all([
87+
readFile(serverOptions.sslCert),
88+
readFile(serverOptions.sslKey),
89+
]);
90+
91+
server.https = {
92+
cert,
93+
key,
94+
};
9295
}
9396

9497
return server;
@@ -98,9 +101,7 @@ function createSsrConfig(
98101
externalMetadata: DevServerExternalResultMetadata,
99102
serverOptions: NormalizedDevServerOptions,
100103
prebundleTransformer: JavaScriptTransformer,
101-
zoneless: boolean,
102-
target: string[],
103-
prebundleLoaderExtensions: EsbuildLoaderOption | undefined,
104+
prebundleLoaderExtensions: RolldownLoaderOption | undefined,
104105
thirdPartySourcemaps: boolean,
105106
define: ApplicationBuilderInternalOptions['define'],
106107
): Vite.SSROptions {
@@ -116,13 +117,13 @@ function createSsrConfig(
116117
exclude: externalMetadata.explicitServer,
117118
// Include all implict dependencies from the external packages internal option
118119
include: externalMetadata.implicitServer,
119-
ssr: true,
120120
prebundleTransformer,
121-
zoneless,
122-
target,
123121
loader: prebundleLoaderExtensions,
124122
thirdPartySourcemaps,
125-
define,
123+
define: {
124+
...define,
125+
'ngServerMode': 'true',
126+
},
126127
}),
127128
};
128129
}
@@ -136,10 +137,9 @@ export async function setupServer(
136137
ssrMode: ServerSsrMode,
137138
prebundleTransformer: JavaScriptTransformer,
138139
target: string[],
139-
zoneless: boolean,
140140
componentStyles: Map<string, ComponentStyleRecord>,
141141
templateUpdates: Map<string, string>,
142-
prebundleLoaderExtensions: EsbuildLoaderOption | undefined,
142+
prebundleLoaderExtensions: RolldownLoaderOption | undefined,
143143
define: ApplicationBuilderInternalOptions['define'],
144144
extensionMiddleware?: Vite.Connect.NextHandleFunction[],
145145
indexHtmlTransformer?: (content: string) => Promise<string>,
@@ -182,7 +182,7 @@ export async function setupServer(
182182
assetsInclude:
183183
prebundleLoaderExtensions &&
184184
Object.entries(prebundleLoaderExtensions)
185-
.filter(([, value]) => value === 'file')
185+
.filter(([, value]) => value === 'asset')
186186
// Create a file extension glob for each key
187187
.map(([key]) => '*' + key),
188188
// Vite will normalize the `base` option by adding a leading slash.
@@ -201,15 +201,16 @@ export async function setupServer(
201201
preTransformRequests,
202202
cacheDir,
203203
),
204+
build: {
205+
target,
206+
},
204207
ssr:
205208
ssrMode === ServerSsrMode.NoSsr
206209
? undefined
207210
: createSsrConfig(
208211
externalMetadata,
209212
serverOptions,
210213
prebundleTransformer,
211-
zoneless,
212-
target,
213214
prebundleLoaderExtensions,
214215
thirdPartySourcemaps,
215216
define,
@@ -244,10 +245,7 @@ export async function setupServer(
244245
exclude: externalMetadata.explicitBrowser,
245246
// Include all implict dependencies from the external packages internal option
246247
include: externalMetadata.implicitBrowser,
247-
ssr: false,
248248
prebundleTransformer,
249-
target,
250-
zoneless,
251249
loader: prebundleLoaderExtensions,
252250
thirdPartySourcemaps,
253251
define,

packages/angular/build/src/tools/esbuild/utils.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import { BuilderContext } from '@angular-devkit/architect';
1010
import { BuildOptions, Metafile, OutputFile, formatMessages } from 'esbuild';
1111
import { Listr } from 'listr2';
12-
import { createHash } from 'node:crypto';
1312
import { basename, join } from 'node:path';
1413
import { pathToFileURL } from 'node:url';
1514
import { brotliCompress } from 'node:zlib';

packages/angular/build/src/tools/vite/utils.ts

Lines changed: 27 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import type { OnLoadArgs, PluginBuild } from 'esbuild';
109
import { lookup as lookupMimeType } from 'mrmime';
1110
import { builtinModules, isBuiltin } from 'node:module';
1211
import { extname } from 'node:path';
@@ -15,7 +14,6 @@ import type { DepOptimizationConfig } from 'vite' with {
1514
};
1615
import type { ExternalResultMetadata } from '../esbuild/bundler-execution-result';
1716
import { JavaScriptTransformer } from '../esbuild/javascript-transformer';
18-
import { getFeatureSupport } from '../esbuild/utils';
1917

2018
export type AngularMemoryOutputFiles = Map<
2119
string,
@@ -44,75 +42,60 @@ export function lookupMimeTypeFromRequest(url: string): string | undefined {
4442
return extension && lookupMimeType(extension);
4543
}
4644

47-
type ViteEsBuildPlugin = NonNullable<
48-
NonNullable<DepOptimizationConfig['esbuildOptions']>['plugins']
49-
>[0];
50-
51-
export type EsbuildLoaderOption = Exclude<
52-
DepOptimizationConfig['esbuildOptions'],
45+
export type RolldownLoaderOption = Exclude<
46+
DepOptimizationConfig['rolldownOptions'],
5347
undefined
54-
>['loader'];
48+
>['moduleTypes'];
5549

5650
export function getDepOptimizationConfig({
5751
disabled,
5852
exclude,
5953
include,
60-
target,
61-
zoneless,
6254
prebundleTransformer,
63-
ssr,
6455
loader,
6556
thirdPartySourcemaps,
6657
define = {},
6758
}: {
6859
disabled: boolean;
6960
exclude: string[];
7061
include: string[];
71-
target: string[];
7262
prebundleTransformer: JavaScriptTransformer;
73-
ssr: boolean;
74-
zoneless: boolean;
75-
loader?: EsbuildLoaderOption;
63+
loader?: RolldownLoaderOption;
7664
thirdPartySourcemaps: boolean;
7765
define: Record<string, string> | undefined;
7866
}): DepOptimizationConfig {
79-
const plugins: ViteEsBuildPlugin[] = [
80-
{
81-
name: `angular-vite-optimize-deps${ssr ? '-ssr' : ''}${
82-
thirdPartySourcemaps ? '-vendor-sourcemap' : ''
83-
}`,
84-
setup(build: PluginBuild) {
85-
build.onLoad({ filter: /\.[cm]?js$/ }, async (args: OnLoadArgs) => {
86-
return {
87-
contents: await prebundleTransformer.transformFile(args.path),
88-
loader: 'js',
89-
};
90-
});
91-
},
92-
},
93-
];
94-
95-
return {
67+
const config: DepOptimizationConfig = {
9668
// Exclude any explicitly defined dependencies (currently build defined externals)
9769
exclude,
9870
// NB: to disable the deps optimizer, set optimizeDeps.noDiscovery to true and optimizeDeps.include as undefined.
9971
// Include all implict dependencies from the external packages internal option
10072
include: disabled ? undefined : include,
10173
noDiscovery: disabled,
102-
// Add an esbuild plugin to run the Angular linker on dependencies
103-
esbuildOptions: {
104-
// Set esbuild supported targets.
105-
target,
106-
supported: getFeatureSupport(zoneless),
107-
plugins,
108-
loader,
109-
define: {
110-
...define,
111-
'ngServerMode': `${ssr}`,
74+
rolldownOptions: {
75+
transform: {
76+
define,
77+
},
78+
moduleTypes: loader,
79+
resolve: {
80+
extensions: ['.mjs', '.js', '.cjs'],
11281
},
113-
resolveExtensions: ['.mjs', '.js', '.cjs'],
82+
plugins: [
83+
{
84+
name: `angular-vite-optimize-deps${thirdPartySourcemaps ? '-vendor-sourcemap' : ''}`,
85+
load: {
86+
filter: { id: /\.[cm]?js$/ },
87+
async handler(id: string) {
88+
const code = await prebundleTransformer.transformFile(id);
89+
90+
return { code: Buffer.from(code).toString('utf-8') };
91+
},
92+
},
93+
},
94+
],
11495
},
11596
};
97+
98+
return config;
11699
}
117100

118101
export interface DevServerExternalResultMetadata {

0 commit comments

Comments
 (0)