Skip to content

Commit

Permalink
feat(turbopack_ecmascript): support partial tsconfig for the transform (
Browse files Browse the repository at this point in the history
vercel/turborepo#3995)

Resolves WEB-667, WEB-659.

This PR allows to specify partial tsconfig (specifically,
`useDefineForClassFields` / legacy decorators for now) into
ecmatransform. There are few tsconfig options affect to the runtime
output of typescript need to be specified when performing transform,
useDefineForClassFields is one of them.

For now, PR attempts to fix
`test/development/basic/define-class-fields.test.ts` /
`test/development/basic/legacy-decorators.test.ts` by enabling one
options. .swcrc is still not being honored in this changes.
  • Loading branch information
kwonoj authored Mar 7, 2023
1 parent ca1c87e commit 932d32c
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 6 deletions.
1 change: 1 addition & 0 deletions crates/turbopack-ecmascript/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ swc_core = { workspace = true, features = [
"ecma_transforms_module",
"ecma_transforms_react",
"ecma_transforms_typescript",
"ecma_transforms_proposal",
"ecma_quote",
"ecma_visit",
"ecma_visit_path",
Expand Down
21 changes: 20 additions & 1 deletion crates/turbopack-ecmascript/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ use turbo_tasks::{
primitives::{StringVc, U64Vc},
Value, ValueToString,
};
use turbo_tasks_fs::{FileContent, FileSystemPath};
use turbo_tasks_fs::{FileContent, FileJsonContentVc, FileSystemPath};
use turbo_tasks_hash::hash_xxh3_hash64;
use turbopack_core::{
asset::{Asset, AssetContent, AssetVc},
resolve::{find_context_file, node::node_cjs_resolve_options, FindContextFileResult},
source_asset::SourceAssetVc,
source_map::{GenerateSourceMap, GenerateSourceMapVc, SourceMapVc},
};
use turbopack_swc_utils::emitter::IssueEmitter;
Expand All @@ -35,6 +37,7 @@ use super::EcmascriptModuleAssetType;
use crate::{
analyzer::graph::EvalContext,
transform::{EcmascriptInputTransformsVc, TransformContext},
typescript::resolve::{read_tsconfigs, tsconfig},
utils::WrapFuture,
EcmascriptInputTransform,
};
Expand Down Expand Up @@ -138,6 +141,19 @@ pub async fn parse(
let fs_path = &*source.ident().path().await?;
let file_path_hash = *hash_ident(source.ident().to_string()).await? as u128;
let ty = ty.into_value();
let tsconfig = find_context_file(source.ident().path(), tsconfig());
let tsconfig = match *tsconfig.await? {
FindContextFileResult::Found(path, _) => Some(
read_tsconfigs(
path.read(),
SourceAssetVc::new(path).into(),
node_cjs_resolve_options(path.root()),
)
.await?,
),
FindContextFileResult::NotFound(_) => None,
};

Ok(match &*content.await? {
AssetContent::File(file) => match &*file.await? {
FileContent::NotFound => ParseResult::NotFound.cell(),
Expand All @@ -151,6 +167,7 @@ pub async fn parse(
source,
ty,
transforms,
tsconfig,
)
.await
{
Expand Down Expand Up @@ -178,6 +195,7 @@ async fn parse_content(
source: AssetVc,
ty: EcmascriptModuleAssetType,
transforms: &[EcmascriptInputTransform],
tsconfig: Option<Vec<(FileJsonContentVc, AssetVc)>>,
) -> Result<ParseResultVc> {
let source_map: Arc<SourceMap> = Default::default();
let handler = Handler::with_emitter(
Expand Down Expand Up @@ -284,6 +302,7 @@ async fn parse_content(
file_path_str: &fs_path.path,
file_name_str: fs_path.file_name(),
file_name_hash: file_path_hash,
tsconfig: &tsconfig,
};
for transform in transforms.iter() {
transform.apply(&mut parsed_program, &context).await?;
Expand Down
70 changes: 66 additions & 4 deletions crates/turbopack-ecmascript/src/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use swc_core::{
preset_env::{self, Targets},
transforms::{
base::{feature::FeatureFlag, helpers::inject_helpers, resolver, Assumptions},
proposal::decorators,
react::react,
},
visit::{FoldWith, VisitMutWith},
Expand All @@ -24,8 +25,10 @@ use turbo_tasks::{
primitives::{StringVc, StringsVc},
trace::TraceRawVcs,
};
use turbo_tasks_fs::{json::parse_json_with_source_context, FileSystemPathVc};
use turbopack_core::environment::EnvironmentVc;
use turbo_tasks_fs::{
json::parse_json_with_source_context, FileJsonContent, FileJsonContentVc, FileSystemPathVc,
};
use turbopack_core::{asset::AssetVc, environment::EnvironmentVc};

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

Expand Down Expand Up @@ -78,6 +81,9 @@ pub enum EcmascriptInputTransform {
StyledComponents,
StyledJsx,
TypeScript,
// Apply ecma decorators transform. This is not part of Typescript transform, even though
// decorators can be ts-specific (legacy decorartors) since there's ecma decorators for js.
Decorators,
}

#[turbo_tasks::value(transparent, serialization = "auto_for_input")]
Expand All @@ -102,6 +108,7 @@ pub struct TransformContext<'a> {
pub file_path_str: &'a str,
pub file_name_str: &'a str,
pub file_name_hash: u128,
pub tsconfig: &'a Option<Vec<(FileJsonContentVc, AssetVc)>>,
}

impl EcmascriptInputTransform {
Expand All @@ -116,6 +123,7 @@ impl EcmascriptInputTransform {
file_path_str,
file_name_str,
file_name_hash,
tsconfig,
}: &TransformContext<'_>,
) -> Result<()> {
match *self {
Expand Down Expand Up @@ -198,9 +206,63 @@ impl EcmascriptInputTransform {
FileName::Anon,
));
}
EcmascriptInputTransform::Decorators => {
// TODO: Currently this only supports legacy decorators from tsconfig / jsconfig
// options.
if let Some(tsconfig) = tsconfig {
// Selectively picks up tsconfig.json values to construct
// swc transform's stripconfig. It doesn't account .swcrc config currently.
for (value, _) in tsconfig {
let value = &*value.await?;
if let FileJsonContent::Content(value) = value {
let legacy_decorators = value["compilerOptions"]
["experimentalDecorators"]
.as_bool()
.unwrap_or(false);

if legacy_decorators {
// TODO: `fn decorators` does not support visitMut yet
let p =
std::mem::replace(program, Program::Module(Module::dummy()));
*program = p.fold_with(&mut chain!(
decorators(decorators::Config {
legacy: true,
emit_metadata: true,
use_define_for_class_fields: value["compilerOptions"]
["useDefineForClassFields"]
.as_bool()
.unwrap_or(false),
}),
inject_helpers(unresolved_mark),
));
}
}
}
};
}
EcmascriptInputTransform::TypeScript => {
use swc_core::ecma::transforms::typescript::strip;
program.visit_mut_with(&mut strip(top_level_mark));
use swc_core::ecma::transforms::typescript::{strip_with_config, Config};

let config = if let Some(tsconfig) = tsconfig {
let mut config = Config {
..Default::default()
};

for (value, _) in tsconfig {
let value = &*value.await?;
if let FileJsonContent::Content(value) = value {
let use_define_for_class_fields =
&value["compilerOptions"]["useDefineForClassFields"];
config.use_define_for_class_fields =
use_define_for_class_fields.as_bool().unwrap_or(false);
}
}
config
} else {
Default::default()
};

program.visit_mut_with(&mut strip_with_config(config, top_level_mark));
}
EcmascriptInputTransform::ClientDirective(transition_name) => {
let transition_name = &*transition_name.await?;
Expand Down
5 changes: 4 additions & 1 deletion crates/turbopack/src/module_options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ impl ModuleOptionsVc {
}
}
}
let mut transforms = custom_ecmascript_app_transforms.clone();
// We apply decorators _before_ any ts transforms, as some of decorator requires
// type information.
let mut transforms = vec![EcmascriptInputTransform::Decorators];
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

0 comments on commit 932d32c

Please sign in to comment.