Skip to content

Commit

Permalink
refactor(ecmsacript): remove custom transform cloned return (#4881)
Browse files Browse the repository at this point in the history
### Description

Part 1 for WEB-1024.

PR refactors custom transformer signature to not allow to return cloned
instance. Also moves out few non-core transforms into plugins, so
next.js turbopack can use it.

Next.js changes: vercel/next.js#49560
  • Loading branch information
kwonoj committed May 10, 2023
1 parent e91ad12 commit 95be42a
Show file tree
Hide file tree
Showing 15 changed files with 322 additions and 27 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions crates/turbopack-ecmascript-plugins/Cargo.toml
Expand Up @@ -15,12 +15,16 @@ transform_emotion = []
[dependencies]
anyhow = { workspace = true }
async-trait = { workspace = true }
indexmap = { workspace = true }
serde = { workspace = true }

turbo-tasks = { workspace = true }
turbo-tasks-fs = { workspace = true }
turbopack-ecmascript = { workspace = true }

modularize_imports = { workspace = true }
styled_components = { workspace = true }
styled_jsx = { workspace = true }
swc_core = { workspace = true, features = ["ecma_ast", "ecma_visit", "common"] }
swc_emotion = { workspace = true }
swc_relay = { workspace = true }
Expand Down
@@ -0,0 +1,37 @@
use anyhow::Result;
use async_trait::async_trait;
use swc_core::ecma::{ast::Program, transforms::base::resolver, visit::VisitMutWith};
use turbo_tasks::primitives::StringVc;
use turbopack_ecmascript::{CustomTransformer, TransformContext};

use super::{is_client_module, server_to_client_proxy::create_proxy_module};

#[derive(Debug)]
pub struct ClientDirectiveTransformer {
transition_name: StringVc,
}

impl ClientDirectiveTransformer {
pub fn new(transition_name: &StringVc) -> Self {
Self {
transition_name: transition_name.clone(),
}
}
}

#[async_trait]
impl CustomTransformer for ClientDirectiveTransformer {
async fn transform(&self, program: &mut Program, ctx: &TransformContext<'_>) -> Result<()> {
if is_client_module(program) {
let transition_name = &*self.transition_name.await?;
*program = create_proxy_module(transition_name, &format!("./{}", ctx.file_name_str));
program.visit_mut_with(&mut resolver(
ctx.unresolved_mark,
ctx.top_level_mark,
false,
));
}

Ok(())
}
}
@@ -0,0 +1,39 @@
use swc_core::ecma::ast::{Lit, Program};

pub mod client;
pub mod server;
mod server_to_client_proxy;

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)
};
}

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"),
}
}

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"),
}
}
@@ -0,0 +1,42 @@
use anyhow::Result;
use async_trait::async_trait;
use swc_core::{
ecma::ast::{ModuleItem, Program},
quote,
};
use turbopack_ecmascript::{CustomTransformer, TransformContext, UnsupportedServerActionIssue};

use super::is_server_module;

#[derive(Debug)]
pub struct ServerDirectiveTransformer;

impl ServerDirectiveTransformer {
pub fn new() -> Self {
Self
}
}

#[async_trait]
impl CustomTransformer for ServerDirectiveTransformer {
async fn transform(&self, program: &mut Program, ctx: &TransformContext<'_>) -> Result<()> {
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: ctx.file_path,
}
.cell()
.as_issue()
.emit();
}

Ok(())
}
}
@@ -0,0 +1,51 @@
use swc_core::{
common::DUMMY_SP,
ecma::{
ast::{
Expr, ExprStmt, Ident, ImportDecl, ImportDefaultSpecifier, ImportSpecifier,
KeyValueProp, Lit, Module, ModuleDecl, ModuleItem, ObjectLit, Program, Prop, PropName,
PropOrSpread, Stmt, Str,
},
utils::private_ident,
},
quote,
};
use turbopack_ecmascript::TURBOPACK_HELPER;

pub fn create_proxy_module(transition_name: &str, target_import: &str) -> Program {
let ident = private_ident!("createProxy");
Program::Module(Module {
body: vec![
ModuleItem::Stmt(Stmt::Expr(ExprStmt {
expr: Box::new(Expr::Lit(Lit::Str(Str {
value: format!("TURBOPACK {{ transition: {transition_name} }}").into(),
raw: None,
span: DUMMY_SP,
}))),
span: DUMMY_SP,
})),
ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl {
specifiers: vec![ImportSpecifier::Default(ImportDefaultSpecifier {
local: ident.clone(),
span: DUMMY_SP,
})],
src: Box::new(target_import.into()),
type_only: false,
asserts: Some(Box::new(ObjectLit {
span: DUMMY_SP,
props: vec![PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Ident(Ident::new(TURBOPACK_HELPER.into(), DUMMY_SP)),
value: Box::new(Expr::Lit(true.into())),
})))],
})),
span: DUMMY_SP,
})),
ModuleItem::Stmt(quote!(
"__turbopack_export_namespace__($proxy);" as Stmt,
proxy = ident,
)),
],
shebang: None,
span: DUMMY_SP,
})
}
8 changes: 2 additions & 6 deletions crates/turbopack-ecmascript-plugins/src/transform/emotion.rs
Expand Up @@ -94,11 +94,7 @@ impl EmotionTransformer {

#[async_trait]
impl CustomTransformer for EmotionTransformer {
async fn transform(
&self,
program: &mut Program,
ctx: &TransformContext<'_>,
) -> Result<Option<Program>> {
async fn transform(&self, program: &mut Program, ctx: &TransformContext<'_>) -> Result<()> {
#[cfg(feature = "transform_emotion")]
{
let p = std::mem::replace(program, Program::Module(Module::dummy()));
Expand All @@ -117,7 +113,7 @@ impl CustomTransformer for EmotionTransformer {
));
}

Ok(None)
Ok(())
}
}

Expand Down
4 changes: 4 additions & 0 deletions crates/turbopack-ecmascript-plugins/src/transform/mod.rs
@@ -1,2 +1,6 @@
pub mod directives;
pub mod emotion;
pub mod modularize_imports;
pub mod relay;
pub mod styled_components;
pub mod styled_jsx;
@@ -0,0 +1,63 @@
use std::collections::HashMap;

use anyhow::Result;
use async_trait::async_trait;
use indexmap::IndexMap;
use modularize_imports::{modularize_imports, Config, PackageConfig};
use serde::{Deserialize, Serialize};
use swc_core::{
common::util::take::Take,
ecma::{
ast::{Module, Program},
visit::FoldWith,
},
};
use turbo_tasks::trace::TraceRawVcs;
use turbopack_ecmascript::{CustomTransformer, TransformContext};

#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, TraceRawVcs)]
#[serde(rename_all = "camelCase")]
pub struct ModularizeImportPackageConfig {
pub transform: String,
#[serde(default)]
pub prevent_full_import: bool,
#[serde(default)]
pub skip_default_conversion: bool,
}

#[derive(Debug)]
pub struct ModularizeImportsTransformer {
packages: HashMap<String, PackageConfig>,
}

impl ModularizeImportsTransformer {
pub fn new(packages: &IndexMap<String, ModularizeImportPackageConfig>) -> Self {
Self {
packages: packages
.iter()
.map(|(k, v)| {
(
k.clone(),
PackageConfig {
transform: v.transform.clone(),
prevent_full_import: v.prevent_full_import,
skip_default_conversion: v.skip_default_conversion,
},
)
})
.collect(),
}
}
}

#[async_trait]
impl CustomTransformer for ModularizeImportsTransformer {
async fn transform(&self, program: &mut Program, _ctx: &TransformContext<'_>) -> Result<()> {
let p = std::mem::replace(program, Program::Module(Module::dummy()));
*program = p.fold_with(&mut modularize_imports(Config {
packages: self.packages.clone(),
}));

Ok(())
}
}
8 changes: 2 additions & 6 deletions crates/turbopack-ecmascript-plugins/src/transform/relay.rs
Expand Up @@ -24,11 +24,7 @@ impl RelayTransformer {

#[async_trait]
impl CustomTransformer for RelayTransformer {
async fn transform(
&self,
program: &mut Program,
ctx: &TransformContext<'_>,
) -> Result<Option<Program>> {
async fn transform(&self, program: &mut Program, ctx: &TransformContext<'_>) -> Result<()> {
// If user supplied artifact_directory, it should be resolvable already.
// Otherwise, supply default relative path (./__generated__)
let (root, config) = if self.config.artifact_directory.is_some() {
Expand All @@ -52,6 +48,6 @@ impl CustomTransformer for RelayTransformer {
Some(ctx.unresolved_mark),
));

Ok(None)
Ok(())
}
}
@@ -0,0 +1,33 @@
use std::path::PathBuf;

use anyhow::Result;
use async_trait::async_trait;
use swc_core::{
common::FileName,
ecma::{ast::Program, visit::VisitMutWith},
};
use turbopack_ecmascript::{CustomTransformer, TransformContext};

#[derive(Debug)]
pub struct StyledComponentsTransformer {
config: styled_components::Config,
}

impl StyledComponentsTransformer {
pub fn new(config: styled_components::Config) -> Self {
Self { config }
}
}

#[async_trait]
impl CustomTransformer for StyledComponentsTransformer {
async fn transform(&self, program: &mut Program, ctx: &TransformContext<'_>) -> Result<()> {
program.visit_mut_with(&mut styled_components::styled_components(
FileName::Real(PathBuf::from(ctx.file_path_str)),
ctx.file_name_hash,
self.config.clone(),
));

Ok(())
}
}
33 changes: 33 additions & 0 deletions crates/turbopack-ecmascript-plugins/src/transform/styled_jsx.rs
@@ -0,0 +1,33 @@
use anyhow::Result;
use async_trait::async_trait;
use swc_core::{
common::{util::take::Take, FileName},
ecma::{
ast::{Module, Program},
visit::FoldWith,
},
};
use turbopack_ecmascript::{CustomTransformer, TransformContext};

#[derive(Debug)]
pub struct StyledJsxTransformer;

impl StyledJsxTransformer {
pub fn new() -> Self {
Self
}
}

#[async_trait]
impl CustomTransformer for StyledJsxTransformer {
async fn transform(&self, program: &mut Program, ctx: &TransformContext<'_>) -> Result<()> {
let p = std::mem::replace(program, Program::Module(Module::dummy()));
*program = p.fold_with(&mut styled_jsx::visitor::styled_jsx(
ctx.source_map.clone(),
// styled_jsx don't really use that in a relevant way
FileName::Anon,
));

Ok(())
}
}

0 comments on commit 95be42a

Please sign in to comment.