fix(parser): reject export as namespace inside a namespace body (TS1316) (#22846)

`export as namespace X` declares a UMD global export, which may only appear at the
top level of a module or a `.d.ts` file. Inside an internal `namespace`/`module`
body it is invalid:

    namespace N { export as namespace N; }          // now TS1316
    module M { export as namespace M; }             // now TS1316
    declare namespace N { export as namespace N; }  // now TS1316

    export as namespace Lib;                         // top level → still valid

This extends the namespace-body validation (`check_namespace_body_statement`) with
a `TSNamespaceExportDeclaration` arm emitting TS1316 ("Global module exports may
only appear at top level."), matching tsc and babel. The statement is already
checked inline as each direct child of the namespace body is parsed, so there is
no extra pass. External module declarations (`declare module "x" {}`) are left
unrestricted, consistent with the existing namespace-body checks.

parser_babel +1 negative-pass; positive/AST stay 100%.
This commit is contained in:
Boshen committed 2026-05-30 04:29:50 +00:00
1 parent 2eafea6681
commit 4f9afc56bd
3 files changed
+17 -3

No files matched your search

+5
View File
@@ -303,6 +303,11 @@ pub fn default_export_in_namespace(span: Span) -> OxcDiagnostic {
.with_label(span)
}
#[cold]
pub fn global_export_in_namespace(span: Span) -> OxcDiagnostic {
ts_error("1316", "Global module exports may only appear at top level.").with_label(span)
}
#[cold]
pub fn async_function_declaration(span: Span) -> OxcDiagnostic {
OxcDiagnostic::error("Async functions can only be declared at the top level or inside a block")
+3
View File
@@ -364,6 +364,9 @@ impl<'a, C: Config> ParserImpl<'a, C> {
Statement::ExportAllDeclaration(decl) => {
self.error(diagnostics::export_in_namespace(decl.span));
}
Statement::TSNamespaceExportDeclaration(decl) => {
self.error(diagnostics::global_export_in_namespace(decl.span));
}
// ES `import "..."` / `import ... from "..."`.
Statement::ImportDeclaration(decl) => {
self.error(diagnostics::import_in_namespace(decl.span));
+9 -3
View File
@@ -3,7 +3,7 @@ commit: d9fe348c
parser_babel Summary:
AST Parsed : 2236/2236 (100.00%)
Positive Passed: 2222/2236 (99.37%)
Negative Passed: 1705/1723 (98.96%)
Negative Passed: 1706/1723 (99.01%)
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/cast/unparenthesized-assert-and-assign/input.ts
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/cast/unparenthesized-type-assertion-and-assign/input.ts
@@ -28,8 +28,6 @@ Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/ty
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/module-namespace/invalid-body-export-named/input.ts
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/module-namespace/invalid-body-namespace-export/input.ts
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/module-namespace/invalid-global-redeclare-block-level-variable-in-module/input.ts
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/module-namespace/module-identifier-invalid/input.ts
@@ -14177,6 +14175,14 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
7 │ }
╰────
× TS(1316): Global module exports may only appear at top level.
╭─[babel/packages/babel-parser/test/fixtures/typescript/module-namespace/invalid-body-namespace-export/input.ts:2:3]
1 │ namespace N {
2 │ export as namespace N;
· ──────────────────────
3 │ }
╰────
× TS(2670): Augmentations for the global scope should have 'declare' modifier unless they appear in already ambient context.
╭─[babel/packages/babel-parser/test/fixtures/typescript/module-namespace/invalid-global-redeclare-block-level-variable/input.ts:3:1]
2 │