Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix server component transforms #49135

Merged
merged 8 commits into from
May 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
35 changes: 31 additions & 4 deletions packages/next-swc/crates/core/src/react_server_components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ impl<C: Comments> VisitMut for ReactServerComponents<C> {
noop_visit_mut_type!();

fn visit_mut_module(&mut self, module: &mut Module) {
let (is_client_entry, imports) = self.collect_top_level_directives_and_imports(module);
let (is_client_entry, is_action_file, imports) =
self.collect_top_level_directives_and_imports(module);
let is_cjs = contains_cjs(module);

if self.is_server {
Expand All @@ -72,7 +73,9 @@ impl<C: Comments> VisitMut for ReactServerComponents<C> {
return;
}
} else {
self.assert_client_graph(&imports, module);
if !is_action_file {
self.assert_client_graph(&imports, module);
}
if is_client_entry {
self.prepend_comment_node(module, is_cjs);
}
Expand All @@ -87,10 +90,24 @@ impl<C: Comments> ReactServerComponents<C> {
fn collect_top_level_directives_and_imports(
&mut self,
module: &mut Module,
) -> (bool, Vec<ModuleImports>) {
) -> (bool, bool, Vec<ModuleImports>) {
let mut imports: Vec<ModuleImports> = vec![];
let mut finished_directives = false;
let mut is_client_entry = false;
let mut is_action_file = false;

fn panic_both_directives(span: Span) {
// It's not possible to have both directives in the same file.
HANDLER.with(|handler| {
handler
.struct_span_err(
span,
"It's not possible to have both `use client` and `use server` directives \
in the same file.",
)
.emit()
})
}

let _ = &module.body.retain(|item| {
match item {
Expand All @@ -107,6 +124,10 @@ impl<C: Comments> ReactServerComponents<C> {
if &**value == "use client" {
if !finished_directives {
is_client_entry = true;

if is_action_file {
panic_both_directives(expr_stmt.span)
}
} else {
HANDLER.with(|handler| {
handler
Expand All @@ -120,6 +141,12 @@ impl<C: Comments> ReactServerComponents<C> {

// Remove the directive.
return false;
} else if &**value == "use server" && !finished_directives {
is_action_file = true;

if is_client_entry {
panic_both_directives(expr_stmt.span)
}
}
}
// Match `ParenthesisExpression` which is some formatting tools
Expand Down Expand Up @@ -230,7 +257,7 @@ impl<C: Comments> ReactServerComponents<C> {
true
});

(is_client_entry, imports)
(is_client_entry, is_action_file, imports)
}

// Convert the client module to the module reference code and add a special
Expand Down
12 changes: 10 additions & 2 deletions packages/next-swc/crates/core/tests/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,21 @@ fn react_server_actions_errors(input: PathBuf) {
let output = input.parent().unwrap().join("output.js");
test_fixture(
syntax(),
&|_tr| {
&|tr| {
chain!(
resolver(Mark::new(), Mark::new(), false),
server_components(
FileName::Real(PathBuf::from("/app/item.js")),
next_swc::react_server_components::Config::WithOptions(
next_swc::react_server_components::Options { is_server: false },
),
tr.comments.as_ref().clone(),
None,
),
server_actions(
&FileName::Real("/app/item.js".into()),
server_actions::Config { is_server: true },
_tr.comments.as_ref().clone(),
tr.comments.as_ref().clone(),
)
)
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
'use server'
'use client'

export async function foo() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* __next_internal_client_entry_do_not_use__ foo auto */ /* __next_internal_action_entry_do_not_use__ foo */ export async function foo() {}
import ensureServerEntryExports from "private-next-rsc-action-proxy";
ensureServerEntryExports([
foo
]);
foo.$$typeof = Symbol.for("react.server.reference");
foo.$$id = "ab21efdafbe611287bc25c0462b1e0510d13e48b";
foo.$$bound = [];
foo.$$with_bound = false;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

x It's not possible to have both `use client` and `use server` directives in the same file.
,-[input.js:1:1]
1 | 'use server'
2 | 'use client'
: ^^^^^^^^^^^^
`----
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use client'
'use strict'

// This is a comment.

'use server'

export async function foo() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* __next_internal_client_entry_do_not_use__ foo auto */ /* __next_internal_action_entry_do_not_use__ foo */ 'use strict';
export async function foo() {}
import ensureServerEntryExports from "private-next-rsc-action-proxy";
ensureServerEntryExports([
foo
]);
foo.$$typeof = Symbol.for("react.server.reference");
foo.$$id = "ab21efdafbe611287bc25c0462b1e0510d13e48b";
foo.$$bound = [];
foo.$$with_bound = false;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

x It's not possible to have both `use client` and `use server` directives in the same file.
,-[input.js:5:1]
5 |
6 | 'use server'
: ^^^^^^^^^^^^
`----
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use server'

// It should be allowed to import server APIs here.
import 'server-only'
import { cookies } from 'next/headers'
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
'use server';
// It should be allowed to import server APIs here.
import 'server-only';
import { cookies } from 'next/headers';