diff --git a/crates/swc/tests/fixture/issues-8xxx/8594/input/.swcrc b/crates/swc/tests/fixture/issues-8xxx/8594/input/.swcrc
new file mode 100644
index 000000000000..63a04f741e14
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8594/input/.swcrc
@@ -0,0 +1,19 @@
+{
+ "jsc": {
+ "parser": {
+ "syntax": "typescript",
+ "tsx": true
+ },
+ "target": "es2015",
+ "loose": false,
+ "minify": {
+ "compress": false,
+ "mangle": false
+ }
+ },
+ "module": {
+ "type": "es6"
+ },
+ "minify": false,
+ "isModule": true
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-8xxx/8594/input/index.tsx b/crates/swc/tests/fixture/issues-8xxx/8594/input/index.tsx
new file mode 100644
index 000000000000..29299e8f061b
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8594/input/index.tsx
@@ -0,0 +1,6 @@
+import * as React from 'react';
+
+export namespace FooNs {
+ export const Shared = () => 'I\'m shared component';
+ export const Main = () => ;
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-8xxx/8594/output/index.tsx b/crates/swc/tests/fixture/issues-8xxx/8594/output/index.tsx
new file mode 100644
index 000000000000..1f804f6d5982
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8594/output/index.tsx
@@ -0,0 +1,6 @@
+import * as React from 'react';
+export var FooNs;
+(function(FooNs) {
+ FooNs.Shared = ()=>'I\'m shared component';
+ FooNs.Main = ()=>React.createElement(FooNs.Shared, null);
+})(FooNs || (FooNs = {}));
diff --git a/crates/swc_ecma_transforms_module/src/module_ref_rewriter.rs b/crates/swc_ecma_transforms_module/src/module_ref_rewriter.rs
index 0e3c659d589e..f4c01babe675 100644
--- a/crates/swc_ecma_transforms_module/src/module_ref_rewriter.rs
+++ b/crates/swc_ecma_transforms_module/src/module_ref_rewriter.rs
@@ -71,6 +71,11 @@ impl QueryRef for ImportQuery {
None
}
+ fn query_jsx(&self, _: &Ident) -> Option {
+ // We do not need to handle JSX since there is no jsx preserve option in swc
+ None
+ }
+
fn should_fix_this(&self, ident: &Ident) -> bool {
if self.helper_ctxt.iter().any(|ctxt| ctxt == &ident.span.ctxt) {
return false;
diff --git a/crates/swc_ecma_transforms_typescript/src/transform.rs b/crates/swc_ecma_transforms_typescript/src/transform.rs
index c6b4e643e6cf..7381191b1f63 100644
--- a/crates/swc_ecma_transforms_typescript/src/transform.rs
+++ b/crates/swc_ecma_transforms_typescript/src/transform.rs
@@ -1231,6 +1231,16 @@ impl QueryRef for ExportQuery {
self.query_ref(ident)
}
+ fn query_jsx(&self, ident: &Ident) -> Option {
+ self.export_id_list.contains(&ident.to_id()).then(|| {
+ JSXMemberExpr {
+ obj: JSXObject::Ident(self.namesapce_id.clone().into()),
+ prop: ident.clone(),
+ }
+ .into()
+ })
+ }
+
fn should_fix_this(&self, _: &Ident) -> bool {
// tsc does not care about `this` in namespace.
false
diff --git a/crates/swc_ecma_utils/src/lib.rs b/crates/swc_ecma_utils/src/lib.rs
index 287268f6bdea..4f2cace4c418 100644
--- a/crates/swc_ecma_utils/src/lib.rs
+++ b/crates/swc_ecma_utils/src/lib.rs
@@ -3021,11 +3021,21 @@ impl VisitMut for IdentRenamer<'_> {
}
pub trait QueryRef {
- fn query_ref(&self, ident: &Ident) -> Option;
- fn query_lhs(&self, ident: &Ident) -> Option;
+ fn query_ref(&self, _ident: &Ident) -> Option {
+ None
+ }
+ fn query_lhs(&self, _ident: &Ident) -> Option {
+ None
+ }
+ /// ref used in JSX
+ fn query_jsx(&self, _ident: &Ident) -> Option {
+ None
+ }
/// when `foo()` is replaced with `bar.baz()`,
/// should `bar.baz` be indirect call?
- fn should_fix_this(&self, ident: &Ident) -> bool;
+ fn should_fix_this(&self, _ident: &Ident) -> bool {
+ false
+ }
}
/// Replace `foo` with `bar` or `bar.baz`
@@ -3129,6 +3139,16 @@ where
*n = n.take().into_indirect()
}
}
+
+ fn visit_mut_jsx_element_name(&mut self, n: &mut JSXElementName) {
+ n.visit_mut_children_with(self);
+
+ if let JSXElementName::Ident(ident) = n {
+ if let Some(expr) = self.query.query_jsx(ident) {
+ *n = expr;
+ }
+ }
+ }
}
#[cfg(test)]