Repository: parcel-bundler/parcel (js)

Issue:
`package.json#types`: Duplicate `export default`
# 🐛 bug report

The output for `package.json#types` seems to include the export declarations of child modules as well.

cc @devongovett

## 🤔 Expected Behavior

dist/index.d.ts:
```ts
export default function (): string;
```

## 😯 Current Behavior

dist/index.d.ts:
```ts
export default _default;
export default function (): string;
```

## 💁 Solution

Fix the bug in `@parcel/transformer-typescript-types`


## 💻 Code Sample

```json
{
	"source": "src/index.ts",
	"module": "dist/index.js",
	"types": "dist/index.d.ts",
	"dependencies": {
		"parcel": "^2.0.0-nightly.266"
	},
	"devDependencies": {
		"typescript": "^3.9.2"
	}
}
```
```js
// src/index.ts
import v from "./value";
export default function() {
	return v;
};

// src/value.ts
export default "foo";
```


## 🌍 Your Environment

| Software         | Version(s) |
| ---------------- | ---------- |
| Parcel           | 3f128ae3

Pull request:
Fix export assignments in ts types transformer
Fixes #4611. TypeScript has a separate AST node type for the `export default expression` form, which we weren't handling. In addition, there were some errors with the way we handled re-exporting default.

Patch:
diff --git a/packages/transformers/typescript-types/src/TSModuleGraph.js b/packages/transformers/typescript-types/src/TSModuleGraph.js
index d7c8e928a..83ea15f58 100644
--- a/packages/transformers/typescript-types/src/TSModuleGraph.js
+++ b/packages/transformers/typescript-types/src/TSModuleGraph.js
@@ -89,7 +89,11 @@ export class TSModuleGraph {
         return null;
       }
 
-      return {module: exp.module, imported: exp.name, name: exportName};
+      return {
+        module: exp.module,
+        imported: exp.imported || exp.name,
+        name: exportName,
+      };
     }
 
     // Import and then export
diff --git a/packages/transformers/typescript-types/src/collect.js b/packages/transformers/typescript-types/src/collect.js
index a26856ceb..4de26c263 100644
--- a/packages/transformers/typescript-types/src/collect.js
+++ b/packages/transformers/typescript-types/src/collect.js
@@ -74,6 +74,11 @@ export function collect(
       }
     }
 
+    // Handle `export default name;`
+    if (ts.isExportAssignment(node) && ts.isIdentifier(node.expression)) {
+      currentModule.addExport('default', node.expression.text);
+    }
+
     if (isDeclaration(ts, node)) {
       if (node.name) {
         currentModule.addLocal(node.name.text, node);
diff --git a/packages/transformers/typescript-types/src/shake.js b/packages/transformers/typescript-types/src/shake.js
index 95b8e8026..7b69751a3 100644
--- a/packages/transformers/typescript-types/src/shake.js
+++ b/packages/transformers/typescript-types/src/shake.js
@@ -81,6 +81,14 @@ export function shake(
       }
     }
 
+    // Remove export assignment if unused.
+    if (ts.isExportAssignment(node)) {
+      let name = currentModule.getName('default');
+      if (exportedNames.get(name) !== currentModule) {
+        return null;
+      }
+    }
+
     if (isDeclaration(ts, node)) {
       let name = getExportedName(ts, node) || node.name.text;
 
@@ -130,7 +138,9 @@ export function shake(
 
       // Remove original export modifiers
       node.modifiers = (node.modifiers || []).filter(
-        m => m.kind !== ts.SyntaxKind.ExportKeyword,
+        m =>
+          m.kind !== ts.SyntaxKind.ExportKeyword &&
+          m.kind !== ts.SyntaxKind.DeclareKeyword,
       );
 
       // Add export modifier if all declarations are exported.
@@ -139,6 +149,9 @@ export function shake(
       );
       if (isExported) {
         node.modifiers.unshift(ts.createModifier(ts.SyntaxKind.ExportKeyword));
+      } else {
+        // Otherwise, add `declare` modifier (required for top-level declarations in d.ts files).
+        node.modifiers.unshift(ts.createModifier(ts.SyntaxKind.DeclareKeyword));
       }
 
       return node;
@@ -153,7 +166,10 @@ export function shake(
 
     // Rename references
     if (ts.isIdentifier(node) && currentModule.names.has(node.text)) {
-      return ts.createIdentifier(nullthrows(currentModule.getName(node.text)));
+      let newName = nullthrows(currentModule.getName(node.text));
+      if (newName !== 'default') {
+        return ts.createIdentifier(newName);
+      }
     }
 
     // Replace namespace references with final names


Test patch:
diff --git a/packages/core/integration-tests/test/integration/ts-types/exporting/expected.d.ts b/packages/core/integration-tests/test/integration/ts-types/exporting/expected.d.ts
index cf3fe2e69..9a9555275 100644
--- a/packages/core/integration-tests/test/integration/ts-types/exporting/expected.d.ts
+++ b/packages/core/integration-tests/test/integration/ts-types/exporting/expected.d.ts
@@ -11,5 +11,7 @@ export interface Params {
 }
 export var a: number, b: number;
 export function toUpperCase(foo: string): string;
+declare const _default: "test";
+export default _default;
 
 //# sourceMappingURL=types.d.ts.map
diff --git a/packages/core/integration-tests/test/integration/ts-types/exporting/index.ts b/packages/core/integration-tests/test/integration/ts-types/exporting/index.ts
index 1d2796793..fdaa58462 100644
--- a/packages/core/integration-tests/test/integration/ts-types/exporting/index.ts
+++ b/packages/core/integration-tests/test/integration/ts-types/exporting/index.ts
@@ -1,3 +1,3 @@
 export {createMessage} from './message';
 export * from './other';
-export {test as toUpperCase} from './test';
+export {test as toUpperCase, default} from './test';
diff --git a/packages/core/integration-tests/test/integration/ts-types/exporting/message.ts b/packages/core/integration-tests/test/integration/ts-types/exporting/message.ts
index 22a36119c..ed2ab140a 100644
--- a/packages/core/integration-tests/test/integration/ts-types/exporting/message.ts
+++ b/packages/core/integration-tests/test/integration/ts-types/exporting/message.ts
@@ -8,3 +8,5 @@ export class Message {
 export function createMessage(msg: string) {
   return new Message(msg);
 }
+
+export default 'unused';
diff --git a/packages/core/integration-tests/test/integration/ts-types/exporting/test.ts b/packages/core/integration-tests/test/integration/ts-types/exporting/test.ts
index 0371743b6..372ca75f5 100644
--- a/packages/core/integration-tests/test/integration/ts-types/exporting/test.ts
+++ b/packages/core/integration-tests/test/integration/ts-types/exporting/test.ts
@@ -1,3 +1,5 @@
 export function test(foo: string) {
   return foo.toUpperCase();
 }
+
+export default 'test';