@@ -33,6 +33,7 @@ use turbopack_core::{
3333 } ,
3434 output:: { OutputAsset , OutputAssets } ,
3535} ;
36+ use turbopack_css:: chunk:: { CssChunk , source_map:: CssChunkSourceMapAsset } ;
3637use turbopack_ecmascript:: {
3738 async_chunk:: module:: AsyncLoaderModule ,
3839 chunk:: EcmascriptChunk ,
@@ -257,6 +258,21 @@ impl BrowserChunkingContextBuilder {
257258 self
258259 }
259260
261+ pub fn css_filename ( mut self , css_filename : RcStr ) -> Self {
262+ self . chunking_context . css_filename = Some ( css_filename) ;
263+ self
264+ }
265+
266+ pub fn css_chunk_filename ( mut self , css_chunk_filename : RcStr ) -> Self {
267+ self . chunking_context . css_chunk_filename = Some ( css_chunk_filename) ;
268+ self
269+ }
270+
271+ pub fn asset_module_filename ( mut self , asset_module_filename : RcStr ) -> Self {
272+ self . chunking_context . asset_module_filename = Some ( asset_module_filename) ;
273+ self
274+ }
275+
260276 pub fn chunk_loading_global ( mut self , chunk_loading_global : RcStr ) -> Self {
261277 self . chunking_context . chunk_loading_global = Some ( chunk_loading_global) ;
262278 self
@@ -360,6 +376,12 @@ pub struct BrowserChunkingContext {
360376 filename : Option < RcStr > ,
361377 /// Non evaluate chunk filename template
362378 chunk_filename : Option < RcStr > ,
379+ /// Initial css chunk filename template
380+ css_filename : Option < RcStr > ,
381+ /// Non initial css chunk filename template
382+ css_chunk_filename : Option < RcStr > ,
383+ /// Asset module filename template
384+ asset_module_filename : Option < RcStr > ,
363385 /// Expose entry module exports to global scope with the specified name.
364386 /// When set, all named exports from the entry module will be available on
365387 /// `window`/`globalThis` under the specified name.
@@ -418,6 +440,9 @@ impl BrowserChunkingContext {
418440 chunk_loading_global : Default :: default ( ) ,
419441 filename : Default :: default ( ) ,
420442 chunk_filename : Default :: default ( ) ,
443+ css_filename : Default :: default ( ) ,
444+ css_chunk_filename : Default :: default ( ) ,
445+ asset_module_filename : Default :: default ( ) ,
421446 entry_root_export : None ,
422447 } ,
423448 }
@@ -630,10 +655,21 @@ impl ChunkingContext for BrowserChunkingContext {
630655 query. get ( "name" ) . unwrap_or ( output_name. as_str ( ) )
631656 } ;
632657
658+ let this = self . await ?;
633659 let filename_template = if evaluate {
634- self . await ? . filename . clone ( )
660+ this . filename . clone ( )
635661 } else {
636- self . await ?. chunk_filename . clone ( )
662+ let resolved_asset = asset. to_resolved ( ) . await ?;
663+ if ResolvedVc :: try_downcast_type :: < CssChunk > ( resolved_asset) . is_some ( )
664+ || ResolvedVc :: try_downcast_type :: < CssChunkSourceMapAsset > ( resolved_asset)
665+ . is_some ( )
666+ {
667+ // TODO: distinguash initial or non-initial css chunk, the non-initial css
668+ // chunk should use css_chunk_filename for template
669+ this. css_filename . clone ( )
670+ } else {
671+ this. chunk_filename . clone ( )
672+ }
637673 } ;
638674
639675 match filename_template {
@@ -730,16 +766,39 @@ impl ChunkingContext for BrowserChunkingContext {
730766 ) -> Result < Vc < FileSystemPath > > {
731767 let source_path = original_asset_ident. path ( ) . await ?;
732768 let basename = source_path. file_name ( ) ;
733- let asset_path = match source_path. extension_ref ( ) {
734- Some ( ext) => format ! (
735- "{basename}.{content_hash}.{ext}" ,
736- basename = & basename[ ..basename. len( ) - ext. len( ) - 1 ] ,
737- content_hash = & content_hash[ ..8 ]
738- ) ,
739- None => format ! (
740- "{basename}.{content_hash}" ,
741- content_hash = & content_hash[ ..8 ]
742- ) ,
769+
770+ let asset_path = match & self . asset_module_filename {
771+ Some ( filename_template) => {
772+ let mut filename = filename_template. to_string ( ) ;
773+
774+ if match_name_placeholder ( & filename) {
775+ filename = replace_name_placeholder ( & filename, basename) ;
776+ }
777+
778+ if match_content_hash_placeholder ( & filename) {
779+ filename = replace_content_hash_placeholder ( & filename, & content_hash) ;
780+ } ;
781+
782+ if let Some ( ext) = source_path. extension_ref ( ) {
783+ if let Some ( ( stem, _) ) = filename. rsplit_once ( "." ) {
784+ filename = stem. to_string ( ) ;
785+ }
786+ filename = format ! ( "{filename}.{ext}" ) ;
787+ }
788+
789+ filename
790+ }
791+ None => match source_path. extension_ref ( ) {
792+ Some ( ext) => format ! (
793+ "{basename}.{content_hash}.{ext}" ,
794+ basename = & basename[ ..basename. len( ) - ext. len( ) - 1 ] ,
795+ content_hash = & content_hash[ ..8 ]
796+ ) ,
797+ None => format ! (
798+ "{basename}.{content_hash}" ,
799+ content_hash = & content_hash[ ..8 ]
800+ ) ,
801+ } ,
743802 } ;
744803
745804 let asset_root_path = tag
0 commit comments