1919
2020import type { Message , Metafile } from 'esbuild' ;
2121import assert from 'node:assert' ;
22- import { type OutputAsset , type OutputChunk , rolldown } from 'rolldown' ;
22+ import { rollup } from 'rollup' ;
23+ import { useRolldownChunks } from '../../utils/environment-options' ;
2324import {
2425 BuildOutputFile ,
2526 BuildOutputFileType ,
@@ -30,13 +31,45 @@ import { createOutputFile } from '../../tools/esbuild/utils';
3031import { assertIsError } from '../../utils/error' ;
3132
3233/**
33- * Converts the output of a rolldown build into an esbuild-compatible metafile.
34- * @param rolldownOutput The output of a rolldown build.
34+ * Represents a minimal subset of a Rollup/Rolldown output asset.
35+ * This is manually defined to avoid hard dependencies on both bundlers' types
36+ * and to ensure compatibility since Rolldown and Rollup types have slight differences
37+ * but share these core properties.
38+ */
39+ interface OutputAsset {
40+ type : 'asset' ;
41+ fileName : string ;
42+ source : string | Uint8Array ;
43+ }
44+
45+ /**
46+ * Represents a minimal subset of a Rollup/Rolldown output chunk.
47+ * This is manually defined to avoid hard dependencies on both bundlers' types
48+ * and to ensure compatibility since Rolldown and Rollup types have slight differences
49+ * but share these core properties.
50+ */
51+ interface OutputChunk {
52+ type : 'chunk' ;
53+ fileName : string ;
54+ code : string ;
55+ modules : Record < string , { renderedLength : number } > ;
56+ imports : string [ ] ;
57+ dynamicImports ?: string [ ] ;
58+ exports : string [ ] ;
59+ isEntry : boolean ;
60+ facadeModuleId : string | null | undefined ;
61+ map ?: { toString ( ) : string } | null ;
62+ sourcemapFileName ?: string | null ;
63+ }
64+
65+ /**
66+ * Converts the output of a bundle build into an esbuild-compatible metafile.
67+ * @param bundleOutput The output of a bundle build.
3568 * @param originalMetafile The original esbuild metafile from the build.
3669 * @returns An esbuild-compatible metafile.
3770 */
38- function rolldownToEsbuildMetafile (
39- rolldownOutput : ( OutputChunk | OutputAsset ) [ ] ,
71+ function bundleOutputToEsbuildMetafile (
72+ bundleOutput : ( OutputChunk | OutputAsset ) [ ] ,
4073 originalMetafile : Metafile ,
4174) : Metafile {
4275 const newMetafile : Metafile = {
@@ -52,7 +85,7 @@ function rolldownToEsbuildMetafile(
5285 ) ;
5386 }
5487
55- for ( const chunk of rolldownOutput ) {
88+ for ( const chunk of bundleOutput ) {
5689 if ( chunk . type === 'asset' ) {
5790 newMetafile . outputs [ chunk . fileName ] = {
5891 bytes :
@@ -214,49 +247,65 @@ export async function optimizeChunks(
214247 const usedChunks = new Set < string > ( ) ;
215248
216249 let bundle ;
217- let optimizedOutput ;
250+ let optimizedOutput : ( OutputChunk | OutputAsset ) [ ] ;
218251 try {
219- bundle = await rolldown ( {
220- input : mainFile ,
221- plugins : [
222- {
223- name : 'angular-bundle' ,
224- resolveId ( source ) {
225- // Remove leading `./` if present
226- const file = source [ 0 ] === '.' && source [ 1 ] === '/' ? source . slice ( 2 ) : source ;
227-
228- if ( chunks [ file ] ) {
229- return file ;
230- }
231-
232- // All other identifiers are considered external to maintain behavior
233- return { id : source , external : true } ;
234- } ,
235- load ( id ) {
236- assert (
237- chunks [ id ] ,
238- `Angular chunk content should always be present in chunk optimizer [${ id } ].` ,
239- ) ;
240-
241- usedChunks . add ( id ) ;
242-
243- const result = {
244- code : chunks [ id ] . text ,
245- map : maps [ id ] ?. text ,
246- } ;
247-
248- return result ;
249- } ,
252+ const plugins = [
253+ {
254+ name : 'angular-bundle' ,
255+ resolveId ( source : string ) {
256+ // Remove leading `./` if present
257+ const file = source [ 0 ] === '.' && source [ 1 ] === '/' ? source . slice ( 2 ) : source ;
258+
259+ if ( chunks [ file ] ) {
260+ return file ;
261+ }
262+
263+ // All other identifiers are considered external to maintain behavior
264+ return { id : source , external : true } ;
265+ } ,
266+ load ( id : string ) {
267+ assert (
268+ chunks [ id ] ,
269+ `Angular chunk content should always be present in chunk optimizer [${ id } ].` ,
270+ ) ;
271+
272+ usedChunks . add ( id ) ;
273+
274+ const result = {
275+ code : chunks [ id ] . text ,
276+ map : maps [ id ] ?. text ,
277+ } ;
278+
279+ return result ;
250280 } ,
251- ] ,
252- } ) ;
253-
254- const result = await bundle . generate ( {
255- minify : { mangle : false , compress : false } ,
256- sourcemap,
257- chunkFileNames : ( chunkInfo ) => `${ chunkInfo . name . replace ( / - [ a - z A - Z 0 - 9 ] { 8 } $ / , '' ) } -[hash].js` ,
258- } ) ;
259- optimizedOutput = result . output ;
281+ } ,
282+ ] ;
283+
284+ if ( useRolldownChunks ) {
285+ const { rolldown } = await import ( 'rolldown' ) ;
286+ bundle = await rolldown ( {
287+ input : mainFile ,
288+ plugins,
289+ } ) ;
290+
291+ const result = await bundle . generate ( {
292+ minify : { mangle : false , compress : false } ,
293+ sourcemap,
294+ chunkFileNames : ( chunkInfo ) => `${ chunkInfo . name . replace ( / - [ a - z A - Z 0 - 9 ] { 8 } $ / , '' ) } -[hash].js` ,
295+ } ) ;
296+ optimizedOutput = result . output ;
297+ } else {
298+ bundle = await rollup ( {
299+ input : mainFile ,
300+ plugins : plugins as any ,
301+ } ) ;
302+
303+ const result = await bundle . generate ( {
304+ sourcemap,
305+ chunkFileNames : ( chunkInfo ) => `${ chunkInfo . name . replace ( / - [ a - z A - Z 0 - 9 ] { 8 } $ / , '' ) } -[hash].js` ,
306+ } ) ;
307+ optimizedOutput = result . output ;
308+ }
260309 } catch ( e ) {
261310 assertIsError ( e ) ;
262311
@@ -269,7 +318,7 @@ export async function optimizeChunks(
269318 }
270319
271320 // Update metafile
272- const newMetafile = rolldownToEsbuildMetafile ( optimizedOutput , original . metafile ) ;
321+ const newMetafile = bundleOutputToEsbuildMetafile ( optimizedOutput , original . metafile ) ;
273322 // Add back the outputs that were not part of the optimization
274323 for ( const [ path , output ] of Object . entries ( original . metafile . outputs ) ) {
275324 if ( usedChunks . has ( path ) ) {
0 commit comments