Skip to content

Commit 9d1c6fd

Browse files
authored
Turbopack: Ensure server actions sourcemaps tests pass (#76157)
## What? Follow-up to #76129. Fixes the sourcemap, ensures the mappings are not carried over when compiling for the client. For the server these mappings are preserved. <!-- Thanks for opening a PR! Your contribution is much appreciated. To make sure your PR is handled as smoothly as possible we request that you follow the checklist sections below. Choose the right checklist for the change(s) that you're making: ## For Contributors ### Improving Documentation - Run `pnpm prettier-fix` to fix formatting issues before opening the PR. - Read the Docs Contribution Guide to ensure your contribution follows the docs guidelines: https://nextjs.org/docs/community/contribution-guide ### Adding or Updating Examples - The "examples guidelines" are followed from our contributing doc https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md - Make sure the linting passes by running `pnpm build && pnpm lint`. See https://github.com/vercel/next.js/blob/canary/contributing/repository/linting.md ### Fixing a bug - Related issues linked using `fixes #number` - Tests added. See: https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs - Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md ### Adding a feature - Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. (A discussion must be opened, see https://github.com/vercel/next.js/discussions/new?category=ideas) - Related issues/discussions are linked using `fixes #number` - e2e tests added (https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) - Documentation added - Telemetry added. In case of a feature if it's used or not. - Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md ## For Maintainers - Minimal description (aim for explaining to someone not on the team to understand the PR) - When linking to a Slack thread, you might want to share details of the conclusion - Link both the Linear (Fixes NEXT-xxx) and the GitHub issues - Add review comments if necessary to explain to the reviewer the logic behind a change ### What? ### Why? ### How? Closes NEXT- Fixes # -->
1 parent c53ac3e commit 9d1c6fd

File tree

16 files changed

+192
-41
lines changed

16 files changed

+192
-41
lines changed

crates/next-core/src/next_client/transforms.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,17 @@ pub async fn get_next_client_transforms_rules(
7373
}
7474
ClientContextType::App { .. } => {
7575
is_app_dir = true;
76-
rules.push(get_server_actions_transform_rule(
77-
ActionsTransform::Client,
78-
encryption_key,
79-
enable_mdx_rs,
80-
use_cache_enabled,
81-
cache_kinds,
82-
));
76+
rules.push(
77+
get_server_actions_transform_rule(
78+
mode,
79+
ActionsTransform::Client,
80+
encryption_key,
81+
enable_mdx_rs,
82+
use_cache_enabled,
83+
cache_kinds,
84+
)
85+
.await?,
86+
);
8387
}
8488
ClientContextType::Fallback | ClientContextType::Other => {}
8589
};

crates/next-core/src/next_server/transforms.rs

+33-21
Original file line numberDiff line numberDiff line change
@@ -111,39 +111,51 @@ pub async fn get_next_server_transforms_rules(
111111
ServerContextType::AppSSR { .. } => {
112112
// Yah, this is SSR, but this is still treated as a Client transform layer.
113113
// need to apply to foreign code too
114-
rules.push(get_server_actions_transform_rule(
115-
ActionsTransform::Client,
116-
encryption_key,
117-
mdx_rs,
118-
use_cache_enabled,
119-
cache_kinds,
120-
));
114+
rules.push(
115+
get_server_actions_transform_rule(
116+
mode,
117+
ActionsTransform::Client,
118+
encryption_key,
119+
mdx_rs,
120+
use_cache_enabled,
121+
cache_kinds,
122+
)
123+
.await?,
124+
);
121125

122126
is_app_dir = true;
123127

124128
false
125129
}
126130
ServerContextType::AppRSC { .. } => {
127-
rules.push(get_server_actions_transform_rule(
128-
ActionsTransform::Server,
129-
encryption_key,
130-
mdx_rs,
131-
use_cache_enabled,
132-
cache_kinds,
133-
));
131+
rules.push(
132+
get_server_actions_transform_rule(
133+
mode,
134+
ActionsTransform::Server,
135+
encryption_key,
136+
mdx_rs,
137+
use_cache_enabled,
138+
cache_kinds,
139+
)
140+
.await?,
141+
);
134142

135143
is_app_dir = true;
136144

137145
true
138146
}
139147
ServerContextType::AppRoute { .. } => {
140-
rules.push(get_server_actions_transform_rule(
141-
ActionsTransform::Server,
142-
encryption_key,
143-
mdx_rs,
144-
use_cache_enabled,
145-
cache_kinds,
146-
));
148+
rules.push(
149+
get_server_actions_transform_rule(
150+
mode,
151+
ActionsTransform::Server,
152+
encryption_key,
153+
mdx_rs,
154+
use_cache_enabled,
155+
cache_kinds,
156+
)
157+
.await?,
158+
);
147159

148160
is_app_dir = true;
149161

crates/next-core/src/next_shared/transforms/server_actions.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ use async_trait::async_trait;
33
use next_custom_transforms::transforms::server_actions::{server_actions, Config};
44
use swc_core::{common::FileName, ecma::ast::Program};
55
use turbo_rcstr::RcStr;
6-
use turbo_tasks::ResolvedVc;
6+
use turbo_tasks::{ResolvedVc, Vc};
77
use turbopack::module_options::{ModuleRule, ModuleRuleEffect};
88
use turbopack_ecmascript::{CustomTransformer, EcmascriptInputTransform, TransformContext};
99

1010
use super::module_rule_match_js_no_url;
11-
use crate::next_config::CacheKinds;
11+
use crate::{mode::NextMode, next_config::CacheKinds};
1212

1313
#[derive(Debug)]
1414
pub enum ActionsTransform {
@@ -17,27 +17,29 @@ pub enum ActionsTransform {
1717
}
1818

1919
/// Returns a rule which applies the Next.js Server Actions transform.
20-
pub fn get_server_actions_transform_rule(
20+
pub async fn get_server_actions_transform_rule(
21+
mode: Vc<NextMode>,
2122
transform: ActionsTransform,
2223
encryption_key: ResolvedVc<RcStr>,
2324
enable_mdx_rs: bool,
2425
use_cache_enabled: bool,
2526
cache_kinds: ResolvedVc<CacheKinds>,
26-
) -> ModuleRule {
27+
) -> Result<ModuleRule> {
2728
let transformer =
2829
EcmascriptInputTransform::Plugin(ResolvedVc::cell(Box::new(NextServerActions {
30+
mode: *mode.await?,
2931
transform,
3032
encryption_key,
3133
use_cache_enabled,
3234
cache_kinds,
3335
}) as _));
34-
ModuleRule::new(
36+
Ok(ModuleRule::new(
3537
module_rule_match_js_no_url(enable_mdx_rs),
3638
vec![ModuleRuleEffect::ExtendEcmascriptTransforms {
3739
prepend: ResolvedVc::cell(vec![]),
3840
append: ResolvedVc::cell(vec![transformer]),
3941
}],
40-
)
42+
))
4143
}
4244

4345
#[derive(Debug)]
@@ -46,6 +48,7 @@ struct NextServerActions {
4648
encryption_key: ResolvedVc<RcStr>,
4749
use_cache_enabled: bool,
4850
cache_kinds: ResolvedVc<CacheKinds>,
51+
mode: NextMode,
4952
}
5053

5154
#[async_trait]
@@ -56,6 +59,7 @@ impl CustomTransformer for NextServerActions {
5659
&FileName::Real(ctx.file_path_str.into()),
5760
Config {
5861
is_react_server_layer: matches!(self.transform, ActionsTransform::Server),
62+
is_development: self.mode.is_development(),
5963
use_cache_enabled: self.use_cache_enabled,
6064
hash_salt: self.encryption_key.await?.to_string(),
6165
cache_kinds: self.cache_kinds.owned().await?,

crates/next-custom-transforms/src/transforms/server_actions.rs

+32-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use swc_core::{
1616
common::{
1717
comments::{Comment, CommentKind, Comments},
1818
errors::HANDLER,
19+
source_map::PURE_SP,
1920
util::take::Take,
2021
BytePos, FileName, Mark, Span, SyntaxContext, DUMMY_SP,
2122
},
@@ -32,6 +33,7 @@ use turbo_rcstr::RcStr;
3233
#[serde(deny_unknown_fields, rename_all = "camelCase")]
3334
pub struct Config {
3435
pub is_react_server_layer: bool,
36+
pub is_development: bool,
3537
pub use_cache_enabled: bool,
3638
pub hash_salt: String,
3739
pub cache_kinds: FxHashSet<RcStr>,
@@ -1859,7 +1861,17 @@ impl<C: Comments> VisitMut for ServerActions<C> {
18591861
ExportDefaultExpr {
18601862
span: DUMMY_SP,
18611863
expr: Box::new(Expr::Call(CallExpr {
1862-
span: ident.span,
1864+
// In development we generate these spans for sourcemapping with
1865+
// better logs/errors
1866+
// For production this is not generated because it would leak
1867+
// server code when available from the browser.
1868+
span: if self.config.is_react_server_layer
1869+
|| self.config.is_development
1870+
{
1871+
ident.span
1872+
} else {
1873+
PURE_SP
1874+
},
18631875
callee: Callee::Expr(Box::new(Expr::Ident(
18641876
create_ref_ident.clone(),
18651877
))),
@@ -1876,8 +1888,8 @@ impl<C: Comments> VisitMut for ServerActions<C> {
18761888
));
18771889
new.push(export_expr);
18781890
} else {
1879-
let call_expr_span = Span::dummy_with_cmt();
1880-
self.comments.add_pure_comment(call_expr_span.lo);
1891+
let dummy_pure_span = Span::dummy_with_cmt();
1892+
self.comments.add_pure_comment(dummy_pure_span.lo);
18811893

18821894
let export_expr =
18831895
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
@@ -1888,10 +1900,25 @@ impl<C: Comments> VisitMut for ServerActions<C> {
18881900
decls: vec![VarDeclarator {
18891901
span: DUMMY_SP,
18901902
name: Pat::Ident(
1891-
IdentName::new(export_name.clone(), ident.span).into(),
1903+
IdentName::new(
1904+
export_name.clone(),
1905+
// In development we generate these spans for
1906+
// sourcemapping with better logs/errors
1907+
// For production this is not generated because it
1908+
// would leak server code when available from the
1909+
// browser.
1910+
if self.config.is_react_server_layer
1911+
|| self.config.is_development
1912+
{
1913+
ident.span
1914+
} else {
1915+
DUMMY_SP
1916+
},
1917+
)
1918+
.into(),
18921919
),
18931920
init: Some(Box::new(Expr::Call(CallExpr {
1894-
span: call_expr_span,
1921+
span: dummy_pure_span,
18951922
callee: Callee::Expr(Box::new(Expr::Ident(
18961923
create_ref_ident.clone(),
18971924
))),

crates/next-custom-transforms/tests/errors.rs

+2
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ fn react_server_actions_errors(input: PathBuf) {
165165
&FileName::Real("/app/item.js".into()),
166166
server_actions::Config {
167167
is_react_server_layer,
168+
is_development: true,
168169
use_cache_enabled: true,
169170
hash_salt: "".into(),
170171
cache_kinds: FxHashSet::default(),
@@ -225,6 +226,7 @@ fn use_cache_not_allowed(input: PathBuf) {
225226
&FileName::Real("/app/item.js".into()),
226227
server_actions::Config {
227228
is_react_server_layer: true,
229+
is_development: true,
228230
use_cache_enabled: false,
229231
hash_salt: "".into(),
230232
cache_kinds: FxHashSet::from_iter(["x".into()]),

crates/next-custom-transforms/tests/fixture.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,7 @@ fn next_font_loaders_fixture(input: PathBuf) {
541541
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"));
544+
let is_development = input.iter().any(|s| s.to_str() == Some("development"));
544545
test_fixture(
545546
syntax(),
546547
&|_tr| {
@@ -550,6 +551,7 @@ fn server_actions_fixture(input: PathBuf) {
550551
&FileName::Real("/app/item.js".into()),
551552
server_actions::Config {
552553
is_react_server_layer,
554+
is_development,
553555
use_cache_enabled: true,
554556
hash_salt: "".into(),
555557
cache_kinds: FxHashSet::from_iter(["x".into()]),
@@ -584,6 +586,7 @@ fn next_font_with_directive_fixture(input: PathBuf) {
584586
&FileName::Real("/app/test.tsx".into()),
585587
server_actions::Config {
586588
is_react_server_layer: true,
589+
is_development: true,
587590
use_cache_enabled: true,
588591
hash_salt: "".into(),
589592
cache_kinds: FxHashSet::default(),
@@ -875,7 +878,10 @@ fn test_edge_assert(input: PathBuf) {
875878

876879
#[fixture("tests/fixture/source-maps/**/input.js")]
877880
fn test_source_maps(input: PathBuf) {
878-
let output = input.parent().unwrap().join("output.js");
881+
let output: PathBuf = input.parent().unwrap().join("output.js");
882+
let is_react_server_layer = input.iter().any(|s| s.to_str() == Some("server-graph"));
883+
let is_development = input.iter().any(|s| s.to_str() == Some("development"));
884+
879885
test_fixture(
880886
syntax(),
881887
&|_tr| {
@@ -884,7 +890,8 @@ fn test_source_maps(input: PathBuf) {
884890
server_actions(
885891
&FileName::Real("/app/item.js".into()),
886892
server_actions::Config {
887-
is_react_server_layer: true,
893+
is_react_server_layer,
894+
is_development,
888895
use_cache_enabled: true,
889896
hash_salt: "".into(),
890897
cache_kinds: FxHashSet::from_iter([]),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use server'
2+
3+
import 'server-only'
4+
5+
import { redirect } from 'next/navigation'
6+
import { headers, cookies } from 'next/headers'
7+
8+
export async function getHeaders() {
9+
console.log('accept header:', (await headers()).get('accept'))
10+
;(await cookies()).set('test-cookie', Date.now())
11+
}
12+
13+
export async function inc(value) {
14+
return value + 1
15+
}
16+
17+
export async function slowInc(value) {
18+
await new Promise((resolve) => setTimeout(resolve, 10000))
19+
return value + 1
20+
}
21+
22+
export async function dec(value) {
23+
return value - 1
24+
}
25+
26+
export default async function (value) {
27+
console.log('this_is_sensitive_info')
28+
return value * 2
29+
}
30+
31+
export async function redirectAction(path) {
32+
redirect(path)
33+
}
34+
35+
const original = async () => {
36+
console.log('action')
37+
}
38+
export { original as renamed }

crates/next-custom-transforms/tests/fixture/source-maps/client-graph/development/server-actions/1/output.js

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

crates/next-custom-transforms/tests/fixture/source-maps/client-graph/development/server-actions/1/output.map

+1
Original file line numberDiff line numberDiff line change
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use server'
2+
3+
import 'server-only'
4+
5+
import { redirect } from 'next/navigation'
6+
import { headers, cookies } from 'next/headers'
7+
8+
export async function getHeaders() {
9+
console.log('accept header:', (await headers()).get('accept'))
10+
;(await cookies()).set('test-cookie', Date.now())
11+
}
12+
13+
export async function inc(value) {
14+
return value + 1
15+
}
16+
17+
export async function slowInc(value) {
18+
await new Promise((resolve) => setTimeout(resolve, 10000))
19+
return value + 1
20+
}
21+
22+
export async function dec(value) {
23+
return value - 1
24+
}
25+
26+
export default async function (value) {
27+
console.log('this_is_sensitive_info')
28+
return value * 2
29+
}
30+
31+
export async function redirectAction(path) {
32+
redirect(path)
33+
}
34+
35+
const original = async () => {
36+
console.log('action')
37+
}
38+
export { original as renamed }

crates/next-custom-transforms/tests/fixture/source-maps/client-graph/production/server-actions/1/output.js

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

crates/next-custom-transforms/tests/fixture/source-maps/client-graph/production/server-actions/1/output.map

+1
Original file line numberDiff line numberDiff line change

0 commit comments

Comments
 (0)