@@ -4,6 +4,7 @@ use std::{
4
4
convert:: { TryFrom , TryInto } ,
5
5
mem:: { replace, take} ,
6
6
rc:: Rc ,
7
+ sync:: Arc ,
7
8
} ;
8
9
9
10
use hex:: encode as hex_encode;
@@ -12,23 +13,32 @@ use rustc_hash::{FxHashMap, FxHashSet};
12
13
use serde:: Deserialize ;
13
14
use sha1:: { Digest , Sha1 } ;
14
15
use swc_core:: {
15
- atoms:: Atom ,
16
+ atoms:: { atom , Atom } ,
16
17
common:: {
17
18
comments:: { Comment , CommentKind , Comments } ,
18
19
errors:: HANDLER ,
19
20
source_map:: PURE_SP ,
20
21
util:: take:: Take ,
21
- BytePos , FileName , Mark , Span , SyntaxContext , DUMMY_SP ,
22
+ BytePos , FileName , Mark , SourceMap , Span , SyntaxContext , DUMMY_SP ,
22
23
} ,
23
24
ecma:: {
24
25
ast:: * ,
26
+ codegen:: { text_writer:: JsWriter , Emitter } ,
25
27
utils:: { private_ident, quote_ident, ExprFactory } ,
26
28
visit:: { noop_visit_mut_type, visit_mut_pass, VisitMut , VisitMutWith } ,
27
29
} ,
28
30
quote,
29
31
} ;
30
32
use turbo_rcstr:: RcStr ;
31
33
34
+ use crate :: FxIndexMap ;
35
+
36
+ #[ derive( Clone , Copy , Debug , Deserialize ) ]
37
+ pub enum ServerActionMode {
38
+ Webpack ,
39
+ Turbopack ,
40
+ }
41
+
32
42
#[ derive( Clone , Debug , Deserialize ) ]
33
43
#[ serde( deny_unknown_fields, rename_all = "camelCase" ) ]
34
44
pub struct Config {
@@ -37,6 +47,7 @@ pub struct Config {
37
47
pub use_cache_enabled : bool ,
38
48
pub hash_salt : String ,
39
49
pub cache_kinds : FxHashSet < RcStr > ,
50
+ pub mode : ServerActionMode ,
40
51
}
41
52
42
53
#[ derive( Clone , Debug ) ]
@@ -1809,46 +1820,49 @@ impl<C: Comments> VisitMut for ServerActions<C> {
1809
1820
let call_server_ident = private_ident ! ( "callServer" ) ;
1810
1821
let find_source_map_url_ident = private_ident ! ( "findSourceMapURL" ) ;
1811
1822
1812
- if ( self . has_action || self . has_cache ) && !self . config . is_react_server_layer {
1813
- // import {
1814
- // createServerReference,
1815
- // callServer,
1816
- // findSourceMapURL
1817
- // } from 'private-next-rsc-action-client-wrapper'
1818
- // createServerReference("action_id")
1819
- new. push ( ModuleItem :: ModuleDecl ( ModuleDecl :: Import ( ImportDecl {
1820
- span : DUMMY_SP ,
1821
- specifiers : vec ! [
1822
- ImportSpecifier :: Named ( ImportNamedSpecifier {
1823
- span: DUMMY_SP ,
1824
- local: create_ref_ident. clone( ) ,
1825
- imported: None ,
1826
- is_type_only: false ,
1827
- } ) ,
1828
- ImportSpecifier :: Named ( ImportNamedSpecifier {
1823
+ let client_layer_import = ( ( self . has_action || self . has_cache )
1824
+ && !self . config . is_react_server_layer )
1825
+ . then ( || {
1826
+ // import {
1827
+ // createServerReference,
1828
+ // callServer,
1829
+ // findSourceMapURL
1830
+ // } from 'private-next-rsc-action-client-wrapper'
1831
+ // createServerReference("action_id")
1832
+ ModuleItem :: ModuleDecl ( ModuleDecl :: Import ( ImportDecl {
1833
+ span : DUMMY_SP ,
1834
+ specifiers : vec ! [
1835
+ ImportSpecifier :: Named ( ImportNamedSpecifier {
1836
+ span: DUMMY_SP ,
1837
+ local: create_ref_ident. clone( ) ,
1838
+ imported: None ,
1839
+ is_type_only: false ,
1840
+ } ) ,
1841
+ ImportSpecifier :: Named ( ImportNamedSpecifier {
1842
+ span: DUMMY_SP ,
1843
+ local: call_server_ident. clone( ) ,
1844
+ imported: None ,
1845
+ is_type_only: false ,
1846
+ } ) ,
1847
+ ImportSpecifier :: Named ( ImportNamedSpecifier {
1848
+ span: DUMMY_SP ,
1849
+ local: find_source_map_url_ident. clone( ) ,
1850
+ imported: None ,
1851
+ is_type_only: false ,
1852
+ } ) ,
1853
+ ] ,
1854
+ src : Box :: new ( Str {
1829
1855
span : DUMMY_SP ,
1830
- local: call_server_ident. clone( ) ,
1831
- imported: None ,
1832
- is_type_only: false ,
1856
+ value : "private-next-rsc-action-client-wrapper" . into ( ) ,
1857
+ raw : None ,
1833
1858
} ) ,
1834
- ImportSpecifier :: Named ( ImportNamedSpecifier {
1835
- span: DUMMY_SP ,
1836
- local: find_source_map_url_ident. clone( ) ,
1837
- imported: None ,
1838
- is_type_only: false ,
1839
- } ) ,
1840
- ] ,
1841
- src : Box :: new ( Str {
1842
- span : DUMMY_SP ,
1843
- value : "private-next-rsc-action-client-wrapper" . into ( ) ,
1844
- raw : None ,
1845
- } ) ,
1846
- type_only : false ,
1847
- with : None ,
1848
- phase : Default :: default ( ) ,
1849
- } ) ) ) ;
1850
- new. rotate_right ( 1 ) ;
1851
- }
1859
+ type_only : false ,
1860
+ with : None ,
1861
+ phase : Default :: default ( ) ,
1862
+ } ) )
1863
+ } ) ;
1864
+
1865
+ let mut client_layer_exports = FxIndexMap :: default ( ) ;
1852
1866
1853
1867
// If it's a "use server" or a "use cache" file, all exports need to be annotated.
1854
1868
if should_track_exports {
@@ -1885,7 +1899,7 @@ impl<C: Comments> VisitMut for ServerActions<C> {
1885
1899
} ) ) ,
1886
1900
} ,
1887
1901
) ) ;
1888
- new . push ( export_expr) ;
1902
+ client_layer_exports . insert ( atom ! ( "default" ) , export_expr) ;
1889
1903
} else {
1890
1904
let export_expr =
1891
1905
ModuleItem :: ModuleDecl ( ModuleDecl :: ExportDecl ( ExportDecl {
@@ -1932,7 +1946,7 @@ impl<C: Comments> VisitMut for ServerActions<C> {
1932
1946
..Default :: default ( )
1933
1947
} ) ) ,
1934
1948
} ) ) ;
1935
- new . push ( export_expr) ;
1949
+ client_layer_exports . insert ( export_name . clone ( ) , export_expr) ;
1936
1950
}
1937
1951
} else if !in_cache_file {
1938
1952
self . annotations . push ( Stmt :: Expr ( ExprStmt {
@@ -2098,6 +2112,50 @@ impl<C: Comments> VisitMut for ServerActions<C> {
2098
2112
new. rotate_right ( 2 ) ;
2099
2113
}
2100
2114
2115
+ if ( self . has_action || self . has_cache ) && !self . config . is_react_server_layer {
2116
+ match self . config . mode {
2117
+ ServerActionMode :: Webpack => {
2118
+ new. push ( client_layer_import. unwrap ( ) ) ;
2119
+ new. rotate_right ( 1 ) ;
2120
+ new. extend ( client_layer_exports. into_iter ( ) . map ( |( _, v) | v) ) ;
2121
+ }
2122
+ ServerActionMode :: Turbopack => {
2123
+ for ( export, stmt) in client_layer_exports {
2124
+ new. push ( ModuleItem :: ModuleDecl ( ModuleDecl :: ExportNamed (
2125
+ NamedExport {
2126
+ specifiers : vec ! [ ExportSpecifier :: Named ( ExportNamedSpecifier {
2127
+ span: DUMMY_SP ,
2128
+ orig: ModuleExportName :: Ident ( export. into( ) ) ,
2129
+ exported: None ,
2130
+ is_type_only: false ,
2131
+ } ) ] ,
2132
+ src : Some ( Box :: new (
2133
+ program_to_data_url ( & Program :: Module ( Module {
2134
+ span : DUMMY_SP ,
2135
+ body : vec ! [
2136
+ ModuleItem :: Stmt ( Stmt :: Expr ( ExprStmt {
2137
+ expr: Box :: new( Expr :: Lit ( Lit :: Str (
2138
+ "use turbopack no side effects" . into( ) ,
2139
+ ) ) ) ,
2140
+ span: DUMMY_SP ,
2141
+ } ) ) ,
2142
+ client_layer_import. clone( ) . unwrap( ) ,
2143
+ stmt,
2144
+ ] ,
2145
+ shebang : None ,
2146
+ } ) )
2147
+ . into ( ) ,
2148
+ ) ) ,
2149
+ span : DUMMY_SP ,
2150
+ type_only : false ,
2151
+ with : None ,
2152
+ } ,
2153
+ ) ) ) ;
2154
+ }
2155
+ }
2156
+ }
2157
+ }
2158
+
2101
2159
* stmts = new;
2102
2160
2103
2161
self . annotations = old_annotations;
@@ -3102,3 +3160,21 @@ fn emit_error(error_kind: ServerActionsErrorKind) {
3102
3160
3103
3161
HANDLER . with ( |handler| handler. struct_span_err ( span, & msg) . emit ( ) ) ;
3104
3162
}
3163
+
3164
+ fn program_to_data_url ( program : & Program ) -> String {
3165
+ let mut output = vec ! [ ] ;
3166
+ let sourcemap = Arc :: new ( SourceMap :: default ( ) ) ;
3167
+ let mut emitter = Emitter {
3168
+ cfg : Default :: default ( ) ,
3169
+ cm : sourcemap. clone ( ) ,
3170
+ wr : Box :: new ( JsWriter :: new ( sourcemap. clone ( ) , " " , & mut output, None ) ) ,
3171
+ comments : None ,
3172
+ } ;
3173
+
3174
+ // println!("Emitting: {:?}", module);
3175
+ emitter. emit_program ( program) . unwrap ( ) ;
3176
+ drop ( emitter) ;
3177
+
3178
+ let output = String :: from_utf8 ( output) . expect ( "codegen generated non-utf8 output" ) ;
3179
+ format ! ( "data:text/javascript,{}" , urlencoding:: encode( & output) )
3180
+ }
0 commit comments