mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
refactor(transformer/typescript): improve transforming namespace (#8459)
I did a few things in this PR, 1. Remove `names` which store the declarations name used for checking re-declaration. (We can use SymbolTable to do it now.) 2. Correct semantic data for namespace transform 3. Simplify code
This commit is contained in:
parent
bf00f82268
commit
c83ce5c6cf
5 changed files with 4373 additions and 9194 deletions
|
|
@ -1,16 +1,14 @@
|
|||
use rustc_hash::FxHashSet;
|
||||
|
||||
use oxc_allocator::{Box as ArenaBox, Vec as ArenaVec};
|
||||
use oxc_ast::{ast::*, NONE};
|
||||
use oxc_ecmascript::BoundNames;
|
||||
use oxc_semantic::Reference;
|
||||
use oxc_span::{Atom, CompactStr, SPAN};
|
||||
use oxc_span::SPAN;
|
||||
use oxc_syntax::{
|
||||
operator::{AssignmentOperator, LogicalOperator},
|
||||
scope::{ScopeFlags, ScopeId},
|
||||
symbol::SymbolFlags,
|
||||
};
|
||||
use oxc_traverse::{Traverse, TraverseCtx};
|
||||
use oxc_traverse::{BoundIdentifier, Traverse, TraverseCtx};
|
||||
|
||||
use crate::TransformCtx;
|
||||
|
||||
|
|
@ -46,9 +44,6 @@ impl<'a> Traverse<'a> for TypeScriptNamespace<'a, '_> {
|
|||
return;
|
||||
}
|
||||
|
||||
// Collect function/class/enum/namespace binding names
|
||||
let mut names: FxHashSet<Atom<'a>> = FxHashSet::default();
|
||||
|
||||
// Recreate the statements vec for memory efficiency.
|
||||
// Inserting the `let` declaration multiple times will reallocate the whole statements vec
|
||||
// every time a namespace declaration is encountered.
|
||||
|
|
@ -57,86 +52,33 @@ impl<'a> Traverse<'a> for TypeScriptNamespace<'a, '_> {
|
|||
for stmt in ctx.ast.move_vec(&mut program.body) {
|
||||
match stmt {
|
||||
Statement::TSModuleDeclaration(decl) => {
|
||||
if !decl.declare {
|
||||
if !self.allow_namespaces {
|
||||
self.ctx.error(namespace_not_supported(decl.span));
|
||||
}
|
||||
|
||||
if let Some(transformed_stmt) = self.handle_nested(
|
||||
{
|
||||
// SAFETY: `ast.copy` is unsound! We need to fix.
|
||||
unsafe { ctx.ast.copy(&decl) }.unbox()
|
||||
},
|
||||
None,
|
||||
ctx,
|
||||
) {
|
||||
let name = decl.id.name();
|
||||
if names.insert(name.clone()) {
|
||||
new_stmts.push(Statement::from(Self::create_variable_declaration(
|
||||
name, ctx,
|
||||
)));
|
||||
}
|
||||
new_stmts.push(transformed_stmt);
|
||||
continue;
|
||||
}
|
||||
if !self.allow_namespaces {
|
||||
self.ctx.error(namespace_not_supported(decl.span));
|
||||
}
|
||||
new_stmts.push(Statement::TSModuleDeclaration(decl));
|
||||
|
||||
self.handle_nested(decl, /* is_export */ false, &mut new_stmts, None, ctx);
|
||||
continue;
|
||||
}
|
||||
Statement::ExportNamedDeclaration(ref export_decl) => {
|
||||
match &export_decl.declaration {
|
||||
Some(Declaration::TSModuleDeclaration(decl)) => {
|
||||
if !decl.declare {
|
||||
if !self.allow_namespaces {
|
||||
self.ctx.error(namespace_not_supported(decl.span));
|
||||
}
|
||||
|
||||
if let Some(transformed_stmt) = self.handle_nested(
|
||||
{
|
||||
// SAFETY: `ast.copy` is unsound! We need to fix.
|
||||
unsafe { ctx.ast.copy(decl) }
|
||||
},
|
||||
None,
|
||||
ctx,
|
||||
) {
|
||||
let name = decl.id.name();
|
||||
if names.insert(name.clone()) {
|
||||
let declaration =
|
||||
Self::create_variable_declaration(name, ctx);
|
||||
let export_named_decl =
|
||||
ctx.ast.plain_export_named_declaration_declaration(
|
||||
SPAN,
|
||||
declaration,
|
||||
);
|
||||
let stmt =
|
||||
Statement::ExportNamedDeclaration(export_named_decl);
|
||||
new_stmts.push(stmt);
|
||||
}
|
||||
new_stmts.push(transformed_stmt);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if let TSModuleDeclarationName::Identifier(id) = &decl.id {
|
||||
names.insert(id.name.clone());
|
||||
}
|
||||
}
|
||||
Some(decl) => match decl {
|
||||
Declaration::FunctionDeclaration(_)
|
||||
| Declaration::ClassDeclaration(_)
|
||||
| Declaration::TSEnumDeclaration(_) => {
|
||||
names.insert(decl.id().as_ref().unwrap().name.clone());
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
Statement::ExportNamedDeclaration(export_decl) => {
|
||||
if export_decl.declaration.as_ref().map_or(true, |decl| {
|
||||
decl.declare() || !matches!(decl, Declaration::TSModuleDeclaration(_))
|
||||
}) {
|
||||
new_stmts.push(Statement::ExportNamedDeclaration(export_decl));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Collect bindings from class, function and enum declarations
|
||||
Statement::FunctionDeclaration(_)
|
||||
| Statement::ClassDeclaration(_)
|
||||
| Statement::TSEnumDeclaration(_) => {
|
||||
names.insert(stmt.to_declaration().id().as_ref().unwrap().name.clone());
|
||||
|
||||
let Some(Declaration::TSModuleDeclaration(decl)) =
|
||||
export_decl.unbox().declaration
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
if !self.allow_namespaces {
|
||||
self.ctx.error(namespace_not_supported(decl.span));
|
||||
}
|
||||
|
||||
self.handle_nested(decl, /* is_export */ true, &mut new_stmts, None, ctx);
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -151,25 +93,34 @@ impl<'a> Traverse<'a> for TypeScriptNamespace<'a, '_> {
|
|||
impl<'a> TypeScriptNamespace<'a, '_> {
|
||||
fn handle_nested(
|
||||
&self,
|
||||
decl: TSModuleDeclaration<'a>,
|
||||
parent_export: Option<Expression<'a>>,
|
||||
decl: ArenaBox<'a, TSModuleDeclaration<'a>>,
|
||||
is_export: bool,
|
||||
parent_stmts: &mut ArenaVec<'a, Statement<'a>>,
|
||||
parent_binding: Option<&BoundIdentifier<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Option<Statement<'a>> {
|
||||
) {
|
||||
if decl.declare {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip empty declaration e.g. `namespace x;`
|
||||
let body = decl.body?;
|
||||
let TSModuleDeclaration { span, id, body, scope_id, .. } = decl.unbox();
|
||||
|
||||
let mut names: FxHashSet<Atom<'a>> = FxHashSet::default();
|
||||
|
||||
let TSModuleDeclarationName::Identifier(BindingIdentifier { name: real_name, .. }) =
|
||||
decl.id
|
||||
else {
|
||||
return None;
|
||||
let TSModuleDeclarationName::Identifier(ident) = id else {
|
||||
self.ctx.error(ambient_module_nested(span));
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(body) = body else {
|
||||
return;
|
||||
};
|
||||
|
||||
let binding = BoundIdentifier::from_binding_ident(&ident);
|
||||
|
||||
// Reuse `TSModuleDeclaration`'s scope in transformed function
|
||||
let scope_id = decl.scope_id.get().unwrap();
|
||||
let binding = ctx.generate_uid(&real_name, scope_id, SymbolFlags::FunctionScopedVariable);
|
||||
let name = binding.name;
|
||||
let scope_id = scope_id.get().unwrap();
|
||||
let uid_binding =
|
||||
ctx.generate_uid(&binding.name, scope_id, SymbolFlags::FunctionScopedVariable);
|
||||
|
||||
let directives;
|
||||
let namespace_top_level;
|
||||
|
|
@ -199,21 +150,7 @@ impl<'a> TypeScriptNamespace<'a, '_> {
|
|||
for stmt in namespace_top_level {
|
||||
match stmt {
|
||||
Statement::TSModuleDeclaration(decl) => {
|
||||
if decl.id.is_string_literal() {
|
||||
self.ctx.error(ambient_module_nested(decl.span));
|
||||
continue;
|
||||
}
|
||||
|
||||
let module_name = decl.id.name().clone();
|
||||
if let Some(transformed) = self.handle_nested(decl.unbox(), None, ctx) {
|
||||
if names.insert(module_name.clone()) {
|
||||
new_stmts.push(Statement::from(Self::create_variable_declaration(
|
||||
module_name.clone(),
|
||||
ctx,
|
||||
)));
|
||||
}
|
||||
new_stmts.push(transformed);
|
||||
}
|
||||
self.handle_nested(decl, /* is_export */ false, &mut new_stmts, None, ctx);
|
||||
continue;
|
||||
}
|
||||
Statement::ExportNamedDeclaration(export_decl) => {
|
||||
|
|
@ -225,16 +162,28 @@ impl<'a> TypeScriptNamespace<'a, '_> {
|
|||
continue;
|
||||
}
|
||||
match decl {
|
||||
Declaration::TSEnumDeclaration(_)
|
||||
| Declaration::FunctionDeclaration(_)
|
||||
| Declaration::ClassDeclaration(_) => {
|
||||
Self::add_declaration(
|
||||
decl,
|
||||
name.clone(),
|
||||
&mut names,
|
||||
&mut new_stmts,
|
||||
ctx,
|
||||
Declaration::TSEnumDeclaration(ref enum_decl) => {
|
||||
let binding = BoundIdentifier::from_binding_ident(&enum_decl.id);
|
||||
new_stmts.push(Statement::from(decl));
|
||||
Self::add_declaration(&uid_binding, &binding, &mut new_stmts, ctx);
|
||||
}
|
||||
Declaration::ClassDeclaration(ref class_decl) => {
|
||||
// Class declaration always has a binding
|
||||
let binding = BoundIdentifier::from_binding_ident(
|
||||
class_decl.id.as_ref().unwrap(),
|
||||
);
|
||||
new_stmts.push(Statement::from(decl));
|
||||
Self::add_declaration(&uid_binding, &binding, &mut new_stmts, ctx);
|
||||
}
|
||||
Declaration::FunctionDeclaration(ref func_decl)
|
||||
if !func_decl.is_typescript_syntax() =>
|
||||
{
|
||||
// Function declaration always has a binding
|
||||
let binding = BoundIdentifier::from_binding_ident(
|
||||
func_decl.id.as_ref().unwrap(),
|
||||
);
|
||||
new_stmts.push(Statement::from(decl));
|
||||
Self::add_declaration(&uid_binding, &binding, &mut new_stmts, ctx);
|
||||
}
|
||||
Declaration::VariableDeclaration(var_decl) => {
|
||||
var_decl.declarations.iter().for_each(|decl| {
|
||||
|
|
@ -243,43 +192,24 @@ impl<'a> TypeScriptNamespace<'a, '_> {
|
|||
}
|
||||
});
|
||||
let stmts =
|
||||
Self::handle_variable_declaration(var_decl, name.clone(), ctx);
|
||||
Self::handle_variable_declaration(var_decl, &uid_binding, ctx);
|
||||
new_stmts.extend(stmts);
|
||||
}
|
||||
Declaration::TSModuleDeclaration(module_decl) => {
|
||||
if module_decl.id.is_string_literal() {
|
||||
self.ctx.error(ambient_module_nested(module_decl.span));
|
||||
continue;
|
||||
}
|
||||
|
||||
let module_name = module_decl.id.name().clone();
|
||||
if let Some(transformed) = self.handle_nested(
|
||||
module_decl.unbox(),
|
||||
Some(ctx.ast.expression_identifier_reference(SPAN, &name)),
|
||||
self.handle_nested(
|
||||
module_decl,
|
||||
/* is_export */
|
||||
false,
|
||||
&mut new_stmts,
|
||||
Some(&uid_binding),
|
||||
ctx,
|
||||
) {
|
||||
if names.insert(module_name.clone()) {
|
||||
new_stmts.push(Statement::from(
|
||||
Self::create_variable_declaration(
|
||||
module_name.clone(),
|
||||
ctx,
|
||||
),
|
||||
));
|
||||
}
|
||||
new_stmts.push(transformed);
|
||||
}
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Collect bindings from class, function and enum declarations
|
||||
Statement::ClassDeclaration(_)
|
||||
| Statement::FunctionDeclaration(_)
|
||||
| Statement::TSEnumDeclaration(_) => {
|
||||
names.insert(stmt.to_declaration().id().as_ref().unwrap().name.clone());
|
||||
}
|
||||
// Retain when `only_remove_type_imports` is true or there are value references
|
||||
// The behavior is the same as `TypeScriptModule::transform_ts_import_equals`
|
||||
Statement::TSImportEqualsDeclaration(decl)
|
||||
|
|
@ -302,30 +232,45 @@ impl<'a> TypeScriptNamespace<'a, '_> {
|
|||
if new_stmts.is_empty() {
|
||||
// Delete the scope binding that `ctx.generate_uid` created above,
|
||||
// as no binding is actually being created
|
||||
ctx.scopes_mut().remove_binding(scope_id, &CompactStr::from(name.as_str()));
|
||||
ctx.scopes_mut().remove_binding(scope_id, uid_binding.name.as_str());
|
||||
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
|
||||
Some(Self::transform_namespace(
|
||||
name,
|
||||
real_name,
|
||||
new_stmts,
|
||||
directives,
|
||||
parent_export,
|
||||
if !Self::is_redeclaration_namespace(&ident, ctx) {
|
||||
let declaration = Self::create_variable_declaration(&binding, ctx);
|
||||
if is_export {
|
||||
let export_named_decl =
|
||||
ctx.ast.plain_export_named_declaration_declaration(SPAN, declaration);
|
||||
let stmt = Statement::ExportNamedDeclaration(export_named_decl);
|
||||
parent_stmts.push(stmt);
|
||||
} else {
|
||||
parent_stmts.push(Statement::from(declaration));
|
||||
}
|
||||
}
|
||||
let func_body = ctx.ast.function_body(SPAN, directives, new_stmts);
|
||||
|
||||
parent_stmts.push(Self::transform_namespace(
|
||||
span,
|
||||
&uid_binding,
|
||||
&binding,
|
||||
parent_binding,
|
||||
func_body,
|
||||
scope_id,
|
||||
ctx,
|
||||
))
|
||||
));
|
||||
}
|
||||
|
||||
// `namespace Foo { }` -> `let Foo; (function (_Foo) { })(Foo || (Foo = {}));`
|
||||
// ^^^^^^^
|
||||
fn create_variable_declaration(name: Atom<'a>, ctx: &TraverseCtx<'a>) -> Declaration<'a> {
|
||||
fn create_variable_declaration(
|
||||
binding: &BoundIdentifier<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Declaration<'a> {
|
||||
let kind = VariableDeclarationKind::Let;
|
||||
let declarations = {
|
||||
let pattern_kind = ctx.ast.binding_pattern_kind_binding_identifier(SPAN, name);
|
||||
let binding = ctx.ast.binding_pattern(pattern_kind, NONE, false);
|
||||
let decl = ctx.ast.variable_declarator(SPAN, kind, binding, None, false);
|
||||
let pattern = binding.create_binding_pattern(ctx);
|
||||
let decl = ctx.ast.variable_declarator(SPAN, kind, pattern, None, false);
|
||||
ctx.ast.vec1(decl)
|
||||
};
|
||||
ctx.ast.declaration_variable(SPAN, kind, declarations, false)
|
||||
|
|
@ -333,21 +278,19 @@ impl<'a> TypeScriptNamespace<'a, '_> {
|
|||
|
||||
// `namespace Foo { }` -> `let Foo; (function (_Foo) { })(Foo || (Foo = {}));`
|
||||
fn transform_namespace(
|
||||
arg_name: Atom<'a>,
|
||||
real_name: Atom<'a>,
|
||||
stmts: ArenaVec<'a, Statement<'a>>,
|
||||
directives: ArenaVec<'a, Directive<'a>>,
|
||||
parent_export: Option<Expression<'a>>,
|
||||
span: Span,
|
||||
param_binding: &BoundIdentifier<'a>,
|
||||
binding: &BoundIdentifier<'a>,
|
||||
parent_binding: Option<&BoundIdentifier<'a>>,
|
||||
func_body: FunctionBody<'a>,
|
||||
scope_id: ScopeId,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Statement<'a> {
|
||||
// `(function (_N) { var x; })(N || (N = {}))`;
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
let callee = {
|
||||
let body = ctx.ast.function_body(SPAN, directives, stmts);
|
||||
let params = {
|
||||
let ident = ctx.ast.binding_pattern_kind_binding_identifier(SPAN, arg_name);
|
||||
let pattern = ctx.ast.binding_pattern(ident, NONE, false);
|
||||
let pattern = param_binding.create_binding_pattern(ctx);
|
||||
let items = ctx.ast.vec1(ctx.ast.plain_formal_parameter(SPAN, pattern));
|
||||
ctx.ast.formal_parameters(SPAN, FormalParameterKind::FormalParameter, items, NONE)
|
||||
};
|
||||
|
|
@ -357,7 +300,7 @@ impl<'a> TypeScriptNamespace<'a, '_> {
|
|||
SPAN,
|
||||
None,
|
||||
params,
|
||||
body,
|
||||
func_body,
|
||||
scope_id,
|
||||
));
|
||||
*ctx.scopes_mut().get_flags_mut(scope_id) =
|
||||
|
|
@ -370,26 +313,21 @@ impl<'a> TypeScriptNamespace<'a, '_> {
|
|||
// Nested namespace arguments Normal namespace arguments
|
||||
let arguments = {
|
||||
// M
|
||||
let logical_left = ctx.ast.expression_identifier_reference(SPAN, real_name.clone());
|
||||
let logical_left = binding.create_read_expression(ctx);
|
||||
|
||||
// (_N.M = {}) or (N = {})
|
||||
let mut logical_right = {
|
||||
// _N.M
|
||||
// SAFETY: `ast.copy` is unsound! We need to fix.
|
||||
let parent_export = unsafe { ctx.ast.copy(&parent_export) };
|
||||
let assign_left = if let Some(parent_export) = parent_export {
|
||||
let assign_left = if let Some(parent_binding) = parent_binding {
|
||||
AssignmentTarget::from(ctx.ast.member_expression_static(
|
||||
SPAN,
|
||||
parent_export,
|
||||
ctx.ast.identifier_name(SPAN, real_name.clone()),
|
||||
parent_binding.create_read_expression(ctx),
|
||||
ctx.ast.identifier_name(SPAN, binding.name.clone()),
|
||||
false,
|
||||
))
|
||||
} else {
|
||||
// _N
|
||||
AssignmentTarget::from(
|
||||
ctx.ast
|
||||
.simple_assignment_target_identifier_reference(SPAN, real_name.clone()),
|
||||
)
|
||||
AssignmentTarget::from(binding.create_write_simple_target(ctx))
|
||||
};
|
||||
|
||||
let assign_right = ctx.ast.expression_object(SPAN, ctx.ast.vec(), None);
|
||||
|
|
@ -400,19 +338,21 @@ impl<'a> TypeScriptNamespace<'a, '_> {
|
|||
};
|
||||
|
||||
// (M = _N.M || (_N.M = {}))
|
||||
if let Some(parent_export) = parent_export {
|
||||
let assign_left =
|
||||
ctx.ast.simple_assignment_target_identifier_reference(SPAN, real_name.clone());
|
||||
if let Some(parent_binding) = parent_binding {
|
||||
let assign_left = AssignmentTarget::from(binding.create_write_simple_target(ctx));
|
||||
let assign_right = {
|
||||
let property = ctx.ast.identifier_name(SPAN, real_name);
|
||||
let logical_left =
|
||||
ctx.ast.member_expression_static(SPAN, parent_export, property, false);
|
||||
let property = ctx.ast.identifier_name(SPAN, binding.name.clone());
|
||||
let logical_left = ctx.ast.member_expression_static(
|
||||
SPAN,
|
||||
parent_binding.create_read_expression(ctx),
|
||||
property,
|
||||
false,
|
||||
);
|
||||
let op = LogicalOperator::Or;
|
||||
ctx.ast.expression_logical(SPAN, logical_left.into(), op, logical_right)
|
||||
};
|
||||
let op = AssignmentOperator::Assign;
|
||||
logical_right =
|
||||
ctx.ast.expression_assignment(SPAN, op, assign_left.into(), assign_right);
|
||||
logical_right = ctx.ast.expression_assignment(SPAN, op, assign_left, assign_right);
|
||||
logical_right = ctx.ast.expression_parenthesized(SPAN, logical_right);
|
||||
}
|
||||
|
||||
|
|
@ -422,50 +362,43 @@ impl<'a> TypeScriptNamespace<'a, '_> {
|
|||
};
|
||||
|
||||
let expr = ctx.ast.expression_call(SPAN, callee, NONE, arguments, false);
|
||||
ctx.ast.statement_expression(SPAN, expr)
|
||||
ctx.ast.statement_expression(span, expr)
|
||||
}
|
||||
|
||||
/// Add assignment statement for decl id
|
||||
/// function id() {} -> function id() {}; Name.id = id;
|
||||
fn add_declaration(
|
||||
decl: Declaration<'a>,
|
||||
name: Atom<'a>,
|
||||
names: &mut FxHashSet<Atom<'a>>,
|
||||
namespace_binding: &BoundIdentifier<'a>,
|
||||
value_binding: &BoundIdentifier<'a>,
|
||||
new_stmts: &mut ArenaVec<'a, Statement<'a>>,
|
||||
ctx: &TraverseCtx<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
// This function is only called with a function, class, or enum declaration,
|
||||
// all of which are guaranteed to have an `id`
|
||||
let ident = decl.id().unwrap();
|
||||
let item_name = ident.name.clone();
|
||||
new_stmts.push(Statement::from(decl));
|
||||
let assignment_statement = Self::create_assignment_statement(name, item_name.clone(), ctx);
|
||||
let assignment_statement =
|
||||
Self::create_assignment_statement(namespace_binding, value_binding, ctx);
|
||||
let assignment_statement = ctx.ast.statement_expression(SPAN, assignment_statement);
|
||||
new_stmts.push(assignment_statement);
|
||||
names.insert(item_name);
|
||||
}
|
||||
|
||||
// name.item_name = item_name
|
||||
// parent_binding.binding = binding
|
||||
fn create_assignment_statement(
|
||||
name: Atom<'a>,
|
||||
item_name: Atom<'a>,
|
||||
ctx: &TraverseCtx<'a>,
|
||||
object_binding: &BoundIdentifier<'a>,
|
||||
value_binding: &BoundIdentifier<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Expression<'a> {
|
||||
let object = ctx.ast.expression_identifier_reference(SPAN, name);
|
||||
let property = ctx.ast.identifier_name(SPAN, &item_name);
|
||||
let object = object_binding.create_read_expression(ctx);
|
||||
let property = ctx.ast.identifier_name(SPAN, &value_binding.name);
|
||||
let left = ctx.ast.member_expression_static(SPAN, object, property, false);
|
||||
let left = AssignmentTarget::from(left);
|
||||
let right = ctx.ast.expression_identifier_reference(SPAN, item_name);
|
||||
let right = value_binding.create_read_expression(ctx);
|
||||
let op = AssignmentOperator::Assign;
|
||||
ctx.ast.expression_assignment(SPAN, op, left, right)
|
||||
}
|
||||
|
||||
/// Convert `export const foo = 1` to `Namespace.foo = 1`;
|
||||
#[expect(clippy::needless_pass_by_value)]
|
||||
fn handle_variable_declaration(
|
||||
mut var_decl: ArenaBox<'a, VariableDeclaration<'a>>,
|
||||
name: Atom<'a>,
|
||||
ctx: &TraverseCtx<'a>,
|
||||
binding: &BoundIdentifier<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> ArenaVec<'a, Statement<'a>> {
|
||||
let is_all_binding_identifier = var_decl
|
||||
.declarations
|
||||
|
|
@ -486,7 +419,7 @@ impl<'a> TypeScriptNamespace<'a, '_> {
|
|||
AssignmentOperator::Assign,
|
||||
SimpleAssignmentTarget::from(ctx.ast.member_expression_static(
|
||||
SPAN,
|
||||
ctx.ast.expression_identifier_reference(SPAN, name.clone()),
|
||||
binding.create_read_expression(ctx),
|
||||
ctx.ast.identifier_name(SPAN, property_name),
|
||||
false,
|
||||
))
|
||||
|
|
@ -503,7 +436,11 @@ impl<'a> TypeScriptNamespace<'a, '_> {
|
|||
// `export const [a] = 1` transforms to `const [a] = 1; N.a = a`
|
||||
let mut assignments = ctx.ast.vec();
|
||||
var_decl.bound_names(&mut |id| {
|
||||
assignments.push(Self::create_assignment_statement(name.clone(), id.name.clone(), ctx));
|
||||
assignments.push(Self::create_assignment_statement(
|
||||
binding,
|
||||
&BoundIdentifier::from_binding_ident(id),
|
||||
ctx,
|
||||
));
|
||||
});
|
||||
|
||||
ctx.ast.vec_from_array([
|
||||
|
|
@ -511,6 +448,23 @@ impl<'a> TypeScriptNamespace<'a, '_> {
|
|||
ctx.ast.statement_expression(SPAN, ctx.ast.expression_sequence(SPAN, assignments)),
|
||||
])
|
||||
}
|
||||
|
||||
/// Check the namespace binding identifier if it is a redeclaration
|
||||
fn is_redeclaration_namespace(id: &BindingIdentifier<'a>, ctx: &TraverseCtx<'a>) -> bool {
|
||||
let symbol_id = id.symbol_id();
|
||||
// Only `enum`, `class`, `function` and `namespace` can be re-declared in same scope
|
||||
ctx.symbols()
|
||||
.get_flags(symbol_id)
|
||||
.intersects(SymbolFlags::RegularEnum | SymbolFlags::Class | SymbolFlags::Function)
|
||||
|| {
|
||||
// ```
|
||||
// namespace Foo {}
|
||||
// namespace Foo {} // is redeclaration
|
||||
// ```
|
||||
let redeclarations = ctx.symbols().get_redeclarations(symbol_id);
|
||||
!redeclarations.is_empty() && redeclarations.contains(&id.span)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the statements contain a namespace declaration
|
||||
|
|
|
|||
|
|
@ -676,17 +676,12 @@ after transform: SymbolId(3): SymbolFlags(RegularEnum)
|
|||
rebuilt : SymbolId(3): SymbolFlags(BlockScopedVariable)
|
||||
|
||||
tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/export/nested-same-name/input.ts
|
||||
semantic error: Missing SymbolId: "N"
|
||||
Missing SymbolId: "_N"
|
||||
Missing ReferenceId: "_N"
|
||||
Missing ReferenceId: "N"
|
||||
Missing ReferenceId: "N"
|
||||
Binding symbols mismatch:
|
||||
after transform: ScopeId(0): [SymbolId(0), SymbolId(1)]
|
||||
rebuilt : ScopeId(0): [SymbolId(0), SymbolId(1)]
|
||||
Binding symbols mismatch:
|
||||
after transform: ScopeId(1): [SymbolId(2), SymbolId(3)]
|
||||
rebuilt : ScopeId(1): [SymbolId(2), SymbolId(3)]
|
||||
semantic error: Symbol flags mismatch for "N":
|
||||
after transform: SymbolId(1): SymbolFlags(NameSpaceModule | ValueModule)
|
||||
rebuilt : SymbolId(1): SymbolFlags(BlockScopedVariable)
|
||||
Symbol span mismatch for "N":
|
||||
after transform: SymbolId(1): Span { start: 37, end: 38 }
|
||||
rebuilt : SymbolId(1): Span { start: 0, end: 0 }
|
||||
|
||||
tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/function/anonymous-generator/input.ts
|
||||
semantic error: Unresolved references mismatch:
|
||||
|
|
@ -1036,16 +1031,12 @@ after transform: ScopeId(0): [ScopeId(1), ScopeId(2), ScopeId(3)]
|
|||
rebuilt : ScopeId(0): []
|
||||
|
||||
tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/module-namespace/body/input.ts
|
||||
semantic error: Missing SymbolId: "N"
|
||||
Missing SymbolId: "_N"
|
||||
Missing ReferenceId: "N"
|
||||
Missing ReferenceId: "N"
|
||||
Binding symbols mismatch:
|
||||
after transform: ScopeId(0): [SymbolId(0)]
|
||||
rebuilt : ScopeId(0): [SymbolId(0)]
|
||||
Binding symbols mismatch:
|
||||
after transform: ScopeId(1): [SymbolId(1), SymbolId(2)]
|
||||
rebuilt : ScopeId(1): [SymbolId(1), SymbolId(2)]
|
||||
semantic error: Symbol flags mismatch for "N":
|
||||
after transform: SymbolId(0): SymbolFlags(NameSpaceModule | ValueModule)
|
||||
rebuilt : SymbolId(0): SymbolFlags(BlockScopedVariable)
|
||||
Symbol span mismatch for "N":
|
||||
after transform: SymbolId(0): Span { start: 10, end: 11 }
|
||||
rebuilt : SymbolId(0): Span { start: 0, end: 0 }
|
||||
|
||||
tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/module-namespace/body-declare/input.ts
|
||||
semantic error: Bindings mismatch:
|
||||
|
|
@ -1096,12 +1087,7 @@ after transform: ScopeId(0): [ScopeId(1)]
|
|||
rebuilt : ScopeId(0): []
|
||||
|
||||
tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/module-namespace/head/input.ts
|
||||
semantic error: Bindings mismatch:
|
||||
after transform: ScopeId(0): ["M", "N", "m"]
|
||||
rebuilt : ScopeId(0): []
|
||||
Scope children mismatch:
|
||||
after transform: ScopeId(0): [ScopeId(1), ScopeId(2), ScopeId(5), ScopeId(6)]
|
||||
rebuilt : ScopeId(0): []
|
||||
semantic error: Ambient modules cannot be nested in other modules or namespaces.
|
||||
|
||||
tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/module-namespace/head-declare/input.ts
|
||||
semantic error: Bindings mismatch:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -121,23 +121,21 @@ after transform: SymbolId(5): [ReferenceId(3), ReferenceId(4), ReferenceId(5), R
|
|||
rebuilt : SymbolId(2): [ReferenceId(0), ReferenceId(1), ReferenceId(2), ReferenceId(3), ReferenceId(4), ReferenceId(5), ReferenceId(6), ReferenceId(8)]
|
||||
|
||||
* export-elimination/input.ts
|
||||
Missing SymbolId: "Name"
|
||||
Missing SymbolId: "_Name"
|
||||
Missing ReferenceId: "_Name"
|
||||
Missing ReferenceId: "Name"
|
||||
Missing ReferenceId: "Name"
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["Bar", "Foo", "Func", "Im", "Name", "Ok"]
|
||||
rebuilt : ScopeId(0): ["Bar", "Foo", "Func", "Im", "Name", "Ok", "T"]
|
||||
Scope children mismatch:
|
||||
after transform: ScopeId(0): [ScopeId(1), ScopeId(2), ScopeId(3), ScopeId(4), ScopeId(5), ScopeId(6), ScopeId(7)]
|
||||
rebuilt : ScopeId(0): [ScopeId(1), ScopeId(2), ScopeId(3), ScopeId(4)]
|
||||
Binding symbols mismatch:
|
||||
after transform: ScopeId(5): [SymbolId(8), SymbolId(10)]
|
||||
rebuilt : ScopeId(3): [SymbolId(6), SymbolId(7)]
|
||||
Scope flags mismatch:
|
||||
after transform: ScopeId(5): ScopeFlags(StrictMode | Function)
|
||||
rebuilt : ScopeId(3): ScopeFlags(Function)
|
||||
Symbol flags mismatch for "Name":
|
||||
after transform: SymbolId(7): SymbolFlags(NameSpaceModule | ValueModule)
|
||||
rebuilt : SymbolId(5): SymbolFlags(BlockScopedVariable)
|
||||
Symbol span mismatch for "Name":
|
||||
after transform: SymbolId(7): Span { start: 116, end: 120 }
|
||||
rebuilt : SymbolId(5): Span { start: 0, end: 0 }
|
||||
Symbol flags mismatch for "T":
|
||||
after transform: SymbolId(9): SymbolFlags(FunctionScopedVariable | TypeAlias)
|
||||
rebuilt : SymbolId(8): SymbolFlags(FunctionScopedVariable)
|
||||
|
|
@ -150,9 +148,6 @@ rebuilt : SymbolId(8): [ReferenceId(9)]
|
|||
Symbol redeclarations mismatch for "T":
|
||||
after transform: SymbolId(9): [Span { start: 226, end: 227 }]
|
||||
rebuilt : SymbolId(8): []
|
||||
Reference symbol mismatch for "Name":
|
||||
after transform: SymbolId(7) "Name"
|
||||
rebuilt : SymbolId(5) "Name"
|
||||
|
||||
* exports/type-and-non-type/input.ts
|
||||
Scope children mismatch:
|
||||
|
|
@ -160,18 +155,7 @@ after transform: ScopeId(0): [ScopeId(1)]
|
|||
rebuilt : ScopeId(0): []
|
||||
|
||||
* namespace/import-=/input.ts
|
||||
Missing SymbolId: "N1"
|
||||
Missing SymbolId: "_N"
|
||||
Missing ReferenceId: "N1"
|
||||
Missing ReferenceId: "N1"
|
||||
Missing SymbolId: "N2"
|
||||
Missing SymbolId: "_N2"
|
||||
Missing SymbolId: "X"
|
||||
Missing ReferenceId: "N2"
|
||||
Missing ReferenceId: "N2"
|
||||
Binding symbols mismatch:
|
||||
after transform: ScopeId(0): [SymbolId(0), SymbolId(1), SymbolId(4)]
|
||||
rebuilt : ScopeId(0): [SymbolId(0), SymbolId(1), SymbolId(4)]
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(1): ["V", "X", "_N"]
|
||||
rebuilt : ScopeId(1): ["V", "_N"]
|
||||
|
|
@ -187,24 +171,25 @@ rebuilt : ScopeId(2): ScopeFlags(Function)
|
|||
Symbol reference IDs mismatch for "A":
|
||||
after transform: SymbolId(0): [ReferenceId(0), ReferenceId(1)]
|
||||
rebuilt : SymbolId(0): [ReferenceId(2)]
|
||||
Symbol flags mismatch for "N1":
|
||||
after transform: SymbolId(1): SymbolFlags(NameSpaceModule | ValueModule)
|
||||
rebuilt : SymbolId(1): SymbolFlags(BlockScopedVariable)
|
||||
Symbol span mismatch for "N1":
|
||||
after transform: SymbolId(1): Span { start: 31, end: 33 }
|
||||
rebuilt : SymbolId(1): Span { start: 0, end: 0 }
|
||||
Symbol flags mismatch for "N2":
|
||||
after transform: SymbolId(4): SymbolFlags(NameSpaceModule | ValueModule)
|
||||
rebuilt : SymbolId(4): SymbolFlags(BlockScopedVariable)
|
||||
Symbol span mismatch for "N2":
|
||||
after transform: SymbolId(4): Span { start: 130, end: 132 }
|
||||
rebuilt : SymbolId(4): Span { start: 0, end: 0 }
|
||||
Reference symbol mismatch for "X":
|
||||
after transform: SymbolId(5) "X"
|
||||
rebuilt : SymbolId(6) "X"
|
||||
|
||||
* namespace/preserve-import-=/input.ts
|
||||
Missing SymbolId: "N1"
|
||||
Missing SymbolId: "_N"
|
||||
Missing SymbolId: "Foo"
|
||||
Missing ReferenceId: "N1"
|
||||
Missing ReferenceId: "N1"
|
||||
Missing SymbolId: "N2"
|
||||
Missing SymbolId: "_N2"
|
||||
Missing SymbolId: "Foo"
|
||||
Missing ReferenceId: "N2"
|
||||
Missing ReferenceId: "N2"
|
||||
Binding symbols mismatch:
|
||||
after transform: ScopeId(0): [SymbolId(0), SymbolId(1), SymbolId(4)]
|
||||
rebuilt : ScopeId(0): [SymbolId(0), SymbolId(1), SymbolId(5)]
|
||||
Binding symbols mismatch:
|
||||
after transform: ScopeId(1): [SymbolId(2), SymbolId(3), SymbolId(7)]
|
||||
rebuilt : ScopeId(1): [SymbolId(2), SymbolId(3), SymbolId(4)]
|
||||
|
|
@ -217,6 +202,18 @@ rebuilt : ScopeId(2): [SymbolId(6), SymbolId(7), SymbolId(8)]
|
|||
Scope flags mismatch:
|
||||
after transform: ScopeId(2): ScopeFlags(StrictMode | Function)
|
||||
rebuilt : ScopeId(2): ScopeFlags(Function)
|
||||
Symbol flags mismatch for "N1":
|
||||
after transform: SymbolId(1): SymbolFlags(NameSpaceModule | ValueModule)
|
||||
rebuilt : SymbolId(1): SymbolFlags(BlockScopedVariable)
|
||||
Symbol span mismatch for "N1":
|
||||
after transform: SymbolId(1): Span { start: 34, end: 36 }
|
||||
rebuilt : SymbolId(1): Span { start: 0, end: 0 }
|
||||
Symbol flags mismatch for "N2":
|
||||
after transform: SymbolId(4): SymbolFlags(NameSpaceModule | ValueModule)
|
||||
rebuilt : SymbolId(5): SymbolFlags(BlockScopedVariable)
|
||||
Symbol span mismatch for "N2":
|
||||
after transform: SymbolId(4): Span { start: 145, end: 147 }
|
||||
rebuilt : SymbolId(5): Span { start: 0, end: 0 }
|
||||
|
||||
* preserve-import-=/input.js
|
||||
Missing SymbolId: "Foo"
|
||||
|
|
|
|||
Loading…
Reference in a new issue