@@ -59,7 +59,6 @@ struct Replacement {
5959#[ derive( Clone , Copy , PartialEq , Eq ) ]
6060enum ScopeMode {
6161 FunctionRoot ,
62- Function ,
6362 Block ,
6463}
6564
@@ -216,12 +215,41 @@ impl<'a> Rewriter<'a> {
216215 Statement :: ImportDeclaration ( decl) => self . add_replacement ( decl. source . span , format ! ( "{:?}" , self . module_specifier( decl. source. value. as_str( ) ) ) , 95 ) ,
217216 Statement :: ExportNamedDeclaration ( decl) => {
218217 if let Some ( source) = & decl. source { self . add_replacement ( source. span , format ! ( "{:?}" , self . module_specifier( source. value. as_str( ) ) ) , 95 ) ; }
218+ if let Some ( inner) = & decl. declaration { self . walk_declaration ( inner) ; }
219219 }
220220 Statement :: ExportAllDeclaration ( decl) => self . add_replacement ( decl. source . span , format ! ( "{:?}" , self . module_specifier( decl. source. value. as_str( ) ) ) , 95 ) ,
221+ Statement :: ExportDefaultDeclaration ( decl) => self . walk_export_default ( decl) ,
221222 _ => { }
222223 }
223224 }
224-
225+ fn walk_declaration ( & mut self , decl : & Declaration < ' a > ) {
226+ match decl {
227+ Declaration :: VariableDeclaration ( decl) => self . walk_variable_declaration ( decl) ,
228+ Declaration :: FunctionDeclaration ( func) => self . walk_function ( func) ,
229+ Declaration :: ClassDeclaration ( class) => {
230+ if let Some ( id) = & class. id { self . declare ( id. name . as_str ( ) ) ; }
231+ for elem in & class. body . body {
232+ match elem {
233+ ClassElement :: PropertyDefinition ( prop) => if let Some ( value) = & prop. value { self . walk_expression ( value) ; } ,
234+ ClassElement :: AccessorProperty ( prop) => if let Some ( value) = & prop. value { self . walk_expression ( value) ; } ,
235+ _ => { }
236+ }
237+ }
238+ }
239+ _ => { }
240+ }
241+ }
242+ fn walk_export_default ( & mut self , decl : & ExportDefaultDeclaration < ' a > ) {
243+ match & decl. declaration {
244+ ExportDefaultDeclarationKind :: FunctionDeclaration ( func) => self . walk_function ( func) ,
245+ ExportDefaultDeclarationKind :: ClassDeclaration ( class) => {
246+ if let Some ( id) = & class. id { self . declare ( id. name . as_str ( ) ) ; }
247+ }
248+ other => {
249+ if let Some ( expr) = other. as_expression ( ) { self . walk_expression ( expr) ; }
250+ }
251+ }
252+ }
225253 fn walk_function ( & mut self , func : & Function < ' a > ) {
226254 if let Some ( id) = & func. id { self . declare ( id. name . as_str ( ) ) ; }
227255 let mut scope = HashSet :: new ( ) ;
@@ -614,34 +642,34 @@ impl<'a> Rewriter<'a> {
614642 Statement :: FunctionDeclaration ( func) => { if let Some ( id) = & func. id { names. insert ( id. name . to_string ( ) ) ; } }
615643 Statement :: ClassDeclaration ( class) => { if let Some ( id) = & class. id { names. insert ( id. name . to_string ( ) ) ; } }
616644 Statement :: VariableDeclaration ( decl) => {
617- if mode == ScopeMode :: FunctionRoot {
618- if decl. kind = = VariableDeclarationKind :: Var { for d in & decl. declarations { self . collect_binding_pattern ( & d. id , names) ; } }
619- } else if decl. kind ! = VariableDeclarationKind :: Var {
645+ if mode == ScopeMode :: Block {
646+ if decl. kind ! = VariableDeclarationKind :: Var { for d in & decl. declarations { self . collect_binding_pattern ( & d. id , names) ; } }
647+ } else if decl. kind = = VariableDeclarationKind :: Var {
620648 for d in & decl. declarations { self . collect_binding_pattern ( & d. id , names) ; }
621649 }
622650 }
623- Statement :: BlockStatement ( block) if mode == ScopeMode :: FunctionRoot => for stmt in & block. body { self . collect_statement_bindings ( stmt, ScopeMode :: Function , names) ; } ,
624- Statement :: IfStatement ( stmt) if mode == ScopeMode :: FunctionRoot => { self . collect_statement_bindings ( & stmt. consequent , ScopeMode :: Function , names) ; if let Some ( alt) = & stmt. alternate { self . collect_statement_bindings ( alt, ScopeMode :: Function , names) ; } }
625- Statement :: ForStatement ( stmt) if mode == ScopeMode :: FunctionRoot => {
651+ Statement :: BlockStatement ( block) if mode != ScopeMode :: Block => for stmt in & block. body { self . collect_statement_bindings ( stmt, mode , names) ; } ,
652+ Statement :: IfStatement ( stmt) if mode != ScopeMode :: Block => { self . collect_statement_bindings ( & stmt. consequent , mode , names) ; if let Some ( alt) = & stmt. alternate { self . collect_statement_bindings ( alt, mode , names) ; } }
653+ Statement :: ForStatement ( stmt) if mode != ScopeMode :: Block => {
626654 if let Some ( ForStatementInit :: VariableDeclaration ( decl) ) = & stmt. init { if decl. kind == VariableDeclarationKind :: Var { for d in & decl. declarations { self . collect_binding_pattern ( & d. id , names) ; } } }
627- self . collect_statement_bindings ( & stmt. body , ScopeMode :: Function , names) ;
628- }
629- Statement :: ForInStatement ( stmt) if mode == ScopeMode :: FunctionRoot => { if let ForStatementLeft :: VariableDeclaration ( decl) = & stmt. left { if decl. kind == VariableDeclarationKind :: Var { for d in & decl. declarations { self . collect_binding_pattern ( & d. id , names) ; } } } self . collect_statement_bindings ( & stmt. body , ScopeMode :: Function , names) ; }
630- Statement :: ForOfStatement ( stmt) if mode == ScopeMode :: FunctionRoot => { if let ForStatementLeft :: VariableDeclaration ( decl) = & stmt. left { if decl. kind == VariableDeclarationKind :: Var { for d in & decl. declarations { self . collect_binding_pattern ( & d. id , names) ; } } } self . collect_statement_bindings ( & stmt. body , ScopeMode :: Function , names) ; }
631- Statement :: WhileStatement ( stmt) if mode == ScopeMode :: FunctionRoot => self . collect_statement_bindings ( & stmt. body , ScopeMode :: Function , names) ,
632- Statement :: DoWhileStatement ( stmt) if mode == ScopeMode :: FunctionRoot => self . collect_statement_bindings ( & stmt. body , ScopeMode :: Function , names) ,
633- Statement :: LabeledStatement ( stmt) if mode == ScopeMode :: FunctionRoot => self . collect_statement_bindings ( & stmt. body , ScopeMode :: Function , names) ,
634- Statement :: SwitchStatement ( stmt) if mode == ScopeMode :: FunctionRoot => for case in & stmt. cases { for child in & case. consequent { self . collect_statement_bindings ( child, ScopeMode :: Function , names) ; } } ,
635- Statement :: TryStatement ( stmt) if mode == ScopeMode :: FunctionRoot => {
636- self . collect_block_bindings ( & stmt. block , names) ;
637- if let Some ( handler) = & stmt. handler { self . collect_block_bindings ( & handler. body , names) ; }
638- if let Some ( finalizer) = & stmt. finalizer { self . collect_block_bindings ( finalizer, names) ; }
655+ self . collect_statement_bindings ( & stmt. body , mode , names) ;
656+ }
657+ Statement :: ForInStatement ( stmt) if mode != ScopeMode :: Block => { if let ForStatementLeft :: VariableDeclaration ( decl) = & stmt. left { if decl. kind == VariableDeclarationKind :: Var { for d in & decl. declarations { self . collect_binding_pattern ( & d. id , names) ; } } } self . collect_statement_bindings ( & stmt. body , mode , names) ; }
658+ Statement :: ForOfStatement ( stmt) if mode != ScopeMode :: Block => { if let ForStatementLeft :: VariableDeclaration ( decl) = & stmt. left { if decl. kind == VariableDeclarationKind :: Var { for d in & decl. declarations { self . collect_binding_pattern ( & d. id , names) ; } } } self . collect_statement_bindings ( & stmt. body , mode , names) ; }
659+ Statement :: WhileStatement ( stmt) if mode != ScopeMode :: Block => self . collect_statement_bindings ( & stmt. body , mode , names) ,
660+ Statement :: DoWhileStatement ( stmt) if mode != ScopeMode :: Block => self . collect_statement_bindings ( & stmt. body , mode , names) ,
661+ Statement :: LabeledStatement ( stmt) if mode != ScopeMode :: Block => self . collect_statement_bindings ( & stmt. body , mode , names) ,
662+ Statement :: SwitchStatement ( stmt) if mode != ScopeMode :: Block => for case in & stmt. cases { for child in & case. consequent { self . collect_statement_bindings ( child, mode , names) ; } } ,
663+ Statement :: TryStatement ( stmt) if mode != ScopeMode :: Block => {
664+ self . collect_block_bindings ( & stmt. block , names, mode ) ;
665+ if let Some ( handler) = & stmt. handler { self . collect_block_bindings ( & handler. body , names, mode ) ; }
666+ if let Some ( finalizer) = & stmt. finalizer { self . collect_block_bindings ( finalizer, names, mode ) ; }
639667 }
640668 _ => { }
641669 }
642670 }
643- fn collect_block_bindings ( & self , block : & BlockStatement < ' a > , names : & mut HashSet < String > ) {
644- for stmt in & block. body { self . collect_statement_bindings ( stmt, ScopeMode :: Function , names) ; }
671+ fn collect_block_bindings ( & self , block : & BlockStatement < ' a > , names : & mut HashSet < String > , mode : ScopeMode ) {
672+ for stmt in & block. body { self . collect_statement_bindings ( stmt, mode , names) ; }
645673 }
646674
647675 fn collect_binding_pattern ( & self , pattern : & BindingPattern < ' a > , names : & mut HashSet < String > ) {
@@ -801,6 +829,9 @@ fn join_url(base: &str, raw: &str) -> String {
801829 }
802830 let prefix = match base. rfind ( '/' ) { Some ( i) => & base[ ..=i] , None => base } ;
803831 let mut parts: Vec < & str > = prefix. split ( '/' ) . collect ( ) ;
832+ if parts. last ( ) == Some ( & "" ) {
833+ parts. pop ( ) ;
834+ }
804835 for part in raw. split ( '/' ) {
805836 match part {
806837 "." => { }
@@ -832,3 +863,58 @@ fn hex(v: u8) -> char {
832863 _ => ( b'A' + ( v - 10 ) ) as char ,
833864 }
834865}
866+
867+ #[ cfg( test) ]
868+ mod tests {
869+ use super :: * ;
870+
871+ fn rewrite_ok ( source : & str , kind : & str , target_url : & str ) -> String {
872+ let out = rewrite_script ( source, kind, target_url, "/zp/" ) ;
873+ assert ! ( out. ok, "rewrite failed: {}" , out. error) ;
874+ out. code
875+ }
876+
877+ #[ test]
878+ fn rewrites_virtualized_globals_and_shadowing ( ) {
879+ let code = rewrite_ok (
880+ "function f(x) { if (x) { var location = { href: 'local' }; } return location.href; }\n window.location.hash += '-tail';\n document.defaultView.location.href;" ,
881+ "classic" ,
882+ "https://example.com/app.js" ,
883+ ) ;
884+ assert ! ( code. contains( "return location.href;" ) ) ;
885+ assert ! ( code. contains( "__zp_assign(__zp_get(__zp_get(globalThis,\" window\" ),\" location\" ),\" hash\" " ) ) ;
886+ assert ! ( code. contains( "__zp_get(__zp_get(globalThis,\" document\" ),\" defaultView\" )" ) ) ;
887+ assert ! ( !code. contains( "return __zp_get(globalThis,\" location\" )" ) ) ;
888+ }
889+
890+ #[ test]
891+ fn rewrites_module_urls_and_dynamic_imports ( ) {
892+ let code = rewrite_ok (
893+ "import './dep.js'; export async function load(name) { await import('./chunks/' + name + '.js'); return new URL('/worker-fixture.js', import.meta.url).href; }" ,
894+ "module" ,
895+ "https://example.com/assets/main.js" ,
896+ ) ;
897+ assert ! ( code. contains( "import \" /zp/api/script?kind=module&u=https%3A%2F%2Fexample.com%2Fassets%2Fdep.js\" ;" ) ) ;
898+ assert ! ( code. contains( "__zp_module_url('./chunks/' + name + '.js',\" https://example.com/assets/main.js\" )" ) ) ;
899+ assert ! ( code. contains( "\" https://example.com/assets/main.js\" " ) ) ;
900+ }
901+
902+ #[ test]
903+ fn rewrites_calls_and_constructors ( ) {
904+ let code = rewrite_ok (
905+ "window.location = '/next'; const ws = new WebSocket('/ws', ['chat']); Object.getOwnPropertyDescriptor(window, 'location');" ,
906+ "classic" ,
907+ "https://example.com/app.js" ,
908+ ) ;
909+ assert ! ( code. contains( "__zp_set(__zp_get(globalThis,\" window\" ),\" location\" ,'/next')" ) ) ;
910+ assert ! ( code. contains( "__zp_construct(__zp_get(globalThis,\" WebSocket\" ),['/ws',['chat']])" ) ) ;
911+ assert ! ( code. contains( "__zp_call(Object,\" getOwnPropertyDescriptor\" ,[__zp_get(globalThis,\" window\" ),'location'])" ) ) ;
912+ }
913+
914+ #[ test]
915+ fn parse_failures_return_error ( ) {
916+ let out = rewrite_script ( "if (" , "classic" , "https://example.com/app.js" , "/zp/" ) ;
917+ assert ! ( !out. ok) ;
918+ assert_eq ! ( out. error, "PARSE_FAILED" ) ;
919+ }
920+ }
0 commit comments