Repository: swc-project/swc (rust)

Issue:
Enum member init expressions should qualify member name references in certain cases
### Describe the bug

Enums that reference themselves should be qualified to `EnumName.MemberName` in certain cases.

I've opened #3000 to resolve the `c` property here not being correct.

### Input code

```typescript
var x = 4;
enum Foo {
    a,
    b = a,
    c = b + 1,
    d = 1 + c * x,
    e = 2 * d,
    f = Foo.e * 10
}
```


### Config

```json
{
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": false
    },
    "target": "es2021",
    "loose": false,
    "minify": {
      "compress": false,
      "mangle": false
    }
  },
  "module": {
    "type": "es6"
  },
  "minify": false,
  "isModule": true
}
```


### Playground link

https://play.swc.rs/?version=1.2.118&code=H4sIAAAAAAAAAytLLFKoULBVMLHmSs0rzVVwy89XqOZSAIJEHTCVBJSEMpOBzCQFbQVDCDcFyDUEcpMVtBQqIEKpQCEjIDcFwk0DcoEG6qUChQwNuGoBOfh4rG0AAAA%3D&config=H4sIAAAAAAAAA0WNSw6DMAxE7%2BI1i5ZFF9yhh4hSg4Lyk8dIjVDu3gSlYmfPPD%2BftMPSclI2ApY%2BoUQ1X1pIS2ZYcVlpIkWLVuPBtS1GNtaGMObH%2FGy1Twk8gImCi24tXWZTyMLAXZm4%2BT9Zmyukz9GD8%2Fp3OV9Ub8e4c3gPUOXg%2BgMnAdpduAAAAA%3D%3D

### Expected behavior

For members like:

```ts
 d = 1 + c * x,
```

It should qualify to:

```ts
 d = 1 + Foo.c * x,
```

### Version

1.2.118

### Additional context

See TypeScript's output here:

https://www.typescriptlang.org/play?#code/G4QwTgBAHhC8EBYDcAoApgOwK4FsIDEB7QiAbxQkohABoKqAjOauqiAY2aYGoIBGVlQAmzPhF6cAVNEGU0zAEwRpQ2RABmzIoQB086XwAMKAL5A

Pull request:
fix(es/typescript): Correct TypeScript Enum Reference
<!-- Thanks for submitting a pull request! Please provide enough information so that others can review your pull request. -->

**Description:**

<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->

**BREAKING CHANGE:**
<!--
If this PR introduces a breaking change, it must contain a notice for it to be included in the CHANGELOG. Add description or remove entirely if not breaking.

You may need to update `MIGRATION.md` for the breaking changes.
-->

**Related issue (if exists):**
- fix #3001 

Patch:
diff --git a/crates/swc_ecma_transforms_typescript/src/strip.rs b/crates/swc_ecma_transforms_typescript/src/strip.rs
index 96c8789649..1057dd8ec4 100644
--- a/crates/swc_ecma_transforms_typescript/src/strip.rs
+++ b/crates/swc_ecma_transforms_typescript/src/strip.rs
@@ -9,16 +9,19 @@ use swc_common::{
     Mark, SourceMap, Span, Spanned, SyntaxContext, DUMMY_SP,
 };
 use swc_ecma_ast::*;
+use swc_ecma_transforms_base::ext::ExprRefExt;
 use swc_ecma_transforms_react::{parse_expr_for_jsx, JsxDirectives};
 use swc_ecma_utils::{
     constructor::inject_after_super, default_constructor, ident::IdentLike, member_expr, prepend,
     private_ident, prop_name_to_expr, quote_ident, replace_ident, var::VarCollector, ExprFactory,
     Id, ModuleItemLike, StmtLike,
 };
-use swc_ecma_visit::{as_folder, Fold, Visit, VisitMut, VisitMutWith, VisitWith};
+use swc_ecma_visit::{
+    as_folder, noop_visit_mut_type, Fold, Visit, VisitMut, VisitMutWith, VisitWith,
+};
 
 /// Value does not contain TsLit::Bool
-type EnumValues = AHashMap<JsWord, TsLit>;
+type EnumValues = AHashMap<JsWord, Option<TsLit>>;
 
 #[derive(Debug, Serialize, Deserialize)]
 #[non_exhaustive]
@@ -846,7 +849,7 @@ where
                     }
 
                     Expr::Ident(ref id) => {
-                        if let Some(v) = values.get(&id.sym) {
+                        if let Some(Some(v)) = values.get(&id.sym) {
                             return Ok(v.clone());
                         }
                         //
@@ -963,7 +966,7 @@ where
                             TsEnumMemberId::Ident(i) => i.sym.clone(),
                             TsEnumMemberId::Str(s) => s.value.clone(),
                         },
-                        val.clone(),
+                        Some(val.clone()),
                     );
 
                     match val {
@@ -978,7 +981,24 @@ where
                 })
                 .or_else(|err| match &m.init {
                     None => Err(err),
-                    Some(v) => Ok(*v.clone()),
+                    Some(v) => {
+                        let mut v = *v.clone();
+                        let mut visitor = EnumValuesVisitor {
+                            previous: &values,
+                            ident: &id,
+                        };
+                        visitor.visit_mut_expr(&mut v);
+
+                        values.insert(
+                            match &m.id {
+                                TsEnumMemberId::Ident(i) => i.sym.clone(),
+                                TsEnumMemberId::Str(s) => s.value.clone(),
+                            },
+                            None,
+                        );
+
+                        Ok(v)
+                    }
                 })?;
 
                 Ok((m, val))
@@ -2723,3 +2743,34 @@ fn create_prop_pat(obj: &Ident, pat: Pat) -> Pat {
         Pat::Expr(..) => pat,
     }
 }
+
+struct EnumValuesVisitor<'a> {
+    ident: &'a Ident,
+    previous: &'a EnumValues,
+}
+impl VisitMut for EnumValuesVisitor<'_> {
+    noop_visit_mut_type!();
+
+    fn visit_mut_expr(&mut self, expr: &mut Expr) {
+        match expr {
+            Expr::Ident(ident) if self.previous.contains_key(&ident.sym) => {
+                *expr = self.ident.clone().make_member(ident.clone());
+            }
+            Expr::Member(MemberExpr {
+                obj: ExprOrSuper::Expr(ref obj),
+                // prop,
+                ..
+            }) if obj
+                .as_ident()
+                .and_then(|obj| self.previous.get(&obj.sym))
+                .is_some() =>
+            {
+                *expr = self.ident.clone().make_member(expr.clone());
+            }
+            Expr::Member(..) => {
+                // skip
+            }
+            _ => expr.visit_mut_children_with(self),
+        }
+    }
+}


Test patch:
diff --git a/crates/swc_ecma_transforms_typescript/tests/fixture/issue-3001/1/input.ts b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-3001/1/input.ts
new file mode 100644
index 0000000000..9770e14fb5
--- /dev/null
+++ b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-3001/1/input.ts
@@ -0,0 +1,9 @@
+var x = 4;
+enum Foo {
+    a,
+    b = a,
+    c = b + 1,
+    d = 1 + c * x,
+    e = 2 * d,
+    f = Foo.e * 10,
+}
diff --git a/crates/swc_ecma_transforms_typescript/tests/fixture/issue-3001/1/output.js b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-3001/1/output.js
new file mode 100644
index 0000000000..c2feb8a605
--- /dev/null
+++ b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-3001/1/output.js
@@ -0,0 +1,11 @@
+var x = 4;
+var Foo;
+(function(Foo) {
+    Foo[Foo["a"] = 0] = "a";
+    Foo[Foo["b"] = 0] = "b";
+    Foo[Foo["c"] = 1] = "c";
+    Foo[Foo["d"] = 1 + Foo.c * x] = "d";
+    Foo[Foo["e"] = 2 * Foo.d] = "e";
+    Foo[Foo["f"] = Foo.e * 10] = "f";
+})(Foo || (Foo = {
+}));
diff --git a/crates/swc_ecma_transforms_typescript/tests/fixture/issue-3001/2/input.ts b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-3001/2/input.ts
new file mode 100644
index 0000000000..7728e8ff69
--- /dev/null
+++ b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-3001/2/input.ts
@@ -0,0 +1,22 @@
+var x = 10;
+
+enum Foo {
+    a = 10,
+    b = a,
+    c = b + x,
+    d = c,
+}
+
+enum Bar {
+    a = 1,
+    b = Foo.a,
+    E = b,
+    F = Math.E,
+}
+
+enum Baz {
+    a = 0,
+    b = 1,
+    // @ts-ignore
+    x = a.toString(),
+}
diff --git a/crates/swc_ecma_transforms_typescript/tests/fixture/issue-3001/2/output.js b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-3001/2/output.js
new file mode 100644
index 0000000000..ae4458f6b6
--- /dev/null
+++ b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-3001/2/output.js
@@ -0,0 +1,24 @@
+var x = 10;
+var Foo;
+(function(Foo) {
+    Foo[Foo["a"] = 10] = "a";
+    Foo[Foo["b"] = 10] = "b";
+    Foo[Foo["c"] = Foo.b + x] = "c";
+    Foo[Foo["d"] = Foo.c] = "d";
+})(Foo || (Foo = {
+}));
+var Bar;
+(function(Bar) {
+    Bar[Bar["a"] = 1] = "a";
+    Bar[Bar["b"] = Foo.a] = "b";
+    Bar[Bar["E"] = Bar.b] = "E";
+    Bar[Bar["F"] = Math.E] = "F";
+})(Bar || (Bar = {
+}));
+var Baz;
+(function(Baz) {
+    Baz[Baz["a"] = 0] = "a";
+    Baz[Baz["b"] = 1] = "b";
+    Baz[Baz["x"] = Baz.a.toString()] = "x";
+})(Baz || (Baz = {
+}));