Repository: swc-project/swc (rust)

Issue:
Setting a getter with Object.defineProperty not working within decorator
**Describe the bug**
If I set a getter for a property with `Object.defineProperty` within a decorator it returns `undefined` instead of the variable

**Input code**
```typescript
/* works */
test("without decorator", () => {
    const returnValue = "asdasd"
    class TestClass {
        public testProperty: Date;
    }

    Object.defineProperty(TestClass.prototype, "testProperty", {
        get: function () { return returnValue },
        enumerable: true,
        configurable: true,
    });
    const instance = new TestClass()
    expect(instance.testProperty).toBe(returnValue)
})

/* fails */
test("with decorator", () => {
    const returnValue = "asdasd"
    class TestClass2 {
        @deco public testProperty: Date;
    }
    function deco(target: any, key: string) {
        Object.defineProperty(target.constructor.prototype, key, {
            get: function () { return returnValue },
            enumerable: true,
            configurable: true,
        });
    }
    const instance = new TestClass2()
    expect(instance.testProperty).toBe(returnValue)
})

/* works */
test("with decorator without getter", () => {
    const returnValue = "asdasd"
    class TestClass2 {
        @deco public testProperty: Date;
    }
    function deco(target: any, key: string) {
        Object.defineProperty(target.constructor.prototype, key, {
            value: returnValue,
            enumerable: true,
            configurable: true,
        });
    }
    const instance = new TestClass2()
    expect(instance.testProperty).toBe(returnValue)
})
```

**Config**

```typescript
/* jest.config.js*/
module.exports = {
    testEnvironment: 'node',
    transform: {
        // "^.+\\.tsx?$": "ts-jest"
		// "^.+\\.(tsx?)$": ["@swc-node/jest", {
		// 	dynamicImport: true,
		// 	emitDecoratorMetadata: true,
		// 	esModuleInterop: true,
		// 	experimentalDecorators: true,
        //     keepClassNames: true,
		// 	// module: "commonjs",
		// 	// target: "es5"
		// }]
		"^.+\\.(tsx?)$": ["@swc/jest", {
            "jsc": {
                "parser": {
                    "syntax": "typescript",
                    "decorators": true,
                    "dynamicImport": false
                },
                "transform": {
                    "legacyDecorator": true,
                    "decoratorMetadata": true
                },
                "target": "es2016",
                "loose": false,
                "externalHelpers": false,
                // Requires v1.2.50 or upper and requires target to be es2016 or upper.
                "keepClassNames": true
            }
        }]
       // "^.+\\.tsx?$": "esbuild-runner/jest"
       // "^.+\\.tsx?$": "esbuild-jest"
    }
};

```

**Expected behavior**
All tests should not fail

`expect(instance.testProperty).toBe(returnValue)` should return `true`

it works, when changing the `transformer` to `ts-jest` or `esbuild-runner/jest` or `esbuild-jest`

**Version**
```json
{
    "@swc-node/jest": "^1.3.1",
    "@swc/core": "^1.2.80",
    "@swc/jest": "^0.2.2"
}
```

**Additional context**
The same problem applies to `@swc-node/jest`. 

This issue most likely prevents users from using the known library `typescript-ioc`: https://github.com/thiagobustamante/typescript-ioc

https://github.com/thiagobustamante/typescript-ioc/issues/68
https://github.com/thiagobustamante/typescript-ioc/issues/77




Pull request:
fix(es/helpers): Fix decorators
swc_ecma_transforms_base:
 - Fix `_applyDecoratedDescriptor`. (Closes #2127)

Patch:
diff --git a/crates/swc_ecma_transforms_base/src/helpers/_apply_decorated_descriptor.js b/crates/swc_ecma_transforms_base/src/helpers/_apply_decorated_descriptor.js
index 9633189117..f513511c39 100644
--- a/crates/swc_ecma_transforms_base/src/helpers/_apply_decorated_descriptor.js
+++ b/crates/swc_ecma_transforms_base/src/helpers/_apply_decorated_descriptor.js
@@ -16,6 +16,14 @@ function _applyDecoratedDescriptor(target, property, decorators, descriptor, con
     desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
     desc.initializer = undefined;
   }
+
+  var own = Object.getOwnPropertyDescriptor(target, property);
+  if (own && (own.get || own.set)) {
+    // Prevent overriding
+    delete desc.writable;
+    delete desc.initializer;
+  }
+
   if (desc.initializer === void 0) {
     Object.defineProperty(target, property, desc);
     desc = null;


Test patch:
diff --git a/crates/swc/tests/fixture/issue-1160/output/entry.ts b/crates/swc/tests/fixture/issue-1160/output/entry.ts
index 133bc1d089..3852ac816f 100644
--- a/crates/swc/tests/fixture/issue-1160/output/entry.ts
+++ b/crates/swc/tests/fixture/issue-1160/output/entry.ts
@@ -17,6 +17,11 @@ function _applyDecoratedDescriptor(target, property, decorators, descriptor, con
         desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
         desc.initializer = undefined;
     }
+    var own = Object.getOwnPropertyDescriptor(target, property);
+    if (own && (own.get || own.set)) {
+        delete desc.writable;
+        delete desc.initializer;
+    }
     if (desc.initializer === void 0) {
         Object.defineProperty(target, property, desc);
         desc = null;
diff --git a/crates/swc/tests/fixture/issue-1278/output/index.ts b/crates/swc/tests/fixture/issue-1278/output/index.ts
index 72fb046a91..f46fdfe073 100644
--- a/crates/swc/tests/fixture/issue-1278/output/index.ts
+++ b/crates/swc/tests/fixture/issue-1278/output/index.ts
@@ -17,6 +17,11 @@ function _applyDecoratedDescriptor(target, property, decorators, descriptor, con
         desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
         desc.initializer = undefined;
     }
+    var own = Object.getOwnPropertyDescriptor(target, property);
+    if (own && (own.get || own.set)) {
+        delete desc.writable;
+        delete desc.initializer;
+    }
     if (desc.initializer === void 0) {
         Object.defineProperty(target, property, desc);
         desc = null;
diff --git a/crates/swc/tests/fixture/issue-1345/output/index.ts b/crates/swc/tests/fixture/issue-1345/output/index.ts
index 7b2aafe9b1..5210b29581 100644
--- a/crates/swc/tests/fixture/issue-1345/output/index.ts
+++ b/crates/swc/tests/fixture/issue-1345/output/index.ts
@@ -16,6 +16,11 @@ function _applyDecoratedDescriptor(target, property, decorators, descriptor, con
         desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
         desc.initializer = undefined;
     }
+    var own = Object.getOwnPropertyDescriptor(target, property);
+    if (own && (own.get || own.set)) {
+        delete desc.writable;
+        delete desc.initializer;
+    }
     if (desc.initializer === void 0) {
         Object.defineProperty(target, property, desc);
         desc = null;
diff --git a/crates/swc/tests/fixture/issue-1362/case1/output/index.ts b/crates/swc/tests/fixture/issue-1362/case1/output/index.ts
index 8b74844b6d..a565626f45 100644
--- a/crates/swc/tests/fixture/issue-1362/case1/output/index.ts
+++ b/crates/swc/tests/fixture/issue-1362/case1/output/index.ts
@@ -24,6 +24,11 @@ function _applyDecoratedDescriptor(target, property, decorators, descriptor, con
         desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
         desc.initializer = undefined;
     }
+    var own = Object.getOwnPropertyDescriptor(target, property);
+    if (own && (own.get || own.set)) {
+        delete desc.writable;
+        delete desc.initializer;
+    }
     if (desc.initializer === void 0) {
         Object.defineProperty(target, property, desc);
         desc = null;
diff --git a/crates/swc/tests/fixture/issue-1421/case1/output/index.ts b/crates/swc/tests/fixture/issue-1421/case1/output/index.ts
index 97c1fdc0e1..2ce3568b60 100644
--- a/crates/swc/tests/fixture/issue-1421/case1/output/index.ts
+++ b/crates/swc/tests/fixture/issue-1421/case1/output/index.ts
@@ -18,6 +18,11 @@ function _applyDecoratedDescriptor(target, property, decorators, descriptor, con
         desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
         desc.initializer = undefined;
     }
+    var own = Object.getOwnPropertyDescriptor(target, property);
+    if (own && (own.get || own.set)) {
+        delete desc.writable;
+        delete desc.initializer;
+    }
     if (desc.initializer === void 0) {
         Object.defineProperty(target, property, desc);
         desc = null;
diff --git a/crates/swc/tests/fixture/issue-2428/1/output/index.ts b/crates/swc/tests/fixture/issue-2428/1/output/index.ts
index 937fbd69c1..d1b7971fe9 100644
--- a/crates/swc/tests/fixture/issue-2428/1/output/index.ts
+++ b/crates/swc/tests/fixture/issue-2428/1/output/index.ts
@@ -17,6 +17,11 @@ function _applyDecoratedDescriptor(target, property, decorators, descriptor, con
         desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
         desc.initializer = undefined;
     }
+    var own = Object.getOwnPropertyDescriptor(target, property);
+    if (own && (own.get || own.set)) {
+        delete desc.writable;
+        delete desc.initializer;
+    }
     if (desc.initializer === void 0) {
         Object.defineProperty(target, property, desc);
         desc = null;
diff --git a/crates/swc_ecma_transforms/tests/decorators.rs b/crates/swc_ecma_transforms/tests/decorators.rs
index f49d4e51fb..2420c96869 100644
--- a/crates/swc_ecma_transforms/tests/decorators.rs
+++ b/crates/swc_ecma_transforms/tests/decorators.rs
@@ -5,6 +5,8 @@
     feature = "swc_ecma_transforms_proposal",
 ))]
 
+use std::{fs, path::PathBuf};
+
 use swc_common::{chain, Mark};
 use swc_ecma_parser::{EsConfig, Syntax, TsConfig};
 use swc_ecma_transforms::resolver_with_mark;
@@ -6034,3 +6036,30 @@ test!(
       initializer: void 0
   }), _class);"
 );
+
+#[testing::fixture("tests/fixture/decorator/**/exec.ts")]
+fn fixture(input: PathBuf) {
+    let code = fs::read_to_string(&input).expect("failed to read file");
+
+    swc_ecma_transforms_testing::exec_tr(
+        "decorator",
+        Syntax::Typescript(TsConfig {
+            decorators: true,
+            ..Default::default()
+        }),
+        |_| {
+            let top_level_mark = Mark::fresh(Mark::root());
+
+            chain!(
+                resolver_with_mark(top_level_mark),
+                strip(top_level_mark),
+                decorators(Config {
+                    legacy: true,
+                    emit_metadata: true,
+                    ..Default::default()
+                })
+            )
+        },
+        &code,
+    );
+}
diff --git a/crates/swc_ecma_transforms/tests/fixture/decorator/issue-2127/1/exec.ts b/crates/swc_ecma_transforms/tests/fixture/decorator/issue-2127/1/exec.ts
new file mode 100644
index 0000000000..646a21b26f
--- /dev/null
+++ b/crates/swc_ecma_transforms/tests/fixture/decorator/issue-2127/1/exec.ts
@@ -0,0 +1,12 @@
+const returnValue = "asdasd"
+class TestClass {
+    public testProperty: Date;
+}
+
+Object.defineProperty(TestClass.prototype, "testProperty", {
+    get: function () { return returnValue },
+    enumerable: true,
+    configurable: true,
+});
+const instance = new TestClass()
+expect(instance.testProperty).toBe(returnValue)
\ No newline at end of file
diff --git a/crates/swc_ecma_transforms/tests/fixture/decorator/issue-2127/2/exec.ts b/crates/swc_ecma_transforms/tests/fixture/decorator/issue-2127/2/exec.ts
new file mode 100644
index 0000000000..f66a697635
--- /dev/null
+++ b/crates/swc_ecma_transforms/tests/fixture/decorator/issue-2127/2/exec.ts
@@ -0,0 +1,15 @@
+const returnValue = "asdasd"
+class TestClass2 {
+    @deco public testProperty: Date;
+}
+function deco(target: any, key: string) {
+    console.log(target, key);
+    Object.defineProperty(target.constructor.prototype, key, {
+        get: function () { return returnValue },
+        enumerable: true,
+        configurable: true,
+    });
+}
+console.log(TestClass2.prototype.testProperty)
+const instance = new TestClass2()
+expect(instance.testProperty).toBe(returnValue)
\ No newline at end of file
diff --git a/crates/swc_ecma_transforms/tests/fixture/decorator/issue-2127/3/exec.ts b/crates/swc_ecma_transforms/tests/fixture/decorator/issue-2127/3/exec.ts
new file mode 100644
index 0000000000..d34d04ad80
--- /dev/null
+++ b/crates/swc_ecma_transforms/tests/fixture/decorator/issue-2127/3/exec.ts
@@ -0,0 +1,15 @@
+const returnValue = "asdasd"
+class TestClass2 {
+    @deco public testProperty: Date;
+}
+function deco(target: any, key: string) {
+    console.log(target, key);
+    Object.defineProperty(target.constructor.prototype, key, {
+        value: returnValue,
+        enumerable: true,
+        configurable: true,
+    });
+}
+console.log(TestClass2.prototype.testProperty)
+const instance = new TestClass2()
+expect(instance.testProperty).toBe(returnValue)
\ No newline at end of file
diff --git a/crates/swc_ecma_transforms_proposal/src/decorators/legacy.rs b/crates/swc_ecma_transforms_proposal/src/decorators/legacy/mod.rs
similarity index 100%
rename from crates/swc_ecma_transforms_proposal/src/decorators/legacy.rs
rename to crates/swc_ecma_transforms_proposal/src/decorators/legacy/mod.rs
diff --git a/crates/swc_ecma_transforms_testing/src/lib.rs b/crates/swc_ecma_transforms_testing/src/lib.rs
index 01fb582040..25021664b1 100644
--- a/crates/swc_ecma_transforms_testing/src/lib.rs
+++ b/crates/swc_ecma_transforms_testing/src/lib.rs
@@ -315,8 +315,8 @@ pub fn test_transform<F, P>(
             return Ok(());
         }
 
-        println!(">>>>> Orig <<<<<\n{}", input);
-        println!(">>>>> Code <<<<<\n{}", actual_src);
+        println!(">>>>> {} <<<<<\n{}", Color::Green.paint("Orig"), input);
+        println!(">>>>> {} <<<<<\n{}", Color::Green.paint("Code"), actual_src);
         if actual_src != expected_src {
             panic!(
                 r#"assertion failed: `(left == right)`
@@ -445,8 +445,11 @@ where
         let src = tester.print(&module, &tester.comments.clone());
 
         println!(
-            "\t>>>>> Orig <<<<<\n{}\n\t>>>>> Code <<<<<\n{}",
-            input, src_without_helpers
+            "\t>>>>> {} <<<<<\n{}\n\t>>>>> {} <<<<<\n{}",
+            Color::Green.paint("Orig"),
+            input,
+            Color::Green.paint("Code"),
+            src_without_helpers
         );
 
         exec_with_node_test_runner(test_name, &src)
@@ -504,12 +507,19 @@ fn exec_with_node_test_runner(test_name: &str, src: &str) -> Result<(), ()> {
         Command::new(&test_runner_path)
     };
 
-    let status = base_cmd
+    let output = base_cmd
         .arg(&format!("{}", path.display()))
+        .arg("--color")
         .current_dir(root)
-        .status()
-        .expect("failed to run jest");
-    if status.success() {
+        .output()
+        .expect("failed to run mocha");
+
+    println!(">>>>> {} <<<<<", Color::Red.paint("Stdout"));
+    println!("{}", String::from_utf8_lossy(&output.stdout));
+    println!(">>>>> {} <<<<<", Color::Red.paint("Stderr"));
+    println!("{}", String::from_utf8_lossy(&output.stderr));
+
+    if output.status.success() {
         fs::write(&success_cache, "").unwrap();
         return Ok(());
     }