@@ -4,20 +4,24 @@ use std::{
44} ;
55
66use rspack_core:: {
7- Compilation , CompilationParams , CompilerCompilation , CompilerFinishMake , ModuleType ,
8- NormalModuleFactoryParser , ParserAndGenerator , ParserOptions , Plugin ,
7+ AssetEmittedInfo , ChunkUkey , Compilation , CompilationParams , CompilerAssetEmitted ,
8+ CompilerCompilation , CompilerFinishMake , ModuleType , NormalModuleFactoryParser ,
9+ ParserAndGenerator , ParserOptions , Plugin , get_module_directives, get_module_hashbang,
10+ rspack_sources:: { ConcatSource , RawStringSource , Source , SourceExt } ,
911} ;
1012use rspack_error:: Result ;
1113use rspack_hook:: { plugin, plugin_hook} ;
1214use rspack_plugin_asset:: AssetParserAndGenerator ;
1315use rspack_plugin_javascript:: {
14- BoxJavascriptParserPlugin , parser_and_generator:: JavaScriptParserAndGenerator ,
16+ BoxJavascriptParserPlugin , JavascriptModulesRender , JsPlugin , RenderSource ,
17+ parser_and_generator:: JavaScriptParserAndGenerator ,
1518} ;
1619
1720use crate :: {
18- asset:: RslibAssetParserAndGenerator , import_dependency:: RslibDependencyTemplate ,
21+ asset:: RslibAssetParserAndGenerator , hashbang_parser_plugin:: HashbangParserPlugin ,
22+ import_dependency:: RslibDependencyTemplate ,
1923 import_external:: replace_import_dependencies_for_external_modules,
20- parser_plugin:: RslibParserPlugin ,
24+ parser_plugin:: RslibParserPlugin , react_directives_parser_plugin :: ReactDirectivesParserPlugin ,
2125} ;
2226
2327#[ derive( Debug ) ]
@@ -54,6 +58,8 @@ async fn nmf_parser(
5458) -> Result < ( ) > {
5559 if let Some ( parser) = parser. downcast_mut :: < JavaScriptParserAndGenerator > ( ) {
5660 if module_type. is_js_like ( ) {
61+ parser. add_parser_plugin ( Box :: new ( HashbangParserPlugin ) as BoxJavascriptParserPlugin ) ;
62+ parser. add_parser_plugin ( Box :: new ( ReactDirectivesParserPlugin ) as BoxJavascriptParserPlugin ) ;
5763 parser. add_parser_plugin (
5864 Box :: new ( RslibParserPlugin :: new ( self . options . intercept_api_plugin ) )
5965 as BoxJavascriptParserPlugin ,
@@ -88,6 +94,71 @@ async fn compilation(
8894 RslibDependencyTemplate :: template_type ( ) ,
8995 Arc :: new ( RslibDependencyTemplate :: default ( ) ) ,
9096 ) ;
97+
98+ // Register render hook for hashbang and directives handling during chunk generation
99+ let hooks = JsPlugin :: get_compilation_hooks_mut ( compilation. id ( ) ) ;
100+ let mut hooks = hooks. write ( ) . await ;
101+ hooks. render . tap ( render:: new ( self ) ) ;
102+ drop ( hooks) ;
103+
104+ Ok ( ( ) )
105+ }
106+
107+ #[ plugin_hook( JavascriptModulesRender for RslibPlugin ) ]
108+ async fn render (
109+ & self ,
110+ compilation : & Compilation ,
111+ chunk_ukey : & ChunkUkey ,
112+ render_source : & mut RenderSource ,
113+ ) -> Result < ( ) > {
114+ // NOTE: This function handles hashbang and directives for non new ESM library formats.
115+ // Similar logic exists in rspack_plugin_esm_library/src/render.rs for ESM format,
116+ // as that plugin's render path is used instead when ESM library plugin is enabled.
117+ let entry_modules = compilation. chunk_graph . get_chunk_entry_modules ( chunk_ukey) ;
118+ if entry_modules. is_empty ( ) {
119+ return Ok ( ( ) ) ;
120+ }
121+
122+ let module_graph = compilation. get_module_graph ( ) ;
123+
124+ for entry_module_id in & entry_modules {
125+ let hashbang = get_module_hashbang ( & module_graph, entry_module_id) ;
126+ let directives = get_module_directives ( & module_graph, entry_module_id) ;
127+
128+ if hashbang. is_none ( ) && directives. is_none ( ) {
129+ continue ;
130+ }
131+
132+ let original_source_str = render_source. source . source ( ) . into_string_lossy ( ) ;
133+
134+ let mut new_source = ConcatSource :: default ( ) ;
135+
136+ if let Some ( hashbang) = hashbang {
137+ new_source. add ( RawStringSource :: from ( format ! ( "{}\n " , hashbang) ) ) ;
138+ }
139+
140+ if let Some ( directives) = directives {
141+ let use_strict_prefix = "\" use strict\" ;\n " ;
142+ if let Some ( rest) = original_source_str. strip_prefix ( use_strict_prefix) {
143+ new_source. add ( RawStringSource :: from ( use_strict_prefix) ) ;
144+ for directive in directives {
145+ new_source. add ( RawStringSource :: from ( format ! ( "{}\n " , directive) ) ) ;
146+ }
147+ new_source. add ( RawStringSource :: from ( rest) ) ;
148+ } else {
149+ for directive in directives {
150+ new_source. add ( RawStringSource :: from ( format ! ( "{}\n " , directive) ) ) ;
151+ }
152+ new_source. add ( render_source. source . clone ( ) ) ;
153+ }
154+ } else {
155+ new_source. add ( render_source. source . clone ( ) ) ;
156+ }
157+
158+ render_source. source = new_source. boxed ( ) ;
159+ break ;
160+ }
161+
91162 Ok ( ( ) )
92163}
93164
@@ -98,6 +169,26 @@ async fn finish_make(&self, compilation: &mut Compilation) -> Result<()> {
98169 Ok ( ( ) )
99170}
100171
172+ #[ plugin_hook( CompilerAssetEmitted for RslibPlugin ) ]
173+ async fn asset_emitted (
174+ & self ,
175+ compilation : & Compilation ,
176+ _filename : & str ,
177+ info : & AssetEmittedInfo ,
178+ ) -> Result < ( ) > {
179+ use rspack_fs:: FilePermissions ;
180+
181+ let content = info. source . source ( ) . into_string_lossy ( ) ;
182+ if content. starts_with ( "#!" ) {
183+ let output_fs = & compilation. output_filesystem ;
184+ let permissions = FilePermissions :: from_mode ( 0o755 ) ;
185+ output_fs
186+ . set_permissions ( & info. target_path , permissions)
187+ . await ?;
188+ }
189+ Ok ( ( ) )
190+ }
191+
101192impl Plugin for RslibPlugin {
102193 fn name ( & self ) -> & ' static str {
103194 "rslib"
@@ -111,6 +202,10 @@ impl Plugin for RslibPlugin {
111202 . tap ( nmf_parser:: new ( self ) ) ;
112203
113204 ctx. compiler_hooks . finish_make . tap ( finish_make:: new ( self ) ) ;
205+ ctx
206+ . compiler_hooks
207+ . asset_emitted
208+ . tap ( asset_emitted:: new ( self ) ) ;
114209
115210 Ok ( ( ) )
116211 }
0 commit comments