diff --git a/apps/api-extractor/src/generators/ApiReportGenerator.ts b/apps/api-extractor/src/generators/ApiReportGenerator.ts index e59d7603ba..4fe95cf9b6 100644 --- a/apps/api-extractor/src/generators/ApiReportGenerator.ts +++ b/apps/api-extractor/src/generators/ApiReportGenerator.ts @@ -302,6 +302,23 @@ export class ApiReportGenerator { break; case ts.SyntaxKind.ExportKeyword: + // Check if this export keyword is part of an ExportDeclaration inside a namespace + // (e.g., "export { Foo, Bar };" inside "declare namespace SDK { ... }") + // In that case, we must preserve the export keyword, otherwise the output is invalid TypeScript. + if (span.node.parent && ts.isExportDeclaration(span.node.parent)) { + const moduleBlock: ts.ModuleBlock | undefined = TypeScriptHelpers.findFirstParent( + span.node, + ts.SyntaxKind.ModuleBlock + ); + if (moduleBlock) { + // This is an export declaration inside a namespace - preserve the export keyword + break; + } + } + // Otherwise, delete the export keyword -- we will re-add it below + span.modification.skipAll(); + break; + case ts.SyntaxKind.DefaultKeyword: case ts.SyntaxKind.DeclareKeyword: // Delete any explicit "export" or "declare" keywords -- we will re-add them below diff --git a/apps/api-extractor/src/generators/DtsRollupGenerator.ts b/apps/api-extractor/src/generators/DtsRollupGenerator.ts index baa933c944..7216dd2b1b 100644 --- a/apps/api-extractor/src/generators/DtsRollupGenerator.ts +++ b/apps/api-extractor/src/generators/DtsRollupGenerator.ts @@ -270,6 +270,23 @@ export class DtsRollupGenerator { break; case ts.SyntaxKind.ExportKeyword: + // Check if this export keyword is part of an ExportDeclaration inside a namespace + // (e.g., "export { Foo, Bar };" inside "declare namespace SDK { ... }") + // In that case, we must preserve the export keyword, otherwise the output is invalid TypeScript. + if (span.node.parent && ts.isExportDeclaration(span.node.parent)) { + const moduleBlock: ts.ModuleBlock | undefined = TypeScriptHelpers.findFirstParent( + span.node, + ts.SyntaxKind.ModuleBlock + ); + if (moduleBlock) { + // This is an export declaration inside a namespace - preserve the export keyword + break; + } + } + // Otherwise, delete the export keyword -- we will re-add it below + span.modification.skipAll(); + break; + case ts.SyntaxKind.DefaultKeyword: case ts.SyntaxKind.DeclareKeyword: // Delete any explicit "export" or "declare" keywords -- we will re-add them below diff --git a/build-tests/api-extractor-scenarios/etc/apiItemKinds/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/apiItemKinds/api-extractor-scenarios.api.md index c0743f1c37..ccc12604dd 100644 --- a/build-tests/api-extractor-scenarios/etc/apiItemKinds/api-extractor-scenarios.api.md +++ b/build-tests/api-extractor-scenarios/etc/apiItemKinds/api-extractor-scenarios.api.md @@ -51,7 +51,7 @@ export namespace n1 { // (undocumented) export class SomeClass2 extends SomeClass1 { } - {}; + export {}; } // @public (undocumented) diff --git a/build-tests/api-extractor-scenarios/etc/apiItemKinds/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/apiItemKinds/rollup.d.ts index dbe1c3219c..b69a119ae1 100644 --- a/build-tests/api-extractor-scenarios/etc/apiItemKinds/rollup.d.ts +++ b/build-tests/api-extractor-scenarios/etc/apiItemKinds/rollup.d.ts @@ -36,7 +36,7 @@ export declare namespace n1 { export class SomeClass3 { } } - {}; + export {}; } /** @public */ diff --git a/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.md index 43157fe55b..483dafd638 100644 --- a/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.md +++ b/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.md @@ -86,7 +86,7 @@ export namespace SomeNamespace1 { } // (undocumented) export function someFunction3(): ForgottenExport3; - {}; + export {}; } // (No @packageDocumentation comment for this package) diff --git a/build-tests/api-extractor-scenarios/etc/includeForgottenExports/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/includeForgottenExports/rollup.d.ts index 8010e49962..4d56859ed8 100644 --- a/build-tests/api-extractor-scenarios/etc/includeForgottenExports/rollup.d.ts +++ b/build-tests/api-extractor-scenarios/etc/includeForgottenExports/rollup.d.ts @@ -80,7 +80,7 @@ export declare namespace SomeNamespace1 { export class ForgottenExport3 { } export function someFunction3(): ForgottenExport3; - {}; + export {}; } export { } diff --git a/build-tests/api-extractor-scenarios/etc/referenceTokens/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/referenceTokens/api-extractor-scenarios.api.md index 065b8df0b8..3194032710 100644 --- a/build-tests/api-extractor-scenarios/etc/referenceTokens/api-extractor-scenarios.api.md +++ b/build-tests/api-extractor-scenarios/etc/referenceTokens/api-extractor-scenarios.api.md @@ -21,13 +21,13 @@ export namespace n1 { export function someFunction2(): SomeType2; // (undocumented) export type SomeType2 = number; - {}; + export {}; } // (undocumented) export function someFunction1(): SomeType1; // (undocumented) export type SomeType1 = number; - {}; + export {}; } // @public (undocumented) diff --git a/build-tests/api-extractor-scenarios/etc/referenceTokens/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/referenceTokens/rollup.d.ts index d1e6004270..c27d5c3ee9 100644 --- a/build-tests/api-extractor-scenarios/etc/referenceTokens/rollup.d.ts +++ b/build-tests/api-extractor-scenarios/etc/referenceTokens/rollup.d.ts @@ -14,9 +14,9 @@ export declare namespace n1 { export type SomeType3 = number; export function someFunction3(): n2.n3.SomeType3; } - {}; + export {}; } - {}; + export {}; } /** @public */ diff --git a/common/changes/@microsoft/api-extractor/fix-namespace-export-keyword_2025-12-24-17-25.json b/common/changes/@microsoft/api-extractor/fix-namespace-export-keyword_2025-12-24-17-25.json new file mode 100644 index 0000000000..99ddbbd79b --- /dev/null +++ b/common/changes/@microsoft/api-extractor/fix-namespace-export-keyword_2025-12-24-17-25.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "Fix missing 'export' keyword for namespace re-exports that produced invalid TypeScript output", + "type": "patch", + "packageName": "@microsoft/api-extractor" + } + ], + "packageName": "@microsoft/api-extractor", + "email": "chris@matterport.com" +}