Skip to content

Commit a39a573

Browse files
fix(api-extractor): preserve export keyword for namespace re-exports (#5517)
* fix(api-extractor): preserve export keyword for namespace re-exports When a namespace contains `export { Foo, Bar };` re-export declarations, api-extractor was incorrectly stripping the `export` keyword, producing `{ Foo, Bar, };` which is syntactically invalid TypeScript (TS1109). This fix checks if an ExportKeyword is part of an ExportDeclaration that is inside a ModuleBlock (namespace body), and if so, preserves it instead of stripping it. Fixes #5516 * chore: add rush change file * chore: update test snapshots for namespace export fix * DRY-er
1 parent 7d992f5 commit a39a573

File tree

10 files changed

+54
-8
lines changed

10 files changed

+54
-8
lines changed

apps/api-extractor/src/generators/ApiReportGenerator.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,14 @@ export class ApiReportGenerator {
302302
break;
303303

304304
case ts.SyntaxKind.ExportKeyword:
305+
if (DtsEmitHelpers.isExportKeywordInNamespaceExportDeclaration(span.node)) {
306+
// This is an export declaration inside a namespace - preserve the export keyword
307+
break;
308+
}
309+
// Otherwise, delete the export keyword -- we will re-add it below
310+
span.modification.skipAll();
311+
break;
312+
305313
case ts.SyntaxKind.DefaultKeyword:
306314
case ts.SyntaxKind.DeclareKeyword:
307315
// Delete any explicit "export" or "declare" keywords -- we will re-add them below

apps/api-extractor/src/generators/DtsEmitHelpers.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { Collector } from '../collector/Collector';
1212
import type { Span } from '../analyzer/Span';
1313
import type { IndentedWriter } from './IndentedWriter';
1414
import { SourceFileLocationFormatter } from '../analyzer/SourceFileLocationFormatter';
15+
import { TypeScriptHelpers } from '../analyzer/TypeScriptHelpers';
1516

1617
/**
1718
* Some common code shared between DtsRollupGenerator and ApiReportGenerator.
@@ -171,4 +172,22 @@ export class DtsEmitHelpers {
171172
}
172173
}
173174
}
175+
176+
/**
177+
* Checks if an export keyword is part of an ExportDeclaration inside a namespace
178+
* (e.g., "export { Foo, Bar };" inside "declare namespace SDK { ... }").
179+
* In that case, the export keyword must be preserved, otherwise the output is invalid TypeScript.
180+
*/
181+
public static isExportKeywordInNamespaceExportDeclaration(node: ts.Node): boolean {
182+
if (node.parent && ts.isExportDeclaration(node.parent)) {
183+
const moduleBlock: ts.ModuleBlock | undefined = TypeScriptHelpers.findFirstParent(
184+
node,
185+
ts.SyntaxKind.ModuleBlock
186+
);
187+
if (moduleBlock) {
188+
return true;
189+
}
190+
}
191+
return false;
192+
}
174193
}

apps/api-extractor/src/generators/DtsRollupGenerator.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,14 @@ export class DtsRollupGenerator {
270270
break;
271271

272272
case ts.SyntaxKind.ExportKeyword:
273+
if (DtsEmitHelpers.isExportKeywordInNamespaceExportDeclaration(span.node)) {
274+
// This is an export declaration inside a namespace - preserve the export keyword
275+
break;
276+
}
277+
// Otherwise, delete the export keyword -- we will re-add it below
278+
span.modification.skipAll();
279+
break;
280+
273281
case ts.SyntaxKind.DefaultKeyword:
274282
case ts.SyntaxKind.DeclareKeyword:
275283
// Delete any explicit "export" or "declare" keywords -- we will re-add them below

build-tests/api-extractor-scenarios/etc/apiItemKinds/api-extractor-scenarios.api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export namespace n1 {
5151
// (undocumented)
5252
export class SomeClass2 extends SomeClass1 {
5353
}
54-
{};
54+
export {};
5555
}
5656

5757
// @public (undocumented)

build-tests/api-extractor-scenarios/etc/apiItemKinds/rollup.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export declare namespace n1 {
3636
export class SomeClass3 {
3737
}
3838
}
39-
{};
39+
export {};
4040
}
4141

4242
/** @public */

build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export namespace SomeNamespace1 {
8686
}
8787
// (undocumented)
8888
export function someFunction3(): ForgottenExport3;
89-
{};
89+
export {};
9090
}
9191

9292
// (No @packageDocumentation comment for this package)

build-tests/api-extractor-scenarios/etc/includeForgottenExports/rollup.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export declare namespace SomeNamespace1 {
8080
export class ForgottenExport3 {
8181
}
8282
export function someFunction3(): ForgottenExport3;
83-
{};
83+
export {};
8484
}
8585

8686
export { }

build-tests/api-extractor-scenarios/etc/referenceTokens/api-extractor-scenarios.api.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ export namespace n1 {
2121
export function someFunction2(): SomeType2;
2222
// (undocumented)
2323
export type SomeType2 = number;
24-
{};
24+
export {};
2525
}
2626
// (undocumented)
2727
export function someFunction1(): SomeType1;
2828
// (undocumented)
2929
export type SomeType1 = number;
30-
{};
30+
export {};
3131
}
3232

3333
// @public (undocumented)

build-tests/api-extractor-scenarios/etc/referenceTokens/rollup.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ export declare namespace n1 {
1414
export type SomeType3 = number;
1515
export function someFunction3(): n2.n3.SomeType3;
1616
}
17-
{};
17+
export {};
1818
}
19-
{};
19+
export {};
2020
}
2121

2222
/** @public */
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"comment": "Fix missing 'export' keyword for namespace re-exports that produced invalid TypeScript output",
5+
"type": "patch",
6+
"packageName": "@microsoft/api-extractor"
7+
}
8+
],
9+
"packageName": "@microsoft/api-extractor",
10+
"email": "[email protected]"
11+
}

0 commit comments

Comments
 (0)