Repository: swc-project/swc (rust)

Issue:
`asciiOnly: true` results in invalid escape code inside identifier
### Describe the bug

`asciiOnly: true` results in invalid escape code inside identifier

### Input code

```typescript
const módulo = 123;
console.log(módulo);
```


### Config

```json
{
  "jsc": {
    "parser": {
      "syntax": "ecmascript",
      "jsx": false
    },
    "target": "es3",
    "loose": false,
        "minify": {
            "compress": false,
            "format": {
                "asciiOnly": true
            }
        }
  },
  "module": {
    "type": "es6"
  },
  "minify": false,
  "isModule": true
}
```


### Playground link (or link to the minimal reproduction)

https://play.swc.rs/?version=1.4.14-nightly-20240411.1&code=H4sIAAAAAAAAA0vOzysuUcg9vDmlNCdfwVbB0MjYmisZKJifk6qXk5%2BuAZXStAYATd5jRSoAAAA%3D&config=H4sIAAAAAAAAA2VQSwqDQAzde4oh6%2B4KXfQO0jMM01hG5kcSoSLevePoqMVd8j55j0yNUtCzgaea8piXpImR9j0jPAbR34wAGq%2FZkE0Ct8r2vFCddowFmlcGRNMHpbj4vsnBxchY5fVExr0NthtPoRtuok%2BEzFdLobtIXsvFVrhc1NpXcMtVoQH%2FFHNznkpl8PE9ODweIWPCtf4DDlEtutcBy211lpz5B8lC%2FqlUAQAA

### SWC Info output

_No response_

### Expected behavior

Output valid JS

### Actual behavior

Invalid JS:
```js
var m\xf3dulo = 123;
console.log(m\xf3dulo);
```

### Version

1.4.14-nightly-20240411.1

### Additional context

Coming from https://github.com/parcel-bundler/parcel/issues/9632

Pull request description:
fix(es/codegen): Fix `ascii_only` for identifiers
**Related issue:**

 - Closes #8852

Patch:
diff --git a/crates/swc_ecma_codegen/src/lib.rs b/crates/swc_ecma_codegen/src/lib.rs
index f2260dc2d7..d5bd57cbea 100644
--- a/crates/swc_ecma_codegen/src/lib.rs
+++ b/crates/swc_ecma_codegen/src/lib.rs
@@ -3871,7 +3871,12 @@ fn get_ascii_only_ident(sym: &str, may_need_quote: bool, target: EsVersion) -> C
     while let Some(c) = iter.next() {
         match c {
             '\x00' => {
-                buf.push_str("\\x00");
+                if may_need_quote {
+                    need_quote = true;
+                    let _ = write!(buf, "\\x00");
+                } else {
+                    let _ = write!(buf, "\\u0000");
+                }
             }
             '\u{0008}' => buf.push_str("\\b"),
             '\u{000c}' => buf.push_str("\\f"),
@@ -3959,10 +3964,20 @@ fn get_ascii_only_ident(sym: &str, may_need_quote: bool, target: EsVersion) -> C
                 buf.push('"');
             }
             '\x01'..='\x0f' if !first => {
-                let _ = write!(buf, "\\x0{:x}", c as u8);
+                if may_need_quote {
+                    need_quote = true;
+                    let _ = write!(buf, "\\x{:x}", c as u8);
+                } else {
+                    let _ = write!(buf, "\\u00{:x}", c as u8);
+                }
             }
             '\x10'..='\x1f' if !first => {
-                let _ = write!(buf, "\\x{:x}", c as u8);
+                if may_need_quote {
+                    need_quote = true;
+                    let _ = write!(buf, "\\x{:x}", c as u8);
+                } else {
+                    let _ = write!(buf, "\\u00{:x}", c as u8);
+                }
             }
             '\x20'..='\x7e' => {
                 buf.push(c);
@@ -3970,9 +3985,10 @@ fn get_ascii_only_ident(sym: &str, may_need_quote: bool, target: EsVersion) -> C
             '\u{7f}'..='\u{ff}' => {
                 if may_need_quote {
                     need_quote = true;
+                    let _ = write!(buf, "\\x{:x}", c as u8);
+                } else {
+                    let _ = write!(buf, "\\u00{:x}", c as u8);
                 }
-
-                let _ = write!(buf, "\\x{:x}", c as u8);
             }
             '\u{2028}' => {
                 buf.push_str("\\u2028");


Test patch:
diff --git a/crates/swc/tests/fixture/issues-8xxx/8260/output/1.js b/crates/swc/tests/fixture/issues-8xxx/8260/output/1.js
index ec1e52900f..f445e9fe77 100644
--- a/crates/swc/tests/fixture/issues-8xxx/8260/output/1.js
+++ b/crates/swc/tests/fixture/issues-8xxx/8260/output/1.js
@@ -1,2 +1,2 @@
-function p(\xb5, \u03C3) {}
+function p(\u00b5, \u03C3) {}
 console.log(p);
diff --git a/crates/swc/tests/fixture/issues-8xxx/8852/input/.swcrc b/crates/swc/tests/fixture/issues-8xxx/8852/input/.swcrc
new file mode 100644
index 0000000000..7d24715868
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8852/input/.swcrc
@@ -0,0 +1,21 @@
+{
+    "jsc": {
+        "parser": {
+            "syntax": "ecmascript",
+            "jsx": false
+        },
+        "target": "es3",
+        "loose": false,
+        "minify": {
+            "compress": false,
+            "format": {
+                "asciiOnly": true
+            }
+        }
+    },
+    "module": {
+        "type": "es6"
+    },
+    "minify": false,
+    "isModule": true
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-8xxx/8852/input/1.js b/crates/swc/tests/fixture/issues-8xxx/8852/input/1.js
new file mode 100644
index 0000000000..8b5ab0df9e
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8852/input/1.js
@@ -0,0 +1,2 @@
+const módulo = 123;
+console.log(módulo);
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-8xxx/8852/input/2.js b/crates/swc/tests/fixture/issues-8xxx/8852/input/2.js
new file mode 100644
index 0000000000..bec72a0f75
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8852/input/2.js
@@ -0,0 +1,2 @@
+const m\u00f3dulo = 123;
+console.log(m\u00f3dulo);
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-8xxx/8852/output/1.js b/crates/swc/tests/fixture/issues-8xxx/8852/output/1.js
new file mode 100644
index 0000000000..531fbe78d9
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8852/output/1.js
@@ -0,0 +1,2 @@
+var m\u00f3dulo = 123;
+console.log(m\u00f3dulo);
diff --git a/crates/swc/tests/fixture/issues-8xxx/8852/output/2.js b/crates/swc/tests/fixture/issues-8xxx/8852/output/2.js
new file mode 100644
index 0000000000..531fbe78d9
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8852/output/2.js
@@ -0,0 +1,2 @@
+var m\u00f3dulo = 123;
+console.log(m\u00f3dulo);