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

add ServerDirective transform which reports unsupported #4477

Merged
merged 1 commit into from
Apr 6, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions crates/turbopack-ecmascript/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use turbo_tasks::{
primitives::{StringVc, U64Vc},
Value, ValueToString,
};
use turbo_tasks_fs::{FileContent, FileSystemPath};
use turbo_tasks_fs::{FileContent, FileSystemPath, FileSystemPathVc};
use turbo_tasks_hash::hash_xxh3_hash64;
use turbopack_core::{
asset::{Asset, AssetContent, AssetVc},
Expand Down Expand Up @@ -142,7 +142,8 @@ pub async fn parse(
transforms: EcmascriptInputTransformsVc,
) -> Result<ParseResultVc> {
let content = source.content();
let fs_path = &*source.ident().path().await?;
let fs_path_vc = source.ident().path();
let fs_path = &*fs_path_vc.await?;
let ident = &*source.ident().to_string().await?;
let file_path_hash = *hash_ident(source.ident().to_string()).await? as u128;
let ty = ty.into_value();
Expand All @@ -154,6 +155,7 @@ pub async fn parse(
let transforms = &*transforms.await?;
match parse_content(
string.into_owned(),
fs_path_vc,
fs_path,
ident,
file_path_hash,
Expand Down Expand Up @@ -182,6 +184,7 @@ pub async fn parse(

async fn parse_content(
string: String,
fs_path_vc: FileSystemPathVc,
fs_path: &FileSystemPath,
ident: &str,
file_path_hash: u128,
Expand Down Expand Up @@ -297,6 +300,7 @@ async fn parse_content(
file_path_str: &fs_path.path,
file_name_str: fs_path.file_name(),
file_name_hash: file_path_hash,
file_path: fs_path_vc,
};
for transform in transforms.iter() {
transform.apply(&mut parsed_program, &context).await?;
Expand Down
68 changes: 64 additions & 4 deletions crates/turbopack-ecmascript/src/transform/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod server_to_client_proxy;
mod util;

use std::{fmt::Debug, path::Path, sync::Arc};

Expand All @@ -15,17 +16,25 @@ use swc_core::{
},
visit::{FoldWith, VisitMutWith},
},
quote,
};
use turbo_tasks::primitives::{OptionStringVc, StringVc};
use turbo_tasks_fs::json::parse_json_with_source_context;
use turbopack_core::environment::EnvironmentVc;
use turbo_tasks_fs::{json::parse_json_with_source_context, FileSystemPathVc};
use turbopack_core::{
environment::EnvironmentVc,
issue::{Issue, IssueSeverity, IssueSeverityVc, IssueVc},
};

use self::server_to_client_proxy::{create_proxy_module, is_client_module};
use self::{
server_to_client_proxy::create_proxy_module,
util::{is_client_module, is_server_module},
};

#[turbo_tasks::value(serialization = "auto_for_input")]
#[derive(Debug, Clone, PartialOrd, Ord, Hash)]
pub enum EcmascriptInputTransform {
ClientDirective(StringVc),
ServerDirective(StringVc),
CommonJs,
Custom(CustomTransformVc),
Emotion,
Expand Down Expand Up @@ -104,6 +113,7 @@ pub struct TransformContext<'a> {
pub file_path_str: &'a str,
pub file_name_str: &'a str,
pub file_name_hash: u128,
pub file_path: FileSystemPathVc,
}

impl EcmascriptInputTransform {
Expand All @@ -115,6 +125,7 @@ impl EcmascriptInputTransform {
unresolved_mark,
file_name_str,
file_name_hash,
file_path,
..
} = ctx;
match self {
Expand Down Expand Up @@ -250,12 +261,28 @@ impl EcmascriptInputTransform {
));
}
EcmascriptInputTransform::ClientDirective(transition_name) => {
let transition_name = &*transition_name.await?;
if is_client_module(program) {
let transition_name = &*transition_name.await?;
*program = create_proxy_module(transition_name, &format!("./{file_name_str}"));
program.visit_mut_with(&mut resolver(unresolved_mark, top_level_mark, false));
}
}
EcmascriptInputTransform::ServerDirective(_transition_name) => {
if is_server_module(program) {
let stmt = quote!(
"throw new Error('Server actions (\"use server\") are not yet supported in \
Turbopack');" as Stmt
);
match program {
Program::Module(m) => m.body = vec![ModuleItem::Stmt(stmt)],
Program::Script(s) => s.body = vec![stmt],
}
UnsupportedServerActionIssue { context: file_path }
.cell()
.as_issue()
.emit();
}
}
EcmascriptInputTransform::Custom(transform) => {
if let Some(output) = transform.await?.transform(program, ctx) {
*program = output;
Expand Down Expand Up @@ -291,3 +318,36 @@ fn unwrap_module_program(program: &mut Program) -> Program {
}),
}
}

#[turbo_tasks::value(shared)]
pub struct UnsupportedServerActionIssue {
pub context: FileSystemPathVc,
}

#[turbo_tasks::value_impl]
impl Issue for UnsupportedServerActionIssue {
#[turbo_tasks::function]
fn severity(&self) -> IssueSeverityVc {
IssueSeverity::Error.into()
}

#[turbo_tasks::function]
fn category(&self) -> StringVc {
StringVc::cell("unsupported".to_string())
}

#[turbo_tasks::function]
fn title(&self) -> StringVc {
StringVc::cell("Server actions (\"use server\") are not yet supported in Turbopack".into())
}

#[turbo_tasks::function]
fn context(&self) -> FileSystemPathVc {
self.context
}

#[turbo_tasks::function]
async fn description(&self) -> Result<StringVc> {
Ok(StringVc::cell("".to_string()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,6 @@ use swc_core::{

use crate::references::TURBOPACK_HELPER;

macro_rules! has_client_directive {
($stmts:expr) => {
$stmts
.map(|item| {
if let Lit::Str(str) = item?.as_expr()?.expr.as_lit()? {
Some(str)
} else {
None
}
})
.take_while(Option::is_some)
.map(Option::unwrap)
.any(|s| &*s.value == "use client")
};
}

pub fn is_client_module(program: &Program) -> bool {
match program {
Program::Module(m) => has_client_directive!(m.body.iter().map(|item| item.as_stmt())),
Program::Script(s) => has_client_directive!(s.body.iter().map(Some)),
}
}

pub fn create_proxy_module(transition_name: &str, target_import: &str) -> Program {
let ident = private_ident!("createProxy");
Program::Module(Module {
Expand Down
35 changes: 35 additions & 0 deletions crates/turbopack-ecmascript/src/transform/util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use swc_core::ecma::ast::{Lit, Program};

macro_rules! has_directive {
($stmts:expr, $name:literal) => {
$stmts
.map(|item| {
if let Lit::Str(str) = item?.as_expr()?.expr.as_lit()? {
Some(str)
} else {
None
}
})
.take_while(Option::is_some)
.map(Option::unwrap)
.any(|s| &*s.value == $name)
};
}

pub fn is_client_module(program: &Program) -> bool {
match program {
Program::Module(m) => {
has_directive!(m.body.iter().map(|item| item.as_stmt()), "use client")
}
Program::Script(s) => has_directive!(s.body.iter().map(Some), "use client"),
}
}

pub fn is_server_module(program: &Program) -> bool {
match program {
Program::Module(m) => {
has_directive!(m.body.iter().map(|item| item.as_stmt()), "use server")
}
Program::Script(s) => has_directive!(s.body.iter().map(Some), "use server"),
}
}