Skip to content

Commit

Permalink
feat(turbopack-ecmascript): accept transform plugin in moduleoptionco…
Browse files Browse the repository at this point in the history
…ntext (vercel#4721)

### Description

- Relates with WEB-955, WEB-848.

This PR is a part of implementing WEB-848, supporting relay graphql
transform in next.js. Historically we've been expanding
`ModuleOptionContext` to have each own config (`enable_mdx`,
`enable_emotion`...) but with new CustomTransformPlugin we can pass
actual transforms instead, what WEB-955 aims.

PR introduces new config option `custom_ecma_transform_plugins` to the
ModuleOptionContext to support it. `custom_ecma_transform_plugins` is a
struct have two fields, `before` / `after` which allows to specify order
of custom transforms if it should be invoked _before_ any core transform
kicks in, or otherwise.

This effectively deprecates existing options, notably
`custom_ecmascript_transforms` and `custom_ecmascript_app_transforms`.
It is currently not actively being used (next-swc have 2 places
utilizing `custom_ecmascript_transforms`), we can consolidate to the new
option instead.

One another notable change is customtransformer's transform is now async
fn. It is due to `TransformContext` have couple of `Vc` properties, that
cannot be accessed in a sync transform fn. Refer counterpart next.js PR
(https://github.com/vercel/next.js/pull/48899/files#diff-c0df3b5a390436575f35365774b078cb5303ef395b064b8992dfdd7dba8f3691)
as ref.
  • Loading branch information
kwonoj authored and NicholasLYang committed May 9, 2023
1 parent 486b954 commit 52dc092
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 10 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions crates/turbopack-ecmascript-plugins/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@ transform_emotion = []

[dependencies]
anyhow = { workspace = true }
async-trait = { workspace = true }
serde = { workspace = true }

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

swc_core = { workspace = true, features = ["ecma_ast", "ecma_visit", "common"] }
swc_emotion = { workspace = true }
swc_relay = { workspace = true }

[build-dependencies]
turbo-tasks-build = { workspace = true }
10 changes: 8 additions & 2 deletions crates/turbopack-ecmascript-plugins/src/transform/emotion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::{
};

use anyhow::Result;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use swc_core::{
common::util::take::Take,
Expand Down Expand Up @@ -91,8 +92,13 @@ impl EmotionTransformer {
}
}

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

None
Ok(None)
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/turbopack-ecmascript-plugins/src/transform/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod emotion;
pub mod relay;
57 changes: 57 additions & 0 deletions crates/turbopack-ecmascript-plugins/src/transform/relay.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use std::path::PathBuf;

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 RelayTransformer {
config: swc_relay::Config,
}

impl RelayTransformer {
pub fn new(config: swc_relay::Config) -> Self {
Self { config }
}
}

#[async_trait]
impl CustomTransformer for RelayTransformer {
async fn transform(
&self,
program: &mut Program,
ctx: &TransformContext<'_>,
) -> Result<Option<Program>> {
// 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() {
(PathBuf::new(), None)
} else {
let config = swc_relay::Config {
artifact_directory: Some(PathBuf::from("__generated__")),
..self.config
};
(PathBuf::from("."), Some(config))
};

let p = std::mem::replace(program, Program::Module(Module::dummy()));
*program = p.fold_with(&mut swc_relay::relay(
config.as_ref().unwrap_or_else(|| &self.config),
FileName::Real(PathBuf::from(ctx.file_name_str)),
root,
// [TODO]: pages_dir comes through next-swc-loader
// https://github.com/vercel/next.js/blob/ea472e8058faea8ebdab2ef6d3aab257a1f0d11c/packages/next/src/build/webpack-config.ts#L792
None,
Some(ctx.unresolved_mark),
));

Ok(None)
}
}
5 changes: 3 additions & 2 deletions crates/turbopack-ecmascript/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ use swc_core::{
},
};
pub use transform::{
CustomTransformer, EcmascriptInputTransform, EcmascriptInputTransformsVc, TransformContext,
TransformPlugin, TransformPluginVc,
CustomTransformer, EcmascriptInputTransform, EcmascriptInputTransformsVc,
OptionTransformPlugin, OptionTransformPluginVc, TransformContext, TransformPlugin,
TransformPluginVc,
};
use turbo_tasks::{
primitives::StringVc, trace::TraceRawVcs, RawVc, ReadRef, TryJoinIterExt, Value, ValueToString,
Expand Down
28 changes: 24 additions & 4 deletions crates/turbopack-ecmascript/src/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod util;
use std::{fmt::Debug, hash::Hash, path::PathBuf, sync::Arc};

use anyhow::Result;
use async_trait::async_trait;
use swc_core::{
base::SwcComments,
common::{chain, util::take::Take, FileName, Mark, SourceMap},
Expand Down Expand Up @@ -77,8 +78,13 @@ pub enum EcmascriptInputTransform {

/// The CustomTransformer trait allows you to implement your own custom SWC
/// transformer to run over all ECMAScript files imported in the graph.
#[async_trait]
pub trait CustomTransformer: Debug {
fn transform(&self, program: &mut Program, ctx: &TransformContext<'_>) -> Option<Program>;
async fn transform(
&self,
program: &mut Program,
ctx: &TransformContext<'_>,
) -> Result<Option<Program>>;
}

/// A wrapper around a TransformPlugin instance, allowing it to operate with
Expand All @@ -93,9 +99,23 @@ pub trait CustomTransformer: Debug {
#[derive(Debug)]
pub struct TransformPlugin(#[turbo_tasks(trace_ignore)] Box<dyn CustomTransformer + Send + Sync>);

#[turbo_tasks::value(transparent)]
pub struct OptionTransformPlugin(Option<TransformPluginVc>);

impl Default for OptionTransformPluginVc {
fn default() -> Self {
OptionTransformPluginVc::cell(None)
}
}

#[async_trait]
impl CustomTransformer for TransformPlugin {
fn transform(&self, program: &mut Program, ctx: &TransformContext<'_>) -> Option<Program> {
self.0.transform(program, ctx)
async fn transform(
&self,
program: &mut Program,
ctx: &TransformContext<'_>,
) -> Result<Option<Program>> {
self.0.transform(program, ctx).await
}
}

Expand Down Expand Up @@ -323,7 +343,7 @@ impl EcmascriptInputTransform {
}
}
EcmascriptInputTransform::Plugin(transform) => {
if let Some(output) = transform.await?.transform(program, ctx) {
if let Some(output) = transform.await?.transform(program, ctx).await? {
*program = output;
}
}
Expand Down
29 changes: 28 additions & 1 deletion crates/turbopack/src/module_options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ impl ModuleOptionsVc {
preset_env_versions,
ref custom_ecmascript_app_transforms,
ref custom_ecmascript_transforms,
ref custom_ecma_transform_plugins,
ref custom_rules,
execution_context,
ref rules,
Expand All @@ -93,7 +94,30 @@ impl ModuleOptionsVc {
}
}
}
let mut transforms = custom_ecmascript_app_transforms.clone();

let (before_transform_plugins, after_transform_plugins) =
if let Some(transform_plugins) = custom_ecma_transform_plugins {
let transform_plugins = transform_plugins.await?;
(
transform_plugins
.source_transforms
.iter()
.cloned()
.map(EcmascriptInputTransform::Plugin)
.collect(),
transform_plugins
.output_transforms
.iter()
.cloned()
.map(|plugin| EcmascriptInputTransform::Plugin(plugin))
.collect(),
)
} else {
(vec![], vec![])
};

let mut transforms = before_transform_plugins;
transforms.extend(custom_ecmascript_app_transforms.iter().cloned());
transforms.extend(custom_ecmascript_transforms.iter().cloned());

// Order of transforms is important. e.g. if the React transform occurs before
Expand Down Expand Up @@ -182,6 +206,7 @@ impl ModuleOptionsVc {
.iter()
.cloned()
.chain(transforms.iter().cloned())
.chain(after_transform_plugins.iter().cloned())
.collect(),
)
} else {
Expand All @@ -202,6 +227,7 @@ impl ModuleOptionsVc {
.iter()
.cloned()
.chain(transforms.iter().cloned())
.chain(after_transform_plugins.iter().cloned())
.collect(),
);

Expand All @@ -222,6 +248,7 @@ impl ModuleOptionsVc {
.iter()
.cloned()
.chain(transforms.iter().cloned())
.chain(after_transform_plugins.iter().cloned())
.collect(),
);

Expand Down
18 changes: 17 additions & 1 deletion crates/turbopack/src/module_options/module_options_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use turbo_tasks::trace::TraceRawVcs;
use turbopack_core::{environment::EnvironmentVc, resolve::options::ImportMappingVc};
use turbopack_ecmascript::EcmascriptInputTransform;
use turbopack_ecmascript::{EcmascriptInputTransform, TransformPluginVc};
use turbopack_ecmascript_plugins::transform::emotion::EmotionTransformConfigVc;
use turbopack_node::{
execution_context::ExecutionContextVc, transforms::webpack::WebpackLoaderConfigItemsVc,
Expand Down Expand Up @@ -156,6 +156,18 @@ impl Default for StyledComponentsTransformConfigVc {
}
}

/// Configuration options for the custom ecma transform to be applied.
#[turbo_tasks::value(shared)]
#[derive(Default, Clone)]
pub struct CustomEcmascriptTransformPlugins {
/// List of plugins to be applied before the main transform.
/// Transform will be applied in the order of the list.
pub source_transforms: Vec<TransformPluginVc>,
/// List of plugins to be applied after the main transform.
/// Transform will be applied in the order of the list.
pub output_transforms: Vec<TransformPluginVc>,
}

#[turbo_tasks::value(shared)]
#[derive(Default, Clone)]
pub struct ModuleOptionsContext {
Expand Down Expand Up @@ -187,11 +199,15 @@ pub struct ModuleOptionsContext {
pub enable_mdx_rs: bool,
#[serde(default)]
pub preset_env_versions: Option<EnvironmentVc>,
#[deprecated(note = "use custom_ecma_transform_plugins instead")]
#[serde(default)]
pub custom_ecmascript_app_transforms: Vec<EcmascriptInputTransform>,
#[deprecated(note = "use custom_ecma_transform_plugins instead")]
#[serde(default)]
pub custom_ecmascript_transforms: Vec<EcmascriptInputTransform>,
#[serde(default)]
pub custom_ecma_transform_plugins: Option<CustomEcmascriptTransformPluginsVc>,
#[serde(default)]
/// Custom rules to be applied after all default rules.
pub custom_rules: Vec<ModuleRule>,
#[serde(default)]
Expand Down

0 comments on commit 52dc092

Please sign in to comment.