@@ -10,40 +10,47 @@ type ViteOutputChunk = Rollup.OutputChunk & { viteMetadata?: ViteChunkMetadata }
1010export function bundleToGraph ( bundle : Rollup . OutputBundle , root : string ) : NormalizedGraph {
1111 const entryPoints : Record < string , EntryFiles > = { } ;
1212 const assets : AssetEntry [ ] = [ ] ;
13- // File names of the CSS emitted for any chunk (entry or lazily-imported). It stays keyed by its
14- // logical name (e.g. `app.css`, `map_controller.css`), matching Rsbuild's chunk-name keying;
15- // Vite reports its `originalFileNames` as the importing JS module, so the source-path branch
16- // below (for imported images/fonts) must not apply to it.
17- const chunkCss = new Set < string > ( ) ;
13+ // Entry CSS stays in the manifest, keyed by its logical name (e.g. `app.css`, matching Rsbuild's
14+ // chunk-name keying). Async (non-entry) chunk CSS does not: it loads at runtime with its lazily
15+ // imported chunk, never via `asset()`, so a manifest entry would only be a byproduct that diverges
16+ // from Rsbuild and collides when two chunks share a name. Both kinds report `originalFileNames` as
17+ // the importing JS, so neither must reach the source-path branch (that is for imported images/fonts).
18+ const entryCss = new Set < string > ( ) ;
19+ const asyncCss = new Set < string > ( ) ;
1820
1921 for ( const file of Object . values ( bundle ) ) {
2022 if ( file . type !== 'chunk' ) continue ;
2123 const chunk = file as ViteOutputChunk ;
2224 const css = chunk . viteMetadata ? [ ...chunk . viteMetadata . importedCss ] : [ ] ;
23- for ( const name of css ) chunkCss . add ( name ) ;
24- if ( ! chunk . isEntry ) continue ;
25- entryPoints [ chunk . name ] = {
26- js : [ chunk . fileName ] ,
27- css,
28- preload : [ ...chunk . imports ] ,
29- dynamic : [ ...chunk . dynamicImports ] ,
30- } ;
31- assets . push ( { logicalName : `${ chunk . name } .js` , fileName : chunk . fileName } ) ;
25+ if ( chunk . isEntry ) {
26+ for ( const name of css ) entryCss . add ( name ) ;
27+ entryPoints [ chunk . name ] = {
28+ js : [ chunk . fileName ] ,
29+ css,
30+ preload : [ ...chunk . imports ] ,
31+ dynamic : [ ...chunk . dynamicImports ] ,
32+ } ;
33+ assets . push ( { logicalName : `${ chunk . name } .js` , fileName : chunk . fileName } ) ;
34+ } else {
35+ for ( const name of css ) asyncCss . add ( name ) ;
36+ }
3237 }
3338
3439 for ( const file of Object . values ( bundle ) ) {
3540 if ( file . type !== 'asset' ) continue ;
36- assets . push ( { logicalName : assetLogicalName ( file , root , chunkCss ) , fileName : file . fileName } ) ;
41+ // Drop async-only chunk CSS (see above); entry CSS (also referenced by an entry) is kept.
42+ if ( asyncCss . has ( file . fileName ) && ! entryCss . has ( file . fileName ) ) continue ;
43+ assets . push ( { logicalName : assetLogicalName ( file , root , entryCss ) , fileName : file . fileName } ) ;
3744 }
3845
3946 return { entryPoints, assets } ;
4047}
4148
42- function assetLogicalName ( file : Rollup . OutputAsset , root : string , chunkCss : Set < string > ) : string {
49+ function assetLogicalName ( file : Rollup . OutputAsset , root : string , entryCss : Set < string > ) : string {
4350 // Imported assets (images, fonts) get their source path relative to the project root, so the
4451 // manifest key matches Rsbuild's `sourceFilename` and same-basename files in different folders
45- // stay distinct. Chunk CSS and assets with no source path fall back to the basename.
46- const original = chunkCss . has ( file . fileName ) ? undefined : file . originalFileNames [ 0 ] ;
52+ // stay distinct. Entry CSS and assets with no source path fall back to the basename.
53+ const original = entryCss . has ( file . fileName ) ? undefined : file . originalFileNames [ 0 ] ;
4754 if ( original ) return slash ( relative ( root , resolve ( root , original ) ) ) ;
4855 return file . names [ 0 ] ?? file . fileName ;
4956}
0 commit comments