Skip to content

Commit ff23bb6

Browse files
committed
Server actions transform data-urls
1 parent c20b83f commit ff23bb6

File tree

9 files changed

+159
-49
lines changed

9 files changed

+159
-49
lines changed

Diff for: Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: crates/next-core/src/next_shared/transforms/server_actions.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use anyhow::Result;
22
use async_trait::async_trait;
3-
use next_custom_transforms::transforms::server_actions::{server_actions, Config};
3+
use next_custom_transforms::transforms::server_actions::{
4+
server_actions, Config, ServerActionsMode,
5+
};
46
use swc_core::{common::FileName, ecma::ast::Program};
57
use turbo_rcstr::RcStr;
68
use turbo_tasks::{ResolvedVc, Vc};
@@ -66,6 +68,7 @@ impl CustomTransformer for NextServerActions {
6668
},
6769
ctx.comments.clone(),
6870
Default::default(),
71+
ServerActionsMode::Turbopack,
6972
);
7073
program.mutate(actions);
7174
Ok(())

Diff for: crates/next-custom-transforms/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ swc_core = { workspace = true, features = [
3838
"cached",
3939
"common_concurrent",
4040
"ecma_ast",
41+
"ecma_codegen",
4142
"ecma_loader_lru",
4243
"ecma_loader_node",
4344
"ecma_minifier",
@@ -59,6 +60,7 @@ swc_emotion = { workspace = true }
5960
swc_relay = { workspace = true }
6061
turbopack-ecmascript-plugins = { workspace = true, optional = true }
6162
turbo-rcstr = { workspace = true }
63+
urlencoding = { workspace = true }
6264

6365
react_remove_properties = "0.33.0"
6466
remove_console = "0.34.0"

Diff for: crates/next-custom-transforms/src/chain_transforms.rs

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use crate::{
2727
fonts::next_font_loaders,
2828
lint_codemod_comments::lint_codemod_comments,
2929
react_server_components,
30+
server_actions::ServerActionsMode,
3031
},
3132
};
3233

@@ -322,6 +323,7 @@ where
322323
config.clone(),
323324
comments.clone(),
324325
use_cache_telemetry_tracker,
326+
ServerActionsMode::Webpack,
325327
)),
326328
None => Either::Right(noop_pass()),
327329
},

Diff for: crates/next-custom-transforms/src/transforms/server_actions.rs

+120-42
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::{
44
convert::{TryFrom, TryInto},
55
mem::{replace, take},
66
rc::Rc,
7+
sync::Arc,
78
};
89

910
use hex::encode as hex_encode;
@@ -12,23 +13,32 @@ use rustc_hash::{FxHashMap, FxHashSet};
1213
use serde::Deserialize;
1314
use sha1::{Digest, Sha1};
1415
use swc_core::{
15-
atoms::Atom,
16+
atoms::{atom, Atom},
1617
common::{
1718
comments::{Comment, CommentKind, Comments},
1819
errors::HANDLER,
1920
source_map::PURE_SP,
2021
util::take::Take,
21-
BytePos, FileName, Mark, Span, SyntaxContext, DUMMY_SP,
22+
BytePos, FileName, Mark, SourceMap, Span, SyntaxContext, DUMMY_SP,
2223
},
2324
ecma::{
2425
ast::*,
26+
codegen::{text_writer::JsWriter, Emitter},
2527
utils::{private_ident, quote_ident, ExprFactory},
2628
visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith},
2729
},
2830
quote,
2931
};
3032
use turbo_rcstr::RcStr;
3133

34+
use crate::FxIndexMap;
35+
36+
#[derive(Clone, Copy, Debug, Deserialize)]
37+
pub enum ServerActionsMode {
38+
Webpack,
39+
Turbopack,
40+
}
41+
3242
#[derive(Clone, Debug, Deserialize)]
3343
#[serde(deny_unknown_fields, rename_all = "camelCase")]
3444
pub struct Config {
@@ -127,9 +137,11 @@ pub fn server_actions<C: Comments>(
127137
config: Config,
128138
comments: C,
129139
use_cache_telemetry_tracker: Rc<RefCell<FxHashMap<String, usize>>>,
140+
mode: ServerActionsMode,
130141
) -> impl Pass {
131142
visit_mut_pass(ServerActions {
132143
config,
144+
mode,
133145
comments,
134146
file_name: file_name.to_string(),
135147
start_pos: BytePos(0),
@@ -184,6 +196,7 @@ struct ServerActions<C: Comments> {
184196
config: Config,
185197
file_name: String,
186198
comments: C,
199+
mode: ServerActionsMode,
187200

188201
start_pos: BytePos,
189202
file_directive: Option<Directive>,
@@ -1809,46 +1822,49 @@ impl<C: Comments> VisitMut for ServerActions<C> {
18091822
let call_server_ident = private_ident!("callServer");
18101823
let find_source_map_url_ident = private_ident!("findSourceMapURL");
18111824

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 {
1825+
let client_layer_import = ((self.has_action || self.has_cache)
1826+
&& !self.config.is_react_server_layer)
1827+
.then(|| {
1828+
// import {
1829+
// createServerReference,
1830+
// callServer,
1831+
// findSourceMapURL
1832+
// } from 'private-next-rsc-action-client-wrapper'
1833+
// createServerReference("action_id")
1834+
ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl {
1835+
span: DUMMY_SP,
1836+
specifiers: vec![
1837+
ImportSpecifier::Named(ImportNamedSpecifier {
1838+
span: DUMMY_SP,
1839+
local: create_ref_ident.clone(),
1840+
imported: None,
1841+
is_type_only: false,
1842+
}),
1843+
ImportSpecifier::Named(ImportNamedSpecifier {
1844+
span: DUMMY_SP,
1845+
local: call_server_ident.clone(),
1846+
imported: None,
1847+
is_type_only: false,
1848+
}),
1849+
ImportSpecifier::Named(ImportNamedSpecifier {
1850+
span: DUMMY_SP,
1851+
local: find_source_map_url_ident.clone(),
1852+
imported: None,
1853+
is_type_only: false,
1854+
}),
1855+
],
1856+
src: Box::new(Str {
18291857
span: DUMMY_SP,
1830-
local: call_server_ident.clone(),
1831-
imported: None,
1832-
is_type_only: false,
1858+
value: "private-next-rsc-action-client-wrapper".into(),
1859+
raw: None,
18331860
}),
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-
}
1861+
type_only: false,
1862+
with: None,
1863+
phase: Default::default(),
1864+
}))
1865+
});
1866+
1867+
let mut client_layer_exports = FxIndexMap::default();
18521868

18531869
// If it's a "use server" or a "use cache" file, all exports need to be annotated.
18541870
if should_track_exports {
@@ -1885,7 +1901,7 @@ impl<C: Comments> VisitMut for ServerActions<C> {
18851901
})),
18861902
},
18871903
));
1888-
new.push(export_expr);
1904+
client_layer_exports.insert(atom!("default"), export_expr);
18891905
} else {
18901906
let export_expr =
18911907
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
@@ -1932,7 +1948,7 @@ impl<C: Comments> VisitMut for ServerActions<C> {
19321948
..Default::default()
19331949
})),
19341950
}));
1935-
new.push(export_expr);
1951+
client_layer_exports.insert(export_name.clone(), export_expr);
19361952
}
19371953
} else if !in_cache_file {
19381954
self.annotations.push(Stmt::Expr(ExprStmt {
@@ -2098,6 +2114,50 @@ impl<C: Comments> VisitMut for ServerActions<C> {
20982114
new.rotate_right(2);
20992115
}
21002116

2117+
if (self.has_action || self.has_cache) && !self.config.is_react_server_layer {
2118+
match self.mode {
2119+
ServerActionsMode::Webpack => {
2120+
new.push(client_layer_import.unwrap());
2121+
new.rotate_right(1);
2122+
new.extend(client_layer_exports.into_iter().map(|(_, v)| v));
2123+
}
2124+
ServerActionsMode::Turbopack => {
2125+
for (export, stmt) in client_layer_exports {
2126+
new.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
2127+
NamedExport {
2128+
specifiers: vec![ExportSpecifier::Named(ExportNamedSpecifier {
2129+
span: DUMMY_SP,
2130+
orig: ModuleExportName::Ident(export.into()),
2131+
exported: None,
2132+
is_type_only: false,
2133+
})],
2134+
src: Some(Box::new(
2135+
program_to_data_url(&Program::Module(Module {
2136+
span: DUMMY_SP,
2137+
body: vec![
2138+
ModuleItem::Stmt(Stmt::Expr(ExprStmt {
2139+
expr: Box::new(Expr::Lit(Lit::Str(
2140+
"use turbopack no side effects".into(),
2141+
))),
2142+
span: DUMMY_SP,
2143+
})),
2144+
client_layer_import.clone().unwrap(),
2145+
stmt,
2146+
],
2147+
shebang: None,
2148+
}))
2149+
.into(),
2150+
)),
2151+
span: DUMMY_SP,
2152+
type_only: false,
2153+
with: None,
2154+
},
2155+
)));
2156+
}
2157+
}
2158+
}
2159+
}
2160+
21012161
*stmts = new;
21022162

21032163
self.annotations = old_annotations;
@@ -3102,3 +3162,21 @@ fn emit_error(error_kind: ServerActionsErrorKind) {
31023162

31033163
HANDLER.with(|handler| handler.struct_span_err(span, &msg).emit());
31043164
}
3165+
3166+
fn program_to_data_url(program: &Program) -> String {
3167+
let mut output = vec![];
3168+
let sourcemap = Arc::new(SourceMap::default());
3169+
let mut emitter = Emitter {
3170+
cfg: Default::default(),
3171+
cm: sourcemap.clone(),
3172+
wr: Box::new(JsWriter::new(sourcemap.clone(), " ", &mut output, None)),
3173+
comments: None,
3174+
};
3175+
3176+
// println!("Emitting: {:?}", module);
3177+
emitter.emit_program(program).unwrap();
3178+
drop(emitter);
3179+
3180+
let output = String::from_utf8(output).expect("codegen generated non-utf8 output");
3181+
format!("data:text/javascript,{}", urlencoding::encode(&output))
3182+
}

Diff for: crates/next-custom-transforms/tests/errors.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ use next_custom_transforms::transforms::{
66
fonts::{next_font_loaders, Config as FontLoaderConfig},
77
next_ssg::next_ssg,
88
react_server_components::server_components,
9-
server_actions::{
10-
server_actions, {self},
11-
},
9+
server_actions::{self, server_actions, ServerActionsMode},
1210
strip_page_exports::{next_transform_strip_page_exports, ExportFilter},
1311
};
1412
use rustc_hash::FxHashSet;
@@ -172,6 +170,7 @@ fn react_server_actions_errors(input: PathBuf) {
172170
},
173171
tr.comments.as_ref().clone(),
174172
Default::default(),
173+
ServerActionsMode::Webpack,
175174
),
176175
)
177176
},
@@ -233,6 +232,7 @@ fn use_cache_not_allowed(input: PathBuf) {
233232
},
234233
tr.comments.as_ref().clone(),
235234
Default::default(),
235+
ServerActionsMode::Webpack,
236236
),
237237
)
238238
},

Diff for: crates/next-custom-transforms/tests/fixture.rs

+16-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use next_custom_transforms::transforms::{
1818
page_config::page_config_test,
1919
pure::pure_magic,
2020
react_server_components::server_components,
21-
server_actions::{self, server_actions},
21+
server_actions::{self, server_actions, ServerActionsMode},
2222
shake_exports::{shake_exports, Config as ShakeExportsConfig},
2323
strip_page_exports::{next_transform_strip_page_exports, ExportFilter},
2424
warn_for_edge_runtime::warn_for_edge_runtime,
@@ -542,9 +542,14 @@ fn server_actions_fixture(input: PathBuf) {
542542
let output = input.parent().unwrap().join("output.js");
543543
let is_react_server_layer = input.iter().any(|s| s.to_str() == Some("server-graph"));
544544
let is_development = input.iter().any(|s| s.to_str() == Some("development"));
545+
let mode = if input.iter().any(|s| s.to_str() == Some("turbopack")) {
546+
ServerActionsMode::Turbopack
547+
} else {
548+
ServerActionsMode::Webpack
549+
};
545550
test_fixture(
546551
syntax(),
547-
&|_tr| {
552+
&|tr| {
548553
(
549554
resolver(Mark::new(), Mark::new(), false),
550555
server_actions(
@@ -556,8 +561,9 @@ fn server_actions_fixture(input: PathBuf) {
556561
hash_salt: "".into(),
557562
cache_kinds: FxHashSet::from_iter(["x".into()]),
558563
},
559-
_tr.comments.as_ref().clone(),
564+
tr.comments.as_ref().clone(),
560565
Default::default(),
566+
mode,
561567
),
562568
)
563569
},
@@ -593,6 +599,7 @@ fn next_font_with_directive_fixture(input: PathBuf) {
593599
},
594600
_tr.comments.as_ref().clone(),
595601
Default::default(),
602+
ServerActionsMode::Webpack,
596603
),
597604
)
598605
},
@@ -881,6 +888,11 @@ fn test_source_maps(input: PathBuf) {
881888
let output: PathBuf = input.parent().unwrap().join("output.js");
882889
let is_react_server_layer = input.iter().any(|s| s.to_str() == Some("server-graph"));
883890
let is_development = input.iter().any(|s| s.to_str() == Some("development"));
891+
let mode = if input.iter().any(|s| s.to_str() == Some("turbopack")) {
892+
ServerActionsMode::Turbopack
893+
} else {
894+
ServerActionsMode::Webpack
895+
};
884896

885897
test_fixture(
886898
syntax(),
@@ -898,6 +910,7 @@ fn test_source_maps(input: PathBuf) {
898910
},
899911
_tr.comments.as_ref().clone(),
900912
Default::default(),
913+
mode,
901914
),
902915
)
903916
},

0 commit comments

Comments
 (0)