mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(isolated-declarations): handle export in the namespace correctly (#5950)
Previous I didn't follow the behavior of `TypeScript` to handle `export` in `namespace` as I thought no one used this
This commit is contained in:
parent
463c24e5ec
commit
4a62703d88
8 changed files with 128 additions and 13 deletions
|
|
@ -1073,13 +1073,22 @@ pub enum TSModuleDeclarationBody<'a> {
|
|||
TSModuleBlock(Box<'a, TSModuleBlock<'a>>) = 1,
|
||||
}
|
||||
|
||||
impl TSModuleDeclarationBody<'_> {
|
||||
impl<'a> TSModuleDeclarationBody<'a> {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
TSModuleDeclarationBody::TSModuleDeclaration(declaration) => declaration.body.is_none(),
|
||||
TSModuleDeclarationBody::TSModuleBlock(block) => block.body.len() == 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_module_block_mut(&mut self) -> Option<&mut TSModuleBlock<'a>> {
|
||||
match self {
|
||||
TSModuleDeclarationBody::TSModuleBlock(block) => Some(block.as_mut()),
|
||||
TSModuleDeclarationBody::TSModuleDeclaration(decl) => {
|
||||
decl.body.as_mut().and_then(|body| body.as_module_block_mut())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See serializer in serialize.rs
|
||||
|
|
|
|||
|
|
@ -204,16 +204,27 @@ impl<'a> IsolatedDeclarations<'a> {
|
|||
for mut stmt in filtered_stmts {
|
||||
match stmt {
|
||||
match_declaration!(Statement) => {
|
||||
if let Declaration::TSModuleDeclaration(decl) = stmt.to_declaration() {
|
||||
if let Statement::TSModuleDeclaration(ref mut decl) = stmt {
|
||||
if self.has_internal_annotation(decl.span) {
|
||||
continue;
|
||||
}
|
||||
// declare global { ... } or declare module "foo" { ... }
|
||||
// `declare global { ... }` or `declare module "foo" { ... }`
|
||||
// We need to emit it anyway
|
||||
if decl.kind.is_global() || decl.id.is_string_literal() {
|
||||
let is_global = decl.kind.is_global();
|
||||
if is_global || decl.id.is_string_literal() {
|
||||
transformed_spans.insert(decl.span);
|
||||
|
||||
// Remove export keyword from all statements in `declare module "xxx" { ... }`
|
||||
if !is_global {
|
||||
if let Some(body) =
|
||||
decl.body.as_mut().and_then(|body| body.as_module_block_mut())
|
||||
{
|
||||
self.strip_export_keyword(&mut body.body);
|
||||
}
|
||||
}
|
||||
|
||||
// We need to visit the module declaration to collect all references
|
||||
self.scope.visit_ts_module_declaration(decl);
|
||||
transformed_spans.insert(decl.span);
|
||||
}
|
||||
}
|
||||
if !self.has_internal_annotation(stmt.span()) {
|
||||
|
|
@ -367,6 +378,10 @@ impl<'a> IsolatedDeclarations<'a> {
|
|||
self.ast.alloc_export_named_declaration(SPAN, None, specifiers, None, kind, NONE);
|
||||
new_stmts
|
||||
.push(Statement::from(ModuleDeclaration::ExportNamedDeclaration(empty_export)));
|
||||
} else if self.scope.is_ts_module_block() {
|
||||
// If we are in a module block and we don't need to add `export {}`, in that case we need to remove `export` keyword from all ExportNamedDeclaration
|
||||
// <https://github.com/microsoft/TypeScript/blob/a709f9899c2a544b6de65a0f2623ecbbe1394eab/src/compiler/transformers/declarations.ts#L1556-L1563>
|
||||
self.strip_export_keyword(&mut new_stmts);
|
||||
}
|
||||
|
||||
new_stmts
|
||||
|
|
@ -600,6 +615,6 @@ impl<'a> IsolatedDeclarations<'a> {
|
|||
|
||||
pub fn is_declare(&self) -> bool {
|
||||
// If we are in a module block, we don't need to add declare
|
||||
!self.scope.is_ts_module_block_flag()
|
||||
!self.scope.is_ts_module_block()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use oxc_allocator::Box;
|
||||
use oxc_allocator::Vec;
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_span::{Atom, GetSpan, SPAN};
|
||||
|
|
@ -124,4 +125,25 @@ impl<'a> IsolatedDeclarations<'a> {
|
|||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Strip export keyword from ExportNamedDeclaration
|
||||
///
|
||||
/// ```ts
|
||||
/// export const a = 1;
|
||||
/// export function b() {}
|
||||
/// ```
|
||||
/// to
|
||||
/// ```ts
|
||||
/// const a = 1;
|
||||
/// function b() {}
|
||||
/// ```
|
||||
pub fn strip_export_keyword(&self, stmts: &mut Vec<'a, Statement<'a>>) {
|
||||
stmts.iter_mut().for_each(|stmt| {
|
||||
if let Statement::ExportNamedDeclaration(decl) = stmt {
|
||||
if let Some(declaration) = &mut decl.declaration {
|
||||
*stmt = Statement::from(self.ast.move_declaration(declaration));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ impl<'a> ScopeTree<'a> {
|
|||
Self { levels }
|
||||
}
|
||||
|
||||
pub fn is_ts_module_block_flag(&self) -> bool {
|
||||
pub fn is_ts_module_block(&self) -> bool {
|
||||
let scope = self.levels.last().unwrap();
|
||||
scope.flags.contains(ScopeFlags::TsModuleBlock)
|
||||
}
|
||||
|
|
|
|||
36
crates/oxc_isolated_declarations/tests/fixtures/module-declaration-with-export.ts
vendored
Normal file
36
crates/oxc_isolated_declarations/tests/fixtures/module-declaration-with-export.ts
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
export namespace OnlyOneExport {
|
||||
export const a = 0;
|
||||
}
|
||||
|
||||
|
||||
export namespace TwoExports {
|
||||
export const c = 0;
|
||||
export const a: typeof c = 0;
|
||||
}
|
||||
|
||||
|
||||
export namespace OneExportReferencedANonExported {
|
||||
const c = 0;
|
||||
export const a: typeof c = c;
|
||||
}
|
||||
|
||||
declare module "OnlyOneExport" {
|
||||
export const a = 0;
|
||||
}
|
||||
|
||||
|
||||
declare module "TwoExports" {
|
||||
export const c = 0;
|
||||
export const a: typeof c;
|
||||
}
|
||||
|
||||
|
||||
declare module "OneExportReferencedANonExported" {
|
||||
const c = 0;
|
||||
export const a: typeof c;
|
||||
}
|
||||
|
||||
declare global {
|
||||
const c = 0;
|
||||
export const a: typeof c;
|
||||
}
|
||||
|
|
@ -8,14 +8,14 @@ input_file: crates/oxc_isolated_declarations/tests/fixtures/expando-function.ts
|
|||
export declare function foo(): void;
|
||||
export declare const bar: () => void;
|
||||
export declare namespace NS {
|
||||
export const goo: () => void;
|
||||
const goo: () => void;
|
||||
}
|
||||
export declare namespace foo {
|
||||
export let baz: number;
|
||||
let baz: number;
|
||||
}
|
||||
declare function qux(): void;
|
||||
declare namespace qux {
|
||||
export let woo: number;
|
||||
let woo: number;
|
||||
}
|
||||
export default qux;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
source: crates/oxc_isolated_declarations/tests/mod.rs
|
||||
input_file: crates/oxc_isolated_declarations/tests/fixtures/module-declaration-with-export.ts
|
||||
---
|
||||
```
|
||||
==================== .D.TS ====================
|
||||
|
||||
export declare namespace OnlyOneExport {
|
||||
const a = 0;
|
||||
}
|
||||
export declare namespace TwoExports {
|
||||
const c = 0;
|
||||
const a: typeof c;
|
||||
}
|
||||
export declare namespace OneExportReferencedANonExported {
|
||||
const c = 0;
|
||||
export const a: typeof c;
|
||||
export {};
|
||||
}
|
||||
declare module "OnlyOneExport" {
|
||||
const a = 0;
|
||||
}
|
||||
declare module "TwoExports" {
|
||||
const c = 0;
|
||||
const a: typeof c;
|
||||
}
|
||||
declare module "OneExportReferencedANonExported" {
|
||||
const c = 0;
|
||||
const a: typeof c;
|
||||
}
|
||||
declare global {
|
||||
const c = 0;
|
||||
export const a: typeof c;
|
||||
}
|
||||
|
|
@ -2,9 +2,8 @@ commit: a709f989
|
|||
|
||||
transpile Summary:
|
||||
AST Parsed : 20/20 (100.00%)
|
||||
Positive Passed: 17/20 (85.00%)
|
||||
Positive Passed: 18/20 (90.00%)
|
||||
Mismatch: tasks/coverage/typescript/tests/cases/transpile/declarationBasicSyntax.ts
|
||||
Mismatch: tasks/coverage/typescript/tests/cases/transpile/declarationEmitPartialNodeReuse.ts
|
||||
Mismatch: tasks/coverage/typescript/tests/cases/transpile/declarationRestParameters.ts
|
||||
|
||||
#### "typescript/tests/cases/transpile/declarationComputedPropertyNames.ts" ####
|
||||
|
|
@ -68,7 +67,7 @@ export const D = {
|
|||
|
||||
//// [declarationComputedPropertyNames.d.ts] ////
|
||||
export declare namespace presentNs {
|
||||
export const a: unknown;
|
||||
const a: unknown;
|
||||
}
|
||||
declare const aliasing: unknown;
|
||||
export type A = {
|
||||
|
|
|
|||
Loading…
Reference in a new issue