From c16a85176d58d5d980e677b483af64f58f26d2a7 Mon Sep 17 00:00:00 2001 From: Boshen <1430279+Boshen@users.noreply.github.com> Date: Tue, 17 Dec 2024 13:49:50 +0000 Subject: [PATCH] feat(napi/transform): add `jsx: 'preserve'` option (#7965) closes #7958 --- crates/oxc_transformer/src/jsx/options.rs | 37 ++++++++++++++++++----- napi/transform/index.d.ts | 2 +- napi/transform/src/transformer.rs | 15 +++++++-- napi/transform/test/transform.test.ts | 25 +++++++++++++++ 4 files changed, 69 insertions(+), 10 deletions(-) diff --git a/crates/oxc_transformer/src/jsx/options.rs b/crates/oxc_transformer/src/jsx/options.rs index a7c0bf6b0aca4..dac670dfdee8b 100644 --- a/crates/oxc_transformer/src/jsx/options.rs +++ b/crates/oxc_transformer/src/jsx/options.rs @@ -109,6 +109,20 @@ pub struct JsxOptions { impl Default for JsxOptions { fn default() -> Self { + Self::enable() + } +} + +impl JsxOptions { + pub fn conform(&mut self) { + if self.development { + self.jsx_plugin = true; + self.jsx_self_plugin = true; + self.jsx_source_plugin = true; + } + } + + pub fn enable() -> Self { Self { jsx_plugin: true, display_name_plugin: true, @@ -126,14 +140,23 @@ impl Default for JsxOptions { refresh: None, } } -} -impl JsxOptions { - pub fn conform(&mut self) { - if self.development { - self.jsx_plugin = true; - self.jsx_self_plugin = true; - self.jsx_source_plugin = true; + pub fn disable() -> Self { + Self { + jsx_plugin: false, + display_name_plugin: false, + jsx_self_plugin: false, + jsx_source_plugin: false, + runtime: JsxRuntime::default(), + development: false, + throw_if_namespace: false, + pure: false, + import_source: None, + pragma: None, + pragma_frag: None, + use_built_ins: None, + use_spread: None, + refresh: None, } } } diff --git a/napi/transform/index.d.ts b/napi/transform/index.d.ts index a40af6a822fcf..492ba4810bc1a 100644 --- a/napi/transform/index.d.ts +++ b/napi/transform/index.d.ts @@ -259,7 +259,7 @@ export interface TransformOptions { /** Configure how TypeScript is transformed. */ typescript?: TypeScriptOptions /** Configure how TSX and JSX are transformed. */ - jsx?: JsxOptions + jsx?: 'preserve' | JsxOptions /** * Sets the target environment for the generated JavaScript. * diff --git a/napi/transform/src/transformer.rs b/napi/transform/src/transformer.rs index 1ce05e297a6fe..800914f37905e 100644 --- a/napi/transform/src/transformer.rs +++ b/napi/transform/src/transformer.rs @@ -106,7 +106,8 @@ pub struct TransformOptions { pub typescript: Option, /// Configure how TSX and JSX are transformed. - pub jsx: Option, + #[napi(ts_type = "'preserve' | JsxOptions")] + pub jsx: Option>, /// Sets the target environment for the generated JavaScript. /// @@ -150,7 +151,17 @@ impl TryFrom for oxc::transformer::TransformOptions { .typescript .map(oxc::transformer::TypeScriptOptions::from) .unwrap_or_default(), - jsx: options.jsx.map(Into::into).unwrap_or_default(), + jsx: match options.jsx { + Some(Either::A(s)) => { + if s == "preserve" { + oxc::transformer::JsxOptions::disable() + } else { + return Err(format!("Invalid jsx option: `{s}`.")); + } + } + Some(Either::B(options)) => oxc::transformer::JsxOptions::from(options), + None => oxc::transformer::JsxOptions::enable(), + }, env, helper_loader: options .helpers diff --git a/napi/transform/test/transform.test.ts b/napi/transform/test/transform.test.ts index f1acbfe78aab1..a271a27e4cfd5 100644 --- a/napi/transform/test/transform.test.ts +++ b/napi/transform/test/transform.test.ts @@ -140,6 +140,31 @@ import bar = require('bar') }); }); +describe('jsx', () => { + const code = `const foo: Foo =
`; + + it('enables jsx transform by default', () => { + const ret = transform('test.tsx', code); + expect(ret.code).toEqual('import { jsx as _jsx } from "react/jsx-runtime";\nconst foo = _jsx("div", {});\n'); + }); + + it('configures jsx', () => { + const ret = transform('test.tsx', code, { + jsx: { + importSource: 'xxx', + }, + }); + expect(ret.code).toEqual('import { jsx as _jsx } from "xxx/jsx-runtime";\nconst foo = _jsx("div", {});\n'); + }); + + it('can preserve jsx transform', () => { + const ret = transform('test.tsx', code, { + jsx: 'preserve', + }); + expect(ret.code).toEqual('const foo =
;\n'); + }); +}); + describe('react refresh plugin', () => { const code = `import { useState } from "react"; export const App = () => {