Repository: swc-project/swc (rust)

Issue:
Static factory function in decorated class uses un-decorated ctor (part 2)
### Describe the bug

This is mostly the same as https://github.com/swc-project/swc/issues/4899 but the repro case is slightly more complex:

### Input code

```typescript
function es5ClassCompat(target: Function): any {
	///@ts-expect-error
	function _() { return Reflect.construct(target, arguments, this.constructor); }
	Object.defineProperty(_, 'name', Object.getOwnPropertyDescriptor(target, 'name')!);
	Object.setPrototypeOf(_, target);
	Object.setPrototypeOf(_.prototype, target.prototype);
	return _;
}
@es5ClassCompat
export class FileSystemError extends Error {

	static FileExists(messageOrUri?: string | URI): FileSystemError {
		return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.FileExists, FileSystemError.FileExists);
	}
	static FileNotFound(messageOrUri?: string | URI): FileSystemError {
		return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.FileNotFound, FileSystemError.FileNotFound);
	}
	static FileNotADirectory(messageOrUri?: string | URI): FileSystemError {
		return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.FileNotADirectory, FileSystemError.FileNotADirectory);
	}
	static FileIsADirectory(messageOrUri?: string | URI): FileSystemError {
		return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.FileIsADirectory, FileSystemError.FileIsADirectory);
	}
	static NoPermissions(messageOrUri?: string | URI): FileSystemError {
		return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.NoPermissions, FileSystemError.NoPermissions);
	}
	static Unavailable(messageOrUri?: string | URI): FileSystemError {
		return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.Unavailable, FileSystemError.Unavailable);
	}

	readonly code: string;

	constructor(uriOrMessage?: string | URI, code: FileSystemProviderErrorCode = FileSystemProviderErrorCode.Unknown, terminator?: Function) {
		super(URI.isUri(uriOrMessage) ? uriOrMessage.toString(true) : uriOrMessage);

		this.code = terminator?.name ?? 'Unknown';

		// mark the error as file system provider error so that
		// we can extract the error code on the receiving side
		markAsFileSystemProviderError(this, code);

		// workaround when extending builtin objects and when compiling to ES5, see:
		// https://github.com/microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
		if (typeof (<any>Object).setPrototypeOf === 'function') {
			(<any>Object).setPrototypeOf(this, FileSystemError.prototype);
		}

		if (typeof Error.captureStackTrace === 'function' && typeof terminator === 'function') {
			// nice stack traces
			Error.captureStackTrace(this, terminator);
		}
	}
}
```


### Config

```json
{
  "$schema": "http://json.schemastore.org/swcrc",
  "exclude": "\\.js$",
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": false,
      "decorators": true
    },
    "target": "es2020",
    "loose": false,
    "minify": {
      "compress": false,
      "mangle": false
    }
  },
  "module": {
    "type": "amd",
    "noInterop": true
  },
  "minify": false
}
```


### Playground link

https://play.swc.rs/?version=1.2.218&code=H4sIAAAAAAAAA82WS2%2FbMAzHz%2FGn4DCgsQE7PvWSrEu7PoAethRNey4Uh0m02JIhyk2DLt99lB%2BNnT527IAEhs0%2FyR9pSbS3KFRipVaAdHyeCqJzneXC%2BlaYJdohXNX2YAhCbeHZ68VxfGopwqccExuhMdp4vZcwD34Az2DQFkbBLS5SFg0SrciaImnChsCXIkNlKQS7krRXaBOMYOf1JrPfznOOC6nwxugcjd36DyH0lciwH0It4GiTjWoEF0iJkTlHeclU6YMvweglKKFlB6vtNsfJwgWtxB9IBnlz14j3T5xbXfDDyNt5p91WetwpbSwk7hFcyRSnW7KYXbrOAT5ZVHOC6u7Z83pkhZVJKbx8kmTJz5BILHFi7o0cD4EbJdUS%2FsD97TW%2FlsOI%2FIoaHIWbQ3MnWNiycrmPco6mVJ3rOQ72BOFhlJbNVb%2FrUP%2FS9koXav6Z3A3D2%2BSN9U32swtp0C3E7ScXsAd5t4q95HUp1%2FR%2FVNLmeLuQtqJbxy99gyaTRHy0fNo%2B6EC8rqBj7uLfK%2FEoZCpmKX4WfAvhNXrLWIG7g0zMtUq3kLB3gzliQ%2BuE9gsjJ%2BZnxXBQS1g7fsAEJ%2F8gXiu9UXzOurYqwQnHrTlUtoYKPu59TjeQxC3oAAUwhvb9wOppCegzPluHHWvgauvVM6hka6UduNkB4zH0a6h%2BqY5jyIRZ8%2BRCKOcfCIIFlwRU1gR5XVRtJc1SHgSl5wYhEcqd%2B0YkthWjTM8T1D3hzYDy0TWVOA47unxn9E7bfIdfNT5oADfarIVxhxxsVqjqOeMizgqZWqlAl5OOeKzXkoTHlUydxGq4nB6HQIjDKtzK2pyGcbyUdlXMuFVZnMnEaNILG9%2FxEJyWczfayLWMZ6mexZlgThP%2F4AW15pjR%2BUqoJdIgm399YYlKlkgqilK5xupjIhLGiG3EWFEmcv5vI6WjVLO3iVxZTCQX4LvRq%2Fn6jb9LvldjOziY23BycgL95tukX62d3kcedSsPN0pn1pf7pI1QaRKR837FqRXJ%2Bo7fLR5kh6MjqB32S%2BxtQm64khyAXCxwCwXJPX8nUQ29j1pT8m%2F3F2lAVgzhCQAA&config=H4sIAAAAAAAAA0WOTQ6DQAhG70JcNmpceoPewc1kxL84wwQw1RjvXqzR7sjj%2Bx7skIkfMDioYVBNdVFMQjG%2FoCgx5sR9IR%2FPHl6Aq5%2BXFi3dNPkkmaFJPNQ7JMeCfE6yRXWrRXRLKJ7HpBZTMdS5WfAFLXpiZ3KBWnnBw9aOe1QroVRlVVphJhJ8KmGMY7edek8hMYr8Vy728508zBWoXU6w%2Fz4wpwutCSO9oyJTeo7e0qv5BTnH4YgMAQAA

### Expected behavior

All ctor invocation must go through the decorator first

### Actual behavior

_No response_

### Version

1.2.218

### Additional context

_No response_

Pull request:
fix(es/decorators): Use decorated consturctors
**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):**

  - Closes #5258

Patch:
diff --git a/crates/swc_ecma_transforms_proposal/src/decorators/legacy/mod.rs b/crates/swc_ecma_transforms_proposal/src/decorators/legacy/mod.rs
index ea78b1094a..e22f44d3c3 100644
--- a/crates/swc_ecma_transforms_proposal/src/decorators/legacy/mod.rs
+++ b/crates/swc_ecma_transforms_proposal/src/decorators/legacy/mod.rs
@@ -413,11 +413,26 @@ impl VisitMut for TscDecorator {
                         decl.visit_mut_with(self);
 
                         if convert_to_let {
+                            let inner_ident = private_ident!(decl.ident.sym.clone());
+
+                            decl.class.body.iter_mut().for_each(|m| match m {
+                                ClassMember::PrivateProp(PrivateProp {
+                                    is_static: true, ..
+                                })
+                                | ClassMember::StaticBlock(..)
+                                | ClassMember::ClassProp(ClassProp {
+                                    is_static: true, ..
+                                }) => {
+                                    replace_ident(m, decl.ident.to_id(), &inner_ident);
+                                }
+                                _ => {}
+                            });
+
                             let d = VarDeclarator {
                                 span: DUMMY_SP,
                                 name: decl.ident.clone().into(),
                                 init: Some(Box::new(Expr::Class(ClassExpr {
-                                    ident: Some(decl.ident.clone()),
+                                    ident: Some(inner_ident),
                                     class: decl.class.take(),
                                 }))),
                                 definite: Default::default(),
@@ -449,10 +464,28 @@ impl VisitMut for TscDecorator {
                     if convert_to_let {
                         let ident = decl.ident.clone().unwrap();
 
+                        let inner_ident = private_ident!(ident.sym.clone());
+
+                        decl.class.body.iter_mut().for_each(|m| match m {
+                            ClassMember::PrivateProp(PrivateProp {
+                                is_static: true, ..
+                            })
+                            | ClassMember::StaticBlock(..)
+                            | ClassMember::ClassProp(ClassProp {
+                                is_static: true, ..
+                            }) => {
+                                replace_ident(m, ident.to_id(), &inner_ident);
+                            }
+                            _ => {}
+                        });
+
                         let d = VarDeclarator {
                             span: DUMMY_SP,
                             name: ident.clone().into(),
-                            init: Some(Box::new(Expr::Class(decl.take()))),
+                            init: Some(Box::new(Expr::Class(ClassExpr {
+                                ident: Some(inner_ident),
+                                ..decl.take()
+                            }))),
                             definite: Default::default(),
                         };
                         *module_item = ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {


Test patch:
diff --git a/crates/swc/tests/fixture/issues-3xxx/3686/1/output/index.ts b/crates/swc/tests/fixture/issues-3xxx/3686/1/output/index.ts
index 06d807e4b6..25a9dd7e1a 100644
--- a/crates/swc/tests/fixture/issues-3xxx/3686/1/output/index.ts
+++ b/crates/swc/tests/fixture/issues-3xxx/3686/1/output/index.ts
@@ -9,7 +9,7 @@ Object.defineProperty(exports, "ServiceError", {
 const _tsDecorate = require("@swc/helpers/lib/_ts_decorate.js").default;
 const CD = ()=>{};
 const PD = ()=>{};
-let ServiceError = class ServiceError extends Error {
+let ServiceError = class ServiceError1 extends Error {
     constructor(...args){
         super(...args);
         this.code = ServiceError.Code.badResponse;
diff --git a/crates/swc/tests/fixture/issues-5xxx/5258/input/.swcrc b/crates/swc/tests/fixture/issues-5xxx/5258/input/.swcrc
new file mode 100644
index 0000000000..258cabeb3f
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-5xxx/5258/input/.swcrc
@@ -0,0 +1,22 @@
+{
+    "$schema": "http://json.schemastore.org/swcrc",
+    "exclude": "\\.js$",
+    "jsc": {
+        "parser": {
+            "syntax": "typescript",
+            "tsx": false,
+            "decorators": true
+        },
+        "target": "es2020",
+        "loose": false,
+        "minify": {
+            "compress": false,
+            "mangle": false
+        }
+    },
+    "module": {
+        "type": "amd",
+        "noInterop": true
+    },
+    "minify": false
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-5xxx/5258/input/index.ts b/crates/swc/tests/fixture/issues-5xxx/5258/input/index.ts
new file mode 100644
index 0000000000..b9e60a7165
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-5xxx/5258/input/index.ts
@@ -0,0 +1,53 @@
+function es5ClassCompat(target: Function): any {
+	///@ts-expect-error
+	function _() { return Reflect.construct(target, arguments, this.constructor); }
+	Object.defineProperty(_, 'name', Object.getOwnPropertyDescriptor(target, 'name')!);
+	Object.setPrototypeOf(_, target);
+	Object.setPrototypeOf(_.prototype, target.prototype);
+	return _;
+}
+@es5ClassCompat
+export class FileSystemError extends Error {
+
+	static FileExists(messageOrUri?: string | URI): FileSystemError {
+		return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.FileExists, FileSystemError.FileExists);
+	}
+	static FileNotFound(messageOrUri?: string | URI): FileSystemError {
+		return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.FileNotFound, FileSystemError.FileNotFound);
+	}
+	static FileNotADirectory(messageOrUri?: string | URI): FileSystemError {
+		return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.FileNotADirectory, FileSystemError.FileNotADirectory);
+	}
+	static FileIsADirectory(messageOrUri?: string | URI): FileSystemError {
+		return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.FileIsADirectory, FileSystemError.FileIsADirectory);
+	}
+	static NoPermissions(messageOrUri?: string | URI): FileSystemError {
+		return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.NoPermissions, FileSystemError.NoPermissions);
+	}
+	static Unavailable(messageOrUri?: string | URI): FileSystemError {
+		return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.Unavailable, FileSystemError.Unavailable);
+	}
+
+	readonly code: string;
+
+	constructor(uriOrMessage?: string | URI, code: FileSystemProviderErrorCode = FileSystemProviderErrorCode.Unknown, terminator?: Function) {
+		super(URI.isUri(uriOrMessage) ? uriOrMessage.toString(true) : uriOrMessage);
+
+		this.code = terminator?.name ?? 'Unknown';
+
+		// mark the error as file system provider error so that
+		// we can extract the error code on the receiving side
+		markAsFileSystemProviderError(this, code);
+
+		// workaround when extending builtin objects and when compiling to ES5, see:
+		// https://github.com/microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
+		if (typeof (<any>Object).setPrototypeOf === 'function') {
+			(<any>Object).setPrototypeOf(this, FileSystemError.prototype);
+		}
+
+		if (typeof Error.captureStackTrace === 'function' && typeof terminator === 'function') {
+			// nice stack traces
+			Error.captureStackTrace(this, terminator);
+		}
+	}
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-5xxx/5258/output/index.ts b/crates/swc/tests/fixture/issues-5xxx/5258/output/index.ts
new file mode 100644
index 0000000000..cf9c1f390b
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-5xxx/5258/output/index.ts
@@ -0,0 +1,55 @@
+define([
+    "require",
+    "exports",
+    "@swc/helpers/src/_ts_decorate.mjs"
+], function(require, exports, _tsDecorate) {
+    "use strict";
+    Object.defineProperty(exports, "FileSystemError", {
+        enumerable: true,
+        get: ()=>FileSystemError
+    });
+    _tsDecorate = _tsDecorate.default;
+    function es5ClassCompat(target) {
+        function _() {
+            return Reflect.construct(target, arguments, this.constructor);
+        }
+        Object.defineProperty(_, 'name', Object.getOwnPropertyDescriptor(target, 'name'));
+        Object.setPrototypeOf(_, target);
+        Object.setPrototypeOf(_.prototype, target.prototype);
+        return _;
+    }
+    let FileSystemError = class FileSystemError1 extends Error {
+        static FileExists(messageOrUri) {
+            return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.FileExists, FileSystemError.FileExists);
+        }
+        static FileNotFound(messageOrUri) {
+            return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.FileNotFound, FileSystemError.FileNotFound);
+        }
+        static FileNotADirectory(messageOrUri) {
+            return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.FileNotADirectory, FileSystemError.FileNotADirectory);
+        }
+        static FileIsADirectory(messageOrUri) {
+            return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.FileIsADirectory, FileSystemError.FileIsADirectory);
+        }
+        static NoPermissions(messageOrUri) {
+            return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.NoPermissions, FileSystemError.NoPermissions);
+        }
+        static Unavailable(messageOrUri) {
+            return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.Unavailable, FileSystemError.Unavailable);
+        }
+        constructor(uriOrMessage, code = FileSystemProviderErrorCode.Unknown, terminator){
+            super(URI.isUri(uriOrMessage) ? uriOrMessage.toString(true) : uriOrMessage);
+            this.code = terminator?.name ?? 'Unknown';
+            markAsFileSystemProviderError(this, code);
+            if (typeof Object.setPrototypeOf === 'function') {
+                Object.setPrototypeOf(this, FileSystemError.prototype);
+            }
+            if (typeof Error.captureStackTrace === 'function' && typeof terminator === 'function') {
+                Error.captureStackTrace(this, terminator);
+            }
+        }
+    };
+    FileSystemError = _tsDecorate([
+        es5ClassCompat
+    ], FileSystemError);
+});