diff --git a/bindings/binding_core_wasm/src/types.rs b/bindings/binding_core_wasm/src/types.rs index 1562e264811a..0ceafa570f8a 100644 --- a/bindings/binding_core_wasm/src/types.rs +++ b/bindings/binding_core_wasm/src/types.rs @@ -793,14 +793,16 @@ export interface EsParserConfig { importAssertions?: boolean; } +type JSXPreset = "react" | "react-jsx" | "react-jsxdev" | "preserve" | "react-native"; + /** * Options for transform. */ export interface TransformConfig { /** - * Effective only if `syntax` supports ƒ. + * Effective only if `syntax` supports. */ - react?: ReactConfig; + react?: JSXPreset | ReactConfig; constModules?: ConstModulesConfig; @@ -889,7 +891,7 @@ export interface ReactConfig { /** * jsx runtime */ - runtime?: 'automatic' | 'classic' + runtime?: 'automatic' | 'classic' | 'preserve'; /** * Declares the module specifier to be used for importing the `jsx` and `jsxs` factory functions when using `runtime` 'automatic' diff --git a/crates/swc/tests/fixture/issues-1xxx/1525/case1/input/.swcrc b/crates/swc/tests/fixture/issues-1xxx/1525/case1/input/.swcrc index b4638fb3b8ff..3958c22e2c67 100644 --- a/crates/swc/tests/fixture/issues-1xxx/1525/case1/input/.swcrc +++ b/crates/swc/tests/fixture/issues-1xxx/1525/case1/input/.swcrc @@ -10,9 +10,7 @@ "tsx": true }, "transform": { - "react": { - "runtime": "classic" - } + "react": "react" } }, "module": { diff --git a/crates/swc/tests/fixture/issues-1xxx/1687/.input/.swcrc b/crates/swc/tests/fixture/issues-1xxx/1687/.input/.swcrc index 68c6aeace690..497dc184c4b8 100644 --- a/crates/swc/tests/fixture/issues-1xxx/1687/.input/.swcrc +++ b/crates/swc/tests/fixture/issues-1xxx/1687/.input/.swcrc @@ -7,9 +7,7 @@ "dynamicImport": true }, "transform": { - "react": { - "runtime": "automatic" - } + "react": "react-jsx" }, "externalHelpers": true }, @@ -17,4 +15,4 @@ "coreJs": "3", "mode": "usage" } -} +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-8xxx/8210/input/.swcrc b/crates/swc/tests/fixture/issues-8xxx/8210/input/.swcrc index 53722f9ac21b..68a3a111ce37 100644 --- a/crates/swc/tests/fixture/issues-8xxx/8210/input/.swcrc +++ b/crates/swc/tests/fixture/issues-8xxx/8210/input/.swcrc @@ -11,10 +11,7 @@ "mangle": false }, "transform": { - "react": { - "development": true, - "runtime": "automatic" - } + "react": "react-jsxdev" } }, "module": { diff --git a/crates/swc/tests/tsc.rs b/crates/swc/tests/tsc.rs index ed502afe4005..742520896f41 100644 --- a/crates/swc/tests/tsc.rs +++ b/crates/swc/tests/tsc.rs @@ -142,40 +142,10 @@ fn matrix(input: &Path) -> Vec { .collect(); for value in values { - match value.as_str() { - "react-jsx" => { - let options = react::Options { - runtime: react::Runtime::Automatic(Default::default()), - ..Default::default() - }; - react.push(options); - } - "react-jsxdev" => { - let options = react::Options { - runtime: react::Runtime::Automatic(Default::default()), - common: react::CommonConfig { - development: true.into(), - ..Default::default() - }, - ..Default::default() - }; - react.push(options); - } - "preserve" => { - react.push(react::Options { - runtime: react::Runtime::Preserve, - ..Default::default() - }); - } - "react" => { - let options = react::Options { - runtime: react::Runtime::Classic(Default::default()), - ..Default::default() - }; - react.push(options); - } - _ => {} - } + let Ok(options) = react::Options::try_from(value.as_str()) else { + continue; + }; + react.push(options); } } "removecomments" => {} diff --git a/crates/swc_ecma_transforms_react/src/jsx/mod.rs b/crates/swc_ecma_transforms_react/src/jsx/mod.rs index 35182e9547c8..fa9a2849e7a4 100644 --- a/crates/swc_ecma_transforms_react/src/jsx/mod.rs +++ b/crates/swc_ecma_transforms_react/src/jsx/mod.rs @@ -76,7 +76,8 @@ impl Default for ClassicConfig { } } -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(try_from = "RuntimeRaw")] pub enum Runtime { Classic(ClassicConfig), Automatic(AutomaticConfig), @@ -95,97 +96,53 @@ impl Merge for Runtime { } } -impl<'de> Deserialize<'de> for Runtime { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - use std::fmt; - - use serde::de::{Error, Visitor}; - - struct RuntimeVisitor; - - impl<'de> Visitor<'de> for RuntimeVisitor { - type Value = Runtime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string or an object for runtime configuration") - } - - fn visit_str(self, value: &str) -> Result - where - E: Error, - { - match value { - "automatic" => Ok(Runtime::Automatic(AutomaticConfig::default())), - "classic" => Ok(Runtime::Classic(ClassicConfig::default())), - "preserve" => Ok(Runtime::Preserve), - _ => Err(Error::unknown_variant( - value, - &["automatic", "classic", "preserve"], - )), - } - } - - fn visit_map(self, map: A) -> Result - where - A: serde::de::MapAccess<'de>, - { - #[derive(Deserialize)] - #[serde(rename_all = "camelCase")] - struct ConfigHelper { - runtime: Option, - pragma: Option, - pragma_frag: Option, - import_source: Option, - } +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct RuntimeRaw { + #[serde(default)] + runtime: Option, + #[serde(default)] + pragma: Option, + #[serde(default)] + pragma_frag: Option, + #[serde(default)] + import_source: Option, +} - let helper: ConfigHelper = - Deserialize::deserialize(serde::de::value::MapAccessDeserializer::new(map))?; - - match helper.runtime.as_deref() { - Some("automatic") => { - let config = AutomaticConfig { - import_source: helper - .import_source - .unwrap_or_else(default_import_source), - }; - Ok(Runtime::Automatic(config)) - } - Some("classic") => { - let config = ClassicConfig { - pragma: helper.pragma.unwrap_or_else(default_pragma), - pragma_frag: helper.pragma_frag.unwrap_or_else(default_pragma_frag), - }; - Ok(Runtime::Classic(config)) - } - Some("preserve") => Ok(Runtime::Preserve), - Some(other) => Err(Error::unknown_variant( - other, - &["automatic", "classic", "preserve"], - )), - None => match (helper.pragma, helper.pragma_frag, helper.import_source) { - (pragma @ Some(..), pragma_frag, None) - | (pragma, pragma_frag @ Some(..), None) => { - Ok(Runtime::Classic(ClassicConfig { - pragma: pragma.unwrap_or_else(default_pragma), - pragma_frag: pragma_frag.unwrap_or_else(default_pragma_frag), - })) - } - (_, _, import_source) => Ok(Runtime::Automatic(AutomaticConfig { - import_source: import_source.unwrap_or_else(default_import_source), - })), - }, +impl TryFrom for Runtime { + type Error = String; + + fn try_from(raw: RuntimeRaw) -> Result { + match raw.runtime.as_deref() { + Some("automatic") => Ok(Runtime::Automatic(AutomaticConfig { + import_source: raw.import_source.unwrap_or_else(default_import_source), + })), + Some("classic") => Ok(Runtime::Classic(ClassicConfig { + pragma: raw.pragma.unwrap_or_else(default_pragma), + pragma_frag: raw.pragma_frag.unwrap_or_else(default_pragma_frag), + })), + Some("preserve") => Ok(Runtime::Preserve), + Some(other) => Err(format!( + "unknown runtime variant `{other}`, expected one of `automatic`, `classic`, \ + `preserve`" + )), + None => match (raw.pragma, raw.pragma_frag, raw.import_source) { + (pragma @ Some(..), pragma_frag, None) | (pragma, pragma_frag @ Some(..), None) => { + Ok(Runtime::Classic(ClassicConfig { + pragma: pragma.unwrap_or_else(default_pragma), + pragma_frag: pragma_frag.unwrap_or_else(default_pragma_frag), + })) } - } + (_, _, import_source) => Ok(Runtime::Automatic(AutomaticConfig { + import_source: import_source.unwrap_or_else(default_import_source), + })), + }, } - - deserializer.deserialize_any(RuntimeVisitor) } } #[derive(Debug, Clone, Serialize, Deserialize, Default, Merge)] +#[serde(try_from = "OptionsRaw")] pub struct Options { #[serde(flatten)] pub runtime: Runtime, @@ -198,6 +155,75 @@ pub struct Options { pub refresh: Option, } +impl TryFrom<&str> for Options { + type Error = String; + + fn try_from(s: &str) -> Result { + match s { + "react" => Ok(Options { + runtime: Runtime::Classic(ClassicConfig::default()), + common: CommonConfig::default(), + refresh: None, + }), + "react-jsx" => Ok(Options { + runtime: Runtime::Automatic(AutomaticConfig::default()), + common: CommonConfig::default(), + refresh: None, + }), + "react-jsxdev" => Ok(Options { + runtime: Runtime::Automatic(AutomaticConfig::default()), + common: CommonConfig { + development: true.into(), + ..CommonConfig::default() + }, + refresh: None, + }), + "preserve" | "react-native" => Ok(Options { + runtime: Runtime::Preserve, + common: CommonConfig::default(), + refresh: None, + }), + other => Err(format!( + "unknown preset `{other}`, expected one of `react`, `react-jsx`, `react-jsxdev`, \ + `preserve`, `react-native`" + )), + } + } +} + +#[derive(Deserialize)] +#[serde(untagged)] +enum OptionsRaw { + Preset(String), + Object { + #[serde(flatten)] + runtime: Runtime, + #[serde(flatten)] + common: CommonConfig, + #[serde(default, deserialize_with = "deserialize_refresh")] + refresh: Option, + }, +} + +impl TryFrom for Options { + type Error = String; + + fn try_from(raw: OptionsRaw) -> Result { + match raw { + OptionsRaw::Preset(preset) => preset.as_str().try_into(), + OptionsRaw::Object { + runtime, + common, + refresh, + } => Ok(Options { + runtime, + common, + refresh, + }), + } + } +} + #[cfg(feature = "concurrent")] macro_rules! static_str { ($s:expr) => {{ diff --git a/packages/types/index.ts b/packages/types/index.ts index 2d52c4aba5c1..a056a3e274b2 100644 --- a/packages/types/index.ts +++ b/packages/types/index.ts @@ -836,14 +836,16 @@ export interface EsParserConfig { explicitResourceManagement?: boolean; } +type JSXPreset = "react" | "react-jsx" | "react-jsxdev" | "preserve" | "react-native"; + /** * Options for transform. */ export interface TransformConfig { /** - * Effective only if `syntax` supports ƒ. + * Effective only if `syntax` supports. */ - react?: ReactConfig; + react?: JSXPreset | ReactConfig; constModules?: ConstModulesConfig; @@ -946,7 +948,7 @@ export interface ReactConfig { /** * jsx runtime */ - runtime?: "automatic" | "classic"; + runtime?: "automatic" | "classic" | "preserve"; /** * Declares the module specifier to be used for importing the `jsx` and `jsxs` factory functions when using `runtime` 'automatic'