Repository: babel/babel (TypeScript)

Issue:
[Bug]: @babel/preset-typescript compile ts file error
### 💻

- [ ] Would you like to work on a fix?

### How are you using Babel?

babel-loader (webpack)

### Input code

```js
export namespace ui.answer.box {
	export class BoxDialogUI {
	
	}
   
	export class RewardDialogUI {
	}
}
export namespace ui.answer.common {
	export class ActOpenDialogUI {
	
	}
}
```

### Configuration file name

_No response_

### Configuration

file name: TestUI.ts

### Current and expected behavior

babel --presets @babel/preset-typescript TestUI.ts

TypeError: /Users/zhangbaokun/Documents/game-subpackage-test/src/app/TestUI.ts: Property body of TSModuleDeclaration expected node to be of a type ["TSModuleBlock","TSModuleDeclaration"] but instead got "BlockStatement"


### Environment

  System:
    OS: macOS 13.4.1
  Binaries:
    Node: 14.19.3 - ~/.nvm/versions/node/v14.19.3/bin/node
    Yarn: 1.15.2 - /usr/local/bin/yarn
    npm: 6.14.17 - ~/.nvm/versions/node/v14.19.3/bin/npm
  npmPackages:
    @babel/cli: ^7.22.9 => 7.22.9 
    @babel/core: ^7.22.9 => 7.22.9 
    @babel/plugin-syntax-dynamic-import: ^7.8.3 => 7.8.3 
    @babel/plugin-transform-modules-commonjs: ^7.21.5 => 7.21.5 
    @babel/plugin-transform-modules-umd: ^7.22.5 => 7.22.5 
    @babel/plugin-transform-runtime: ^7.21.4 => 7.21.4 
    @babel/preset-env: ^7.21.5 => 7.21.5 
    @babel/preset-react: ^7.22.5 => 7.22.5 
    @babel/preset-typescript: ^7.22.5 => 7.22.5 
    @babel/runtime: ^7.21.5 => 7.21.5 
    babel-loader: ^9.1.2 => 9.1.2 
    webpack: ^5.82.1 => 5.82.1 

### Possible solution

_No response_

### Additional context

_No response_


Hints:
Hey @Cola-Zhang! We really appreciate you taking the time to report an issue. The collaborators on this project attempt to help as many people as possible, but we're a limited number of volunteers, so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant [Slack community](https://babeljs.slack.com) that typically always has someone willing to help. You can sign-up [here](https://slack.babeljs.io/) for an invite.
Minimal example:
```js
export namespace ui.a {
}
export namespace ui.b {
}
```
// same error
export module ui.a {
}
export module ui.b {
}

Patch:
diff --git a/packages/babel-plugin-transform-typescript/src/index.ts b/packages/babel-plugin-transform-typescript/src/index.ts
--- a/packages/babel-plugin-transform-typescript/src/index.ts
+++ b/packages/babel-plugin-transform-typescript/src/index.ts
@@ -460,6 +460,29 @@ export default declare((api, opts: Options) => {
           return;
         }
 
+        // Convert `export namespace X {}` into `export let X; namespace X {}`,
+        // so that when visiting TSModuleDeclaration we do not have to possibly
+        // replace its parent path.
+        if (t.isTSModuleDeclaration(path.node.declaration)) {
+          const namespace = path.node.declaration;
+          const { id } = namespace;
+          if (t.isIdentifier(id)) {
+            if (path.scope.hasOwnBinding(id.name)) {
+              path.replaceWith(namespace);
+            } else {
+              const [newExport] = path.replaceWithMultiple([
+                t.exportNamedDeclaration(
+                  t.variableDeclaration("let", [
+                    t.variableDeclarator(t.cloneNode(id)),
+                  ]),
+                ),
+                namespace,
+              ]);
+              path.scope.registerDeclaration(newExport);
+            }
+          }
+        }
+
         NEEDS_EXPLICIT_ESM.set(state.file.ast.program, false);
       },
 
diff --git a/packages/babel-plugin-transform-typescript/src/namespace.ts b/packages/babel-plugin-transform-typescript/src/namespace.ts
--- a/packages/babel-plugin-transform-typescript/src/namespace.ts
+++ b/packages/babel-plugin-transform-typescript/src/namespace.ts
@@ -23,16 +23,7 @@ export default function transpileNamespace(
 
   const name = path.node.id.name;
   const value = handleNested(path, t.cloneNode(path.node, true));
-  const bound = path.scope.hasOwnBinding(name);
-  if (path.parent.type === "ExportNamedDeclaration") {
-    if (!bound) {
-      path.parentPath.insertAfter(value);
-      path.replaceWith(getDeclaration(name));
-      path.scope.registerDeclaration(path.parentPath);
-    } else {
-      path.parentPath.replaceWith(value);
-    }
-  } else if (bound) {
+  if (path.scope.hasOwnBinding(name)) {
     path.replaceWith(value);
   } else {
     path.scope.registerDeclaration(


Test patch:
diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/nested-shorthand-export/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/nested-shorthand-export/input.ts
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/nested-shorthand-export/input.ts
@@ -0,0 +1,6 @@
+export namespace ui.a {
+  export const x = 1;
+}
+export namespace ui.b {
+  export const y = 2;
+}
diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/nested-shorthand-export/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/nested-shorthand-export/output.mjs
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/nested-shorthand-export/output.mjs
@@ -0,0 +1,13 @@
+export let ui;
+(function (_ui2) {
+  let a;
+  (function (_a) {
+    const x = _a.x = 1;
+  })(a || (a = _ui2.a || (_ui2.a = {})));
+})(ui || (ui = {}));
+(function (_ui) {
+  let b;
+  (function (_b) {
+    const y = _b.y = 2;
+  })(b || (b = _ui.b || (_ui.b = {})));
+})(ui || (ui = {}));