Skip to content

Commit

Permalink
feat(turbopack): support transform options (#47365)
Browse files Browse the repository at this point in the history
<!-- 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



-->

### What?

This PR implements a feature for the turbpack to support (partial)
transform configuration inherited from ts/jsconfigs, notably for the
legacy decorators and jsx runtimes.

### Why?

### How?
- Closes WEB-667
  • Loading branch information
kwonoj authored Apr 3, 2023
1 parent aeec6b5 commit 036f540
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 74 deletions.
2 changes: 1 addition & 1 deletion packages/next-swc/crates/next-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub mod react_refresh;
pub mod router;
pub mod router_source;
mod runtime;
mod typescript;
mod transform_options;
mod util;
mod web_entry_source;

Expand Down
18 changes: 9 additions & 9 deletions packages/next-swc/crates/next-core/src/next_client/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use turbo_binding::{
turbopack::{
module_options::{
module_options_context::{ModuleOptionsContext, ModuleOptionsContextVc},
JsxTransformOptions, PostCssTransformOptions, WebpackLoadersOptions,
PostCssTransformOptions, WebpackLoadersOptions,
},
resolve_options_context::{ResolveOptionsContext, ResolveOptionsContextVc},
transition::TransitionsByNameVc,
Expand All @@ -46,7 +46,10 @@ use crate::{
get_next_client_resolved_map,
},
react_refresh::assert_can_resolve_react_refresh,
typescript::get_typescript_transform_options,
transform_options::{
get_decorators_transform_options, get_jsx_transform_options,
get_typescript_transform_options,
},
util::foreign_code_context_condition,
};

Expand Down Expand Up @@ -153,6 +156,8 @@ pub async fn get_client_module_options_context(
.is_found();

let tsconfig = get_typescript_transform_options(project_path);
let decorators_options = get_decorators_transform_options(project_path);
let jsx_runtime_options = get_jsx_transform_options(project_path);
let enable_webpack_loaders = {
let options = &*next_config.webpack_loaders_options().await?;
let loaders_options = WebpackLoadersOptions {
Expand All @@ -179,13 +184,7 @@ pub async fn get_client_module_options_context(
// We don't need to resolve React Refresh for each module. Instead,
// we try resolve it once at the root and pass down a context to all
// the modules.
enable_jsx: Some(
JsxTransformOptions {
import_source: None,
runtime: None,
}
.cell(),
),
enable_jsx: Some(jsx_runtime_options),
enable_emotion: true,
enable_react_refresh,
enable_styled_components: true,
Expand All @@ -196,6 +195,7 @@ pub async fn get_client_module_options_context(
}),
enable_webpack_loaders,
enable_typescript_transform: Some(tsconfig),
decorators: Some(decorators_options),
rules: vec![(
foreign_code_context_condition(next_config).await?,
module_options_context.clone().cell(),
Expand Down
33 changes: 17 additions & 16 deletions packages/next-swc/crates/next-core/src/next_server/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ use turbo_binding::{
node::execution_context::ExecutionContextVc,
turbopack::{
module_options::{
JsxTransformOptions, JsxTransformOptionsVc, ModuleOptionsContext,
ModuleOptionsContextVc, PostCssTransformOptions, WebpackLoadersOptions,
ModuleOptionsContext, ModuleOptionsContextVc, PostCssTransformOptions,
WebpackLoadersOptions,
},
resolve_options_context::{ResolveOptionsContext, ResolveOptionsContextVc},
},
Expand All @@ -31,7 +31,10 @@ use crate::{
next_build::{get_external_next_compiled_package_mapping, get_postcss_package_mapping},
next_config::NextConfigVc,
next_import_map::get_next_server_import_map,
typescript::get_typescript_transform_options,
transform_options::{
get_decorators_transform_options, get_jsx_transform_options,
get_typescript_transform_options,
},
util::foreign_code_context_condition,
};

Expand Down Expand Up @@ -230,6 +233,8 @@ pub async fn get_server_module_options_context(
};

let tsconfig = get_typescript_transform_options(project_path);
let decorators_options = get_decorators_transform_options(project_path);
let jsx_runtime_options = get_jsx_transform_options(project_path);

let module_options_context = match ty.into_value() {
ServerContextType::Pages { .. } | ServerContextType::PagesData { .. } => {
Expand All @@ -238,11 +243,12 @@ pub async fn get_server_module_options_context(
..Default::default()
};
ModuleOptionsContext {
enable_jsx: Some(get_jsx_transform_options()),
enable_jsx: Some(jsx_runtime_options),
enable_styled_jsx: true,
enable_postcss_transform,
enable_webpack_loaders,
enable_typescript_transform: Some(tsconfig),
decorators: Some(decorators_options),
rules: vec![(
foreign_code_context_condition,
module_options_context.clone().cell(),
Expand All @@ -257,11 +263,12 @@ pub async fn get_server_module_options_context(
..Default::default()
};
ModuleOptionsContext {
enable_jsx: Some(get_jsx_transform_options()),
enable_jsx: Some(jsx_runtime_options),
enable_styled_jsx: true,
enable_postcss_transform,
enable_webpack_loaders,
enable_typescript_transform: Some(tsconfig),
decorators: Some(decorators_options),
rules: vec![(
foreign_code_context_condition,
module_options_context.clone().cell(),
Expand All @@ -279,10 +286,11 @@ pub async fn get_server_module_options_context(
..Default::default()
};
ModuleOptionsContext {
enable_jsx: Some(get_jsx_transform_options()),
enable_jsx: Some(jsx_runtime_options),
enable_postcss_transform,
enable_webpack_loaders,
enable_typescript_transform: Some(tsconfig),
decorators: Some(decorators_options),
rules: vec![(
foreign_code_context_condition,
module_options_context.clone().cell(),
Expand All @@ -300,6 +308,7 @@ pub async fn get_server_module_options_context(
enable_postcss_transform,
enable_webpack_loaders,
enable_typescript_transform: Some(tsconfig),
decorators: Some(decorators_options),
rules: vec![(
foreign_code_context_condition,
module_options_context.clone().cell(),
Expand All @@ -314,11 +323,12 @@ pub async fn get_server_module_options_context(
..Default::default()
};
ModuleOptionsContext {
enable_jsx: Some(get_jsx_transform_options()),
enable_jsx: Some(jsx_runtime_options),
enable_styled_jsx: true,
enable_postcss_transform,
enable_webpack_loaders,
enable_typescript_transform: Some(tsconfig),
decorators: Some(decorators_options),
rules: vec![(
foreign_code_context_condition,
module_options_context.clone().cell(),
Expand All @@ -333,15 +343,6 @@ pub async fn get_server_module_options_context(
Ok(module_options_context)
}

#[turbo_tasks::function]
pub fn get_jsx_transform_options() -> JsxTransformOptionsVc {
JsxTransformOptions {
import_source: None,
runtime: None,
}
.cell()
}

#[turbo_tasks::function]
pub fn get_build_module_options_context() -> ModuleOptionsContextVc {
ModuleOptionsContext {
Expand Down
154 changes: 154 additions & 0 deletions packages/next-swc/crates/next-core/src/transform_options.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use anyhow::Result;
use turbo_binding::turbopack::{
core::{
asset::AssetVc,
resolve::{find_context_file, node::node_cjs_resolve_options, FindContextFileResult},
source_asset::SourceAssetVc,
},
ecmascript::typescript::resolve::{read_from_tsconfigs, read_tsconfigs, tsconfig},
turbopack::module_options::{
DecoratorsKind, DecoratorsOptions, DecoratorsOptionsVc, JsxTransformOptions,
JsxTransformOptionsVc, TypescriptTransformOptions, TypescriptTransformOptionsVc,
},
};
use turbo_tasks_fs::{FileJsonContentVc, FileSystemPathVc};

async fn get_typescript_options(
project_path: FileSystemPathVc,
) -> Option<Vec<(FileJsonContentVc, AssetVc)>> {
let tsconfig = find_context_file(project_path, tsconfig());
match *tsconfig.await.ok()? {
FindContextFileResult::Found(path, _) => Some(
read_tsconfigs(
path.read(),
SourceAssetVc::new(path).into(),
node_cjs_resolve_options(path.root()),
)
.await
.ok()?,
),
FindContextFileResult::NotFound(_) => None,
}
}

/// Build the transform options for specifically for the typescript's runtime
/// outputs
#[turbo_tasks::function]
pub async fn get_typescript_transform_options(
project_path: FileSystemPathVc,
) -> Result<TypescriptTransformOptionsVc> {
let tsconfig = get_typescript_options(project_path).await;

let use_define_for_class_fields = if let Some(tsconfig) = tsconfig {
read_from_tsconfigs(&tsconfig, |json, _| {
json["compilerOptions"]["useDefineForClassFields"].as_bool()
})
.await?
.unwrap_or(false)
} else {
false
};

let ts_transform_options = TypescriptTransformOptions {
use_define_for_class_fields,
};

Ok(ts_transform_options.cell())
}

/// Build the transform options for the decorators.
/// [TODO]: Currnently only typescript's legacy decorators are supported
#[turbo_tasks::function]
pub async fn get_decorators_transform_options(
project_path: FileSystemPathVc,
) -> Result<DecoratorsOptionsVc> {
let tsconfig = get_typescript_options(project_path).await;

let decorators_transform_options = if let Some(tsconfig) = tsconfig {
read_from_tsconfigs(&tsconfig, |json, _| {
let decorators_kind = if json["compilerOptions"]["experimentalDecorators"]
.as_bool()
.unwrap_or(false)
{
Some(DecoratorsKind::Legacy)
} else {
// ref: https://devblogs.microsoft.com/typescript/announcing-typescript-5-0-rc/#differences-with-experimental-legacy-decorators
// `without the flag, decorators will now be valid syntax for all new code.
// Outside of --experimentalDecorators, they will be type-checked and emitted
// differently with ts 5.0, new ecma decorators will be enabled
// if legacy decorators are not enabled
Some(DecoratorsKind::Ecma)
};

let emit_decorators_metadata = if let Some(decorators_kind) = &decorators_kind {
match decorators_kind {
DecoratorsKind::Legacy => {
// ref: This new decorators proposal is not compatible with
// --emitDecoratorMetadata, and it does not allow decorating parameters.
// Future ECMAScript proposals may be able to help bridge that gap
json["compilerOptions"]["emitDecoratorMetadata"]
.as_bool()
.unwrap_or(false)
}
DecoratorsKind::Ecma => false,
}
} else {
false
};

Some(DecoratorsOptions {
decorators_kind,
emit_decorators_metadata,
use_define_for_class_fields: json["compilerOptions"]["useDefineForClassFields"]
.as_bool()
.unwrap_or(false),
..Default::default()
})
})
.await?
.unwrap_or_default()
} else {
Default::default()
};

Ok(decorators_transform_options.cell())
}

#[turbo_tasks::function]
pub async fn get_jsx_transform_options(
project_path: FileSystemPathVc,
) -> Result<JsxTransformOptionsVc> {
let tsconfig = get_typescript_options(project_path).await;

let react_transform_options = if let Some(tsconfig) = tsconfig {
read_from_tsconfigs(&tsconfig, |json, _| {
let jsx_import_source = json["compilerOptions"]["jsxImportSource"]
.as_str()
.map(|s| s.to_string());

// interop between tsconfig's jsx to swc's jsx runtime configuration. Swc's jsx
// runtime is a subset of tsconfig's jsx.
let runtime = if let Some(jsx_runtime) = json["compilerOptions"]["jsx"].as_str() {
match jsx_runtime {
"react" => Some("classic".to_string()),
"react-jsx" => Some("automatic".to_string()),
"react-jsxdev" => Some("automatic".to_string()),
_ => None,
}
} else {
None
};

Some(JsxTransformOptions {
import_source: jsx_import_source,
runtime,
})
})
.await?
.unwrap_or_default()
} else {
Default::default()
};

Ok(react_transform_options.cell())
}
48 changes: 0 additions & 48 deletions packages/next-swc/crates/next-core/src/typescript.rs

This file was deleted.

0 comments on commit 036f540

Please sign in to comment.