Repository: vercel/next.js
Pull request: TURBOPACK: typescript transform support verbatimModuleSyntax

In utoo case, we need to support react classic jsx transform(To compitable with react 16 or above project).

We have a case like:

```ts
// index.ts
import React from 'react';

export const App = () => <div></div>;
```

Under the classic jsx transform, the jsx will be transformed as follows:

```ts
export const App = () => React.createElement("div", /***/);
```

But turbopack's jsx transform was behind typescript transform, so in the stage of typescript transform, the import of react will be deleted by the default config of swc typescript transform(which set `verbatimmodulesyntax` as false by default): https://swc.rs/docs/migrating-from-tsc#verbatimmodulesyntax-true

Then the output of classic jsx transform will get a runtime error like `React was not found`. 

 For solve the problem, this pr just support a config `verbatimModuleSyntax` for turbopack's typescript transform, it will not remove user's React import under classic jsx transform.

We test the case under utoopack in the following pr:

- https://github.com/utooland/utoo/pull/2653
- https://github.com/utooland/utoo/pull/2655

And Rspack also enable `verbatimModuleSyntax` to true by default under 2.0: https://github.com/web-infra-dev/rspack/discussions/9270#discussioncomment-13429924 


Changed files:
filename: crates/next-core/src/transform_options.rs
patch: @@ -54,9 +54,23 @@ pub async fn get_typescript_transform_options(
 ) -> Result<Vc<TypescriptTransformOptions>> {
     let tsconfig = get_typescript_options(project_path, tsconfig_path).await?;
 
-    let use_define_for_class_fields = if let Some(tsconfig) = tsconfig {
-        read_from_tsconfigs(&tsconfig, |json, _| {
-            json["compilerOptions"]["useDefineForClassFields"].as_bool()
+    let use_define_for_class_fields = if let Some(ref tsconfig) = tsconfig {
+        read_from_tsconfigs(tsconfig, |json, _| {
+            json.get("compilerOptions")
+                .and_then(|opts| opts.get("useDefineForClassFields"))
+                .and_then(|v| v.as_bool())
+        })
+        .await?
+        .unwrap_or(false)
+    } else {
+        false
+    };
+
+    let verbatim_module_syntax = if let Some(ref tsconfig) = tsconfig {
+        read_from_tsconfigs(tsconfig, |json, _| {
+            json.get("compilerOptions")
+                .and_then(|opts| opts.get("verbatimModuleSyntax"))
+                .and_then(|v| v.as_bool())
         })
         .await?
         .unwrap_or(false)
@@ -66,6 +80,7 @@ pub async fn get_typescript_transform_options(
 
     let ts_transform_options = TypescriptTransformOptions {
         use_define_for_class_fields,
+        verbatim_module_syntax,
     };
 
     Ok(ts_transform_options.cell())
@@ -82,7 +97,9 @@ pub async fn get_decorators_transform_options(
 
     let experimental_decorators = if let Some(ref tsconfig) = tsconfig {
         read_from_tsconfigs(tsconfig, |json, _| {
-            json["compilerOptions"]["experimentalDecorators"].as_bool()
+            json.get("compilerOptions")
+                .and_then(|opts| opts.get("experimentalDecorators"))
+                .and_then(|v| v.as_bool())
         })
         .await?
         .unwrap_or(false)
@@ -103,7 +120,9 @@ pub async fn get_decorators_transform_options(
 
     let emit_decorators_metadata = if let Some(ref tsconfig) = tsconfig {
         read_from_tsconfigs(tsconfig, |json, _| {
-            json["compilerOptions"]["emitDecoratorMetadata"].as_bool()
+            json.get("compilerOptions")
+                .and_then(|opts| opts.get("emitDecoratorMetadata"))
+                .and_then(|v| v.as_bool())
         })
         .await?
         .unwrap_or(false)
@@ -113,7 +132,9 @@ pub async fn get_decorators_transform_options(
 
     let use_define_for_class_fields = if let Some(ref tsconfig) = tsconfig {
         read_from_tsconfigs(tsconfig, |json, _| {
-            json["compilerOptions"]["useDefineForClassFields"].as_bool()
+            json.get("compilerOptions")
+                .and_then(|opts| opts.get("useDefineForClassFields"))
+                .and_then(|v| v.as_bool())
         })
         .await?
         .unwrap_or(false)
@@ -186,8 +207,10 @@ pub async fn get_jsx_transform_options(
 
     let react_transform_options = if let Some(tsconfig) = tsconfig {
         read_from_tsconfigs(&tsconfig, |json, _| {
-            let jsx_import_source = json["compilerOptions"]["jsxImportSource"]
-                .as_str()
+            let jsx_import_source = json
+                .get("compilerOptions")
+                .and_then(|opts| opts.get("jsxImportSource"))
+                .and_then(|v| v.as_str())
                 .map(|s| s.into());
 
             Some(JsxTransformOptions {

filename: turbopack/crates/turbopack-ecmascript/src/transform/mod.rs
patch: @@ -15,6 +15,7 @@ use swc_core::{
                 helpers::{HELPERS, HelperData, Helpers},
             },
             react::react,
+            typescript::{Config, typescript},
         },
         utils::IsDirective,
     },
@@ -44,6 +45,7 @@ pub enum EcmascriptInputTransform {
     // it doesn't derive `Copy` so repeating values in here
     TypeScript {
         use_define_for_class_fields: bool,
+        verbatim_module_syntax: bool,
     },
     Decorators {
         is_legacy: bool,
@@ -230,9 +232,12 @@ impl EcmascriptInputTransform {
             EcmascriptInputTransform::TypeScript {
                 // TODO(WEB-1213)
                 use_define_for_class_fields: _use_define_for_class_fields,
+                verbatim_module_syntax,
             } => {
-                use swc_core::ecma::transforms::typescript::typescript;
-                let config = Default::default();
+                let config = Config {
+                    verbatim_module_syntax: *verbatim_module_syntax,
+                    ..Default::default()
+                };
                 apply_transform(
                     program,
                     helpers,

filename: turbopack/crates/turbopack/src/module_options/mod.rs
patch: @@ -716,12 +716,14 @@ impl ModuleOptions {
         ]);
 
         if let Some(options) = enable_typescript_transform {
+            let options = options.await?;
             let ts_preprocess = ResolvedVc::cell(
                 decorators_transform
                     .clone()
                     .into_iter()
                     .chain(std::iter::once(EcmascriptInputTransform::TypeScript {
-                        use_define_for_class_fields: options.await?.use_define_for_class_fields,
+                        use_define_for_class_fields: options.use_define_for_class_fields,
+                        verbatim_module_syntax: options.verbatim_module_syntax,
                     }))
                     .collect(),
             );

filename: turbopack/crates/turbopack/src/module_options/module_options_context.rs
patch: @@ -165,6 +165,7 @@ impl ValueDefault for DecoratorsOptions {
 #[derive(Default, Clone, Debug)]
 pub struct TypescriptTransformOptions {
     pub use_define_for_class_fields: bool,
+    pub verbatim_module_syntax: bool,
 }
 
 #[turbo_tasks::value_impl]