From 1fdf61f9e74798c78b878829d74487fa86eca510 Mon Sep 17 00:00:00 2001 From: overlookmotel Date: Mon, 4 Nov 2024 15:42:25 +0000 Subject: [PATCH] feat(transformer): class properties transform --- .../src/common/helper_loader.rs | 10 + .../src/common/var_declarations.rs | 1 - .../src/compiler_assumptions.rs | 1 - .../src/es2022/class_properties.rs | 94 - .../src/es2022/class_properties/class.rs | 765 +++++++++ .../es2022/class_properties/constructor.rs | 656 +++++++ .../src/es2022/class_properties/mod.rs | 296 ++++ .../src/es2022/class_properties/private.rs | 984 +++++++++++ .../src/es2022/class_properties/utils.rs | 55 + .../src/es2022/class_static_block.rs | 5 +- crates/oxc_transformer/src/es2022/mod.rs | 44 +- crates/oxc_transformer/src/lib.rs | 6 + .../integrations/snapshots/es_target.snap | 5 +- .../snapshots/babel.snap.md | 735 +++++++- .../snapshots/babel_exec.snap.md | 1508 ++++++++++++++++- tasks/transform_conformance/src/constants.rs | 3 +- 16 files changed, 5044 insertions(+), 124 deletions(-) delete mode 100644 crates/oxc_transformer/src/es2022/class_properties.rs create mode 100644 crates/oxc_transformer/src/es2022/class_properties/class.rs create mode 100644 crates/oxc_transformer/src/es2022/class_properties/constructor.rs create mode 100644 crates/oxc_transformer/src/es2022/class_properties/mod.rs create mode 100644 crates/oxc_transformer/src/es2022/class_properties/private.rs create mode 100644 crates/oxc_transformer/src/es2022/class_properties/utils.rs diff --git a/crates/oxc_transformer/src/common/helper_loader.rs b/crates/oxc_transformer/src/common/helper_loader.rs index 323154111b11bc..c09c984269b36d 100644 --- a/crates/oxc_transformer/src/common/helper_loader.rs +++ b/crates/oxc_transformer/src/common/helper_loader.rs @@ -147,6 +147,11 @@ pub enum Helper { ObjectDestructuringEmpty, ObjectWithoutProperties, ToPropertyKey, + DefineProperty, + ClassPrivateFieldInitSpec, + ClassPrivateFieldGet2, + ClassPrivateFieldSet2, + AssertClassBrand, } impl Helper { @@ -162,6 +167,11 @@ impl Helper { Self::ObjectDestructuringEmpty => "objectDestructuringEmpty", Self::ObjectWithoutProperties => "objectWithoutProperties", Self::ToPropertyKey => "toPropertyKey", + Self::DefineProperty => "defineProperty", + Self::ClassPrivateFieldInitSpec => "classPrivateFieldInitSpec", + Self::ClassPrivateFieldGet2 => "classPrivateFieldGet2", + Self::ClassPrivateFieldSet2 => "classPrivateFieldSet2", + Self::AssertClassBrand => "assertClassBrand", } } } diff --git a/crates/oxc_transformer/src/common/var_declarations.rs b/crates/oxc_transformer/src/common/var_declarations.rs index 7701c15a5378ce..66fb24af9e44cb 100644 --- a/crates/oxc_transformer/src/common/var_declarations.rs +++ b/crates/oxc_transformer/src/common/var_declarations.rs @@ -98,7 +98,6 @@ impl<'a> VarDeclarationsStore<'a> { /// Add a `let` declaration to be inserted at top of current enclosing statement block, /// given a `BoundIdentifier`. - #[expect(dead_code)] pub fn insert_let( &self, binding: &BoundIdentifier<'a>, diff --git a/crates/oxc_transformer/src/compiler_assumptions.rs b/crates/oxc_transformer/src/compiler_assumptions.rs index 577b505d625dde..143b711d06642f 100644 --- a/crates/oxc_transformer/src/compiler_assumptions.rs +++ b/crates/oxc_transformer/src/compiler_assumptions.rs @@ -81,7 +81,6 @@ pub struct CompilerAssumptions { pub set_computed_properties: bool, #[serde(default)] - #[deprecated = "Not Implemented"] pub set_public_class_fields: bool, #[serde(default)] diff --git a/crates/oxc_transformer/src/es2022/class_properties.rs b/crates/oxc_transformer/src/es2022/class_properties.rs deleted file mode 100644 index becd2424c9f246..00000000000000 --- a/crates/oxc_transformer/src/es2022/class_properties.rs +++ /dev/null @@ -1,94 +0,0 @@ -//! ES2022: Class Properties -//! -//! This plugin transforms class properties to initializers inside class constructor. -//! -//! > This plugin is included in `preset-env`, in ES2022 -//! -//! ## Example -//! -//! Input: -//! ```js -//! class C { -//! foo = 123; -//! #bar = 456; -//! } -//! -//! let x = 123; -//! class D extends S { -//! foo = x; -//! constructor(x) { -//! if (x) { -//! let s = super(x); -//! } else { -//! super(x); -//! } -//! } -//! } -//! ``` -//! -//! Output: -//! ```js -//! var _bar = /*#__PURE__*/ new WeakMap(); -//! class C { -//! constructor() { -//! babelHelpers.defineProperty(this, "foo", 123); -//! babelHelpers.classPrivateFieldInitSpec(this, _bar, 456); -//! } -//! } -//! -//! let x = 123; -//! class D extends S { -//! constructor(_x) { -//! if (_x) { -//! let s = (super(_x), babelHelpers.defineProperty(this, "foo", x)); -//! } else { -//! super(_x); -//! babelHelpers.defineProperty(this, "foo", x); -//! } -//! } -//! } -//! ``` -//! -//! ## Implementation -//! -//! WORK IN PROGRESS. INCOMPLETE. -//! -//! Implementation based on [@babel/plugin-transform-class-properties](https://babel.dev/docs/babel-plugin-transform-class-properties). -//! -//! ## References: -//! * Babel plugin implementation: -//! * -//! * -//! * -//! * Class properties TC39 proposal: - -use serde::Deserialize; - -use oxc_ast::ast::*; -use oxc_traverse::{Traverse, TraverseCtx}; - -use crate::TransformCtx; - -#[derive(Debug, Default, Clone, Copy, Deserialize)] -#[serde(default, rename_all = "camelCase")] -pub struct ClassPropertiesOptions { - #[serde(alias = "loose")] - pub(crate) set_public_class_fields: bool, -} - -pub struct ClassProperties<'a, 'ctx> { - #[expect(dead_code)] - options: ClassPropertiesOptions, - #[expect(dead_code)] - ctx: &'ctx TransformCtx<'a>, -} - -impl<'a, 'ctx> ClassProperties<'a, 'ctx> { - pub fn new(options: ClassPropertiesOptions, ctx: &'ctx TransformCtx<'a>) -> Self { - Self { options, ctx } - } -} - -impl<'a, 'ctx> Traverse<'a> for ClassProperties<'a, 'ctx> { - fn enter_class_body(&mut self, _body: &mut ClassBody<'a>, _ctx: &mut TraverseCtx<'a>) {} -} diff --git a/crates/oxc_transformer/src/es2022/class_properties/class.rs b/crates/oxc_transformer/src/es2022/class_properties/class.rs new file mode 100644 index 00000000000000..747b3914ea48e6 --- /dev/null +++ b/crates/oxc_transformer/src/es2022/class_properties/class.rs @@ -0,0 +1,765 @@ +//! ES2022: Class Properties +//! Transform of class itself. + +use oxc_allocator::Address; +use oxc_ast::{ast::*, NONE}; +use oxc_span::SPAN; +use oxc_syntax::{ + reference::ReferenceFlags, + symbol::{SymbolFlags, SymbolId}, +}; +use oxc_traverse::{BoundIdentifier, TraverseCtx}; + +use crate::common::helper_loader::Helper; + +use super::super::ClassStaticBlock; +use super::{ + utils::{ + create_assignment, create_underscore_ident_name, create_variable_declaration, + exprs_into_stmts, + }, + ClassName, ClassProperties, FxIndexMap, PrivateProp, PrivateProps, +}; + +impl<'a, 'ctx> ClassProperties<'a, 'ctx> { + /// Transform class expression. + // `#[inline]` so that compiler sees that `expr` is an `Expression::ClassExpression`. + // Main guts of transform is broken out into `transform_class_expression_start` and + // `transform_class_expression_finish` to keep this function as small as possible. + // Want it to be inlined into `enter_expression` and for `enter_expression` to be inlined into parent. + #[inline] + pub(super) fn transform_class_expression( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + let Expression::ClassExpression(class) = expr else { unreachable!() }; + + let expr_count = self.transform_class_expression_start(class, ctx); + if expr_count > 0 { + self.transform_class_expression_finish(expr, expr_count, ctx); + } + } + + fn transform_class_expression_start( + &mut self, + class: &mut Class<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> usize { + // Check this class isn't being visited twice + let class_address = Address::from_ptr(class); + if *self.class_expression_addresses_stack.last() == class_address { + // This class has already been transformed, and we're now encountering it again + // in the sequence expression which was substituted for it. So don't transform it again! + // Returning 0 tells `enter_expression` not to call `transform_class_expression_finish` either. + self.class_expression_addresses_stack.pop(); + return 0; + } + + self.class_name = ClassName::Name(match &class.id { + Some(id) => id.name.as_str(), + None => "Class", + }); + self.is_declaration = false; + + self.transform_class(class, ctx); + + // Return number of expressions to be inserted before/after the class + let mut expr_count = self.insert_before.len() + self.insert_after_exprs.len(); + + let private_props = self.private_props_stack.last(); + if let Some(private_props) = private_props { + expr_count += private_props.props.len(); + } + + if expr_count > 0 { + // We're going to replace class expression with a sequence expression + // `(..., _C = class C {}, ..., _C)`, so this class will be visited again. + // Store the `Address` of class in stack. This will cause bail-out when we re-visit it. + self.class_expression_addresses_stack.push(class_address); + } + + expr_count + } + + /// Insert expressions before/after the class. + /// `C = class { [x()] = 1; static y = 2 };` + /// -> `C = (_x = x(), _Class = class C { constructor() { this[_x] = 1; } }, _Class.y = 2, _Class)` + fn transform_class_expression_finish( + &mut self, + expr: &mut Expression<'a>, + mut expr_count: usize, + ctx: &mut TraverseCtx<'a>, + ) { + // TODO: Name class if had no name, and name is statically knowable (as in example above). + // If class name shadows var which is referenced within class, rename that var. + // `var C = class { prop = C }; var C2 = C;` + // -> `var _C = class C { constructor() { this.prop = _C; } }; var C2 = _C;` + // This is really difficult as need to rename all references too to that binding too, + // which can be very far above the class in AST, when it's a `var`. + // Maybe for now only add class name if it doesn't shadow a var used within class? + + // TODO: Deduct static private props from `expr_count`. + // Or maybe should store count and increment it when create private static props? + // They're probably pretty rare, so it'll be rarely used. + expr_count += match &self.class_name { + ClassName::Binding(_) => 2, + ClassName::Name(_) => 1, + }; + + let mut exprs = ctx.ast.vec_with_capacity(expr_count); + + // Insert `_prop = new WeakMap()` expressions for private instance props. + // Babel has these always go first, regardless of order of class elements. + // Also insert `var _prop;` temp var declarations for private static props. + let private_props = self.private_props_stack.last(); + if let Some(private_props) = private_props { + let mut weakmap_symbol_id = None; + exprs.extend(private_props.props.values().filter_map(|prop| { + // Insert `var _prop;` declaration. + // Do it here rather than when binding was created to maintain same order of `var` + // declarations as Babel. `c = class C { #x = 1; static y = 2; }` -> `var _C, _x;` + self.ctx.var_declarations.insert_var(&prop.binding, None, ctx); + + if prop.is_static { + return None; + } + + // `_prop = new WeakMap()` + Some(create_assignment( + &prop.binding, + create_new_weakmap(&mut weakmap_symbol_id, ctx), + ctx, + )) + })); + } + + // Insert computed key initializers + exprs.extend(self.insert_before.drain(..)); + + // Insert class + static property assignments + static blocks + let class_expr = ctx.ast.move_expression(expr); + if let ClassName::Binding(binding) = &self.class_name { + // `_Class = class {}` + let assignment = create_assignment(binding, class_expr, ctx); + exprs.push(assignment); + // Add static property assignments + static blocks + exprs.extend(self.insert_after_exprs.drain(..)); + // `_Class` + exprs.push(binding.create_read_expression(ctx)); + } else { + // Add static blocks (which didn't reference class name) + // TODO: If class has `extends` clause, and it may have side effects, then static block contents + // goes after class expression, and temp var is called `_temp` not `_Class`. + // `let C = class extends Unbound { static { x = 1; } };` + // -> `var _temp; let C = ((_temp = class C extends Unbound {}), (x = 1), _temp);` + // `let C = class extends Bound { static { x = 1; } };` + // -> `let C = ((x = 1), class C extends Bound {});` + exprs.extend(self.insert_after_exprs.drain(..)); + + exprs.push(class_expr); + } + + *expr = ctx.ast.expression_sequence(SPAN, exprs); + } + + /// Transform class declaration. + pub(super) fn transform_class_declaration( + &mut self, + class: &mut Class<'a>, + stmt_address: Address, + ctx: &mut TraverseCtx<'a>, + ) { + // Ignore TS class declarations + // TODO: Is this correct? + // TODO: If remove this check, remove from `transform_class_on_exit` too. + if class.declare { + return; + } + + // Class declarations are always named, except for `export default class {}`, which is handled separately + let ident = class.id.as_ref().unwrap(); + self.class_name = ClassName::Binding(BoundIdentifier::from_binding_ident(ident)); + + self.transform_class_declaration_impl(class, stmt_address, ctx); + } + + /// Transform `export default class {}`. + /// + /// Separate function as this is only circumstance where have to deal with anonymous class declaration, + /// and it's an uncommon case (can only be 1 per file). + pub(super) fn transform_class_export_default( + &mut self, + class: &mut Class<'a>, + stmt_address: Address, + ctx: &mut TraverseCtx<'a>, + ) { + // Class declarations as default export may not have a name + self.class_name = match class.id.as_ref() { + Some(ident) => ClassName::Binding(BoundIdentifier::from_binding_ident(ident)), + None => ClassName::Name("Class"), + }; + + self.transform_class_declaration_impl(class, stmt_address, ctx); + + // If class was unnamed `export default class {}`, and a binding is required, set its name. + // e.g. `export default class { static x = 1; }` -> `export default class _Class {}; _Class.x = 1;` + // TODO(improve-on-babel): Could avoid this if treated `export default class {}` as a class expression + // instead of a class declaration. + if class.id.is_none() { + if let ClassName::Binding(binding) = &self.class_name { + class.id = Some(binding.create_binding_identifier(ctx)); + } + } + } + + fn transform_class_declaration_impl( + &mut self, + class: &mut Class<'a>, + stmt_address: Address, + ctx: &mut TraverseCtx<'a>, + ) { + self.is_declaration = true; + + self.transform_class(class, ctx); + + // TODO: Run other transforms on inserted statements. How? + + // Insert expressions before/after class + if !self.insert_before.is_empty() { + self.ctx.statement_injector.insert_many_before( + &stmt_address, + exprs_into_stmts(self.insert_before.drain(..), ctx), + ); + } + + if let Some(private_props) = self.private_props_stack.last() { + // TODO: Only call `insert_many_before` if some private *instance* props + let mut weakmap_symbol_id = None; + self.ctx.statement_injector.insert_many_before( + &stmt_address, + private_props.props.values().filter_map(|prop| { + if prop.is_static { + return None; + } + + // `var _prop = new WeakMap()` + Some(create_variable_declaration( + &prop.binding, + create_new_weakmap(&mut weakmap_symbol_id, ctx), + ctx, + )) + }), + ); + } + + if !self.insert_after_stmts.is_empty() { + self.ctx + .statement_injector + .insert_many_after(&stmt_address, self.insert_after_stmts.drain(..)); + } + } + + /// Main guts of the transform. + fn transform_class(&mut self, class: &mut Class<'a>, ctx: &mut TraverseCtx<'a>) { + // TODO(improve-on-babel): If outer scope is sloppy mode, all code which is moved to outside + // the class should be wrapped in an IIFE with `'use strict'` directive. Babel doesn't do this. + + // TODO: If static blocks transform is disabled, it's possible to get incorrect execution order. + // ```js + // class C { + // static x = console.log('x'); + // static { + // console.log('block'); + // } + // static y = console.log('y'); + // } + // ``` + // This logs "x", "block", "y". But in transformed output it'd be "block", "x", "y". + // Maybe force transform of static blocks if any static properties? + // Or alternatively could insert static property initializers into static blocks. + + // Check if class has any properties and get index of constructor (if class has one) + let mut instance_prop_count = 0; + let mut has_static_prop_or_static_block = false; + // TODO: Store `FxIndexMap`s in a pool and re-use them + let mut private_props = FxIndexMap::default(); + let mut constructor_index = None; + let mut index_not_including_removed = 0; + for element in &class.body.body { + match element { + ClassElement::PropertyDefinition(prop) => { + // TODO: Throw error if property has decorators + + // Create binding for private property key + if let PropertyKey::PrivateIdentifier(ident) = &prop.key { + // Note: Current scope is outside class. + let binding = ctx.generate_uid_in_current_scope( + ident.name.as_str(), + SymbolFlags::FunctionScopedVariable, + ); + private_props.insert( + ident.name.clone(), + PrivateProp { binding, is_static: prop.r#static }, + ); + } + + if prop.r#static { + // TODO(improve-on-babel): Even though private static properties may not access + // class name, Babel still creates a temp var for class. That's unnecessary. + self.initialize_class_name_binding(ctx); + + has_static_prop_or_static_block = true; + } else { + instance_prop_count += 1; + } + + continue; + } + ClassElement::StaticBlock(_) => { + // Static block only necessitates transforming class if it's being transformed + if self.static_block { + has_static_prop_or_static_block = true; + continue; + } + } + ClassElement::MethodDefinition(method) => { + if method.kind == MethodDefinitionKind::Constructor + && method.value.body.is_some() + { + constructor_index = Some(index_not_including_removed); + } + } + ClassElement::AccessorProperty(_) | ClassElement::TSIndexSignature(_) => { + // TODO: Need to handle these? + } + } + + index_not_including_removed += 1; + } + + if private_props.is_empty() { + self.private_props_stack.push(None); + } else { + let class_name_binding = match &self.class_name { + ClassName::Binding(binding) => Some(binding.clone()), + ClassName::Name(_) => None, + }; + self.private_props_stack.push(Some(PrivateProps { + props: private_props, + class_name_binding, + is_declaration: self.is_declaration, + })); + } + + // Exit if nothing to transform + if instance_prop_count == 0 && !has_static_prop_or_static_block { + return; + } + + // Extract properties and static blocks from class body + substitute computed method keys + let mut instance_inits = Vec::with_capacity(instance_prop_count); + class.body.body.retain_mut(|element| { + match element { + ClassElement::PropertyDefinition(prop) => { + if prop.r#static { + self.convert_static_property(prop, ctx); + } else { + self.convert_instance_property(prop, &mut instance_inits, ctx); + } + false + } + ClassElement::StaticBlock(block) => { + if self.static_block { + self.convert_static_block(block, ctx); + false + } else { + true + } + } + ClassElement::MethodDefinition(method) => { + self.substitute_temp_var_for_method_computed_key(method, ctx); + true + } + ClassElement::AccessorProperty(_) | ClassElement::TSIndexSignature(_) => { + // TODO: Need to handle these? + true + } + } + }); + + // Insert instance initializers into constructor + if !instance_inits.is_empty() { + // TODO: Re-parent any scopes within initializers. + if let Some(constructor_index) = constructor_index { + // Existing constructor - amend it + self.insert_inits_into_constructor(class, instance_inits, constructor_index, ctx); + } else { + // No constructor - create one + Self::insert_constructor(class, instance_inits, ctx); + } + } + } + + /// Pop from private props stack. + pub(super) fn transform_class_on_exit(&mut self, class: &Class) { + // Ignore TS class declarations + // TODO: Is this correct? + if class.declare { + return; + } + + self.private_props_stack.pop(); + } + + /// Convert instance property to initialization expression. + /// Property `foo = 123;` -> Expression `this.foo = 123` or `_defineProperty(this, "foo", 123)`. + fn convert_instance_property( + &mut self, + prop: &mut PropertyDefinition<'a>, + instance_inits: &mut Vec>, + ctx: &mut TraverseCtx<'a>, + ) { + // Get value + let value = match &mut prop.value { + Some(value) => ctx.ast.move_expression(value), + None => ctx.ast.void_0(SPAN), + }; + + let init_expr = if let PropertyKey::PrivateIdentifier(ident) = &mut prop.key { + self.create_private_instance_init_assignment(ident, value, ctx) + } else { + // Convert to assignment or `_defineProperty` call, depending on `loose` option + let this = ctx.ast.expression_this(SPAN); + self.create_init_assignment(prop, value, this, false, ctx) + }; + instance_inits.push(init_expr); + } + + /// Convert static property to initialization expression. + /// Property `static foo = 123;` -> Expression `C.foo = 123` or `_defineProperty(C, "foo", 123)`. + fn convert_static_property( + &mut self, + prop: &mut PropertyDefinition<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + // Get value + let value = match &mut prop.value { + Some(value) => ctx.ast.move_expression(value), + None => ctx.ast.void_0(SPAN), + }; + + if let PropertyKey::PrivateIdentifier(ident) = &mut prop.key { + self.insert_private_static_init_assignment(ident, value, ctx); + } else { + // Convert to assignment or `_defineProperty` call, depending on `loose` option + let ClassName::Binding(class_name_binding) = &self.class_name else { + // Binding is initialized in 1st pass in `transform_class` when a static prop is found + unreachable!(); + }; + let assignee = class_name_binding.create_read_expression(ctx); + let init_expr = self.create_init_assignment(prop, value, assignee, true, ctx); + self.insert_expr_after_class(init_expr, ctx); + } + } + + /// Create a binding for class name, if there isn't one already. + fn initialize_class_name_binding(&mut self, ctx: &mut TraverseCtx<'a>) -> &BoundIdentifier<'a> { + if let ClassName::Name(name) = &self.class_name { + let binding = if self.is_declaration { + ctx.generate_uid_in_current_scope(name, SymbolFlags::Class) + } else { + let flags = SymbolFlags::FunctionScopedVariable; + let binding = ctx.generate_uid_in_current_scope(name, flags); + self.ctx.var_declarations.insert_var(&binding, None, ctx); + binding + }; + self.class_name = ClassName::Binding(binding); + } + let ClassName::Binding(binding) = &self.class_name else { unreachable!() }; + binding + } + + /// `assignee.foo = value` or `_defineProperty(assignee, "foo", value)` + fn create_init_assignment( + &mut self, + prop: &mut PropertyDefinition<'a>, + value: Expression<'a>, + assignee: Expression<'a>, + is_static: bool, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + if self.set_public_class_fields { + // `assignee.foo = value` + self.create_init_assignment_loose(prop, value, assignee, is_static, ctx) + } else { + // `_defineProperty(assignee, "foo", value)` + self.create_init_assignment_not_loose(prop, value, assignee, ctx) + } + } + + /// `this.foo = value` or `_Class.foo = value` + fn create_init_assignment_loose( + &mut self, + prop: &mut PropertyDefinition<'a>, + value: Expression<'a>, + assignee: Expression<'a>, + is_static: bool, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + // In-built static props `name` and `length` need to be set with `_defineProperty` + let needs_define = |name| is_static && (name == "name" || name == "length"); + + let left = match &mut prop.key { + PropertyKey::StaticIdentifier(ident) => { + if needs_define(&ident.name) { + return self.create_init_assignment_not_loose(prop, value, assignee, ctx); + } + ctx.ast.member_expression_static(SPAN, assignee, ident.as_ref().clone(), false) + } + PropertyKey::StringLiteral(str_lit) if needs_define(&str_lit.value) => { + return self.create_init_assignment_not_loose(prop, value, assignee, ctx); + } + key @ match_expression!(PropertyKey) => { + // TODO: This can also be a numeric key (non-computed). Maybe other key types? + let key = self.create_computed_key_temp_var(key.to_expression_mut(), ctx); + ctx.ast.member_expression_computed(SPAN, assignee, key, false) + } + PropertyKey::PrivateIdentifier(_) => { + // Handled in `convert_instance_property` and `convert_static_property` + unreachable!(); + } + }; + + // TODO: Should this have span of the original `PropertyDefinition`? + ctx.ast.expression_assignment( + SPAN, + AssignmentOperator::Assign, + AssignmentTarget::from(left), + value, + ) + } + + /// `_defineProperty(this, "foo", value)` or `_defineProperty(_Class, "foo", value)` + fn create_init_assignment_not_loose( + &mut self, + prop: &mut PropertyDefinition<'a>, + value: Expression<'a>, + assignee: Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + let key = match &mut prop.key { + PropertyKey::StaticIdentifier(ident) => { + ctx.ast.expression_string_literal(ident.span, ident.name.clone()) + } + key @ match_expression!(PropertyKey) => { + // TODO: This can also be a numeric key (non-computed). Maybe other key types? + self.create_computed_key_temp_var(key.to_expression_mut(), ctx) + } + PropertyKey::PrivateIdentifier(_) => { + // Handled in `convert_instance_property` and `convert_static_property` + unreachable!(); + } + }; + + let arguments = ctx.ast.vec_from_array([ + Argument::from(assignee), + Argument::from(key), + Argument::from(value), + ]); + // TODO: Should this have span of the original `PropertyDefinition`? + self.ctx.helper_call_expr(Helper::DefineProperty, SPAN, arguments, ctx) + } + + /// Create `_classPrivateFieldInitSpec(this, _prop, value)` to be inserted into class constructor. + fn create_private_instance_init_assignment( + &mut self, + ident: &PrivateIdentifier<'a>, + value: Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + let private_props = self.private_props_stack.last().unwrap(); + let prop = &private_props.props[&ident.name]; + let arguments = ctx.ast.vec_from_array([ + Argument::from(ctx.ast.expression_this(SPAN)), + Argument::from(prop.binding.create_read_expression(ctx)), + Argument::from(value), + ]); + // TODO: Should this have span of original `PropertyDefinition`? + self.ctx.helper_call_expr(Helper::ClassPrivateFieldInitSpec, SPAN, arguments, ctx) + } + + /// Insert after class: + /// * Class declaration: `var _prop = {_: value};` + /// * Class expression: `_prop = {_: value}` + fn insert_private_static_init_assignment( + &mut self, + ident: &PrivateIdentifier<'a>, + value: Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + // `_prop = {_: value}` + let property = ctx.ast.object_property_kind_object_property( + SPAN, + PropertyKind::Init, + PropertyKey::StaticIdentifier(ctx.ast.alloc(create_underscore_ident_name(ctx))), + value, + false, + false, + false, + ); + let obj = ctx.ast.expression_object(SPAN, ctx.ast.vec1(property), None); + + // Insert after class + let private_props = self.private_props_stack.last().unwrap(); + let prop = &private_props.props[&ident.name]; + + if self.is_declaration { + // `var _prop = {_: value};` + let var_decl = create_variable_declaration(&prop.binding, obj, ctx); + self.insert_after_stmts.push(var_decl); + } else { + // `_prop = {_: value}` + let assignment = create_assignment(&prop.binding, obj, ctx); + self.insert_after_exprs.push(assignment); + } + } + + /// Substitute temp var for method computed key. + /// `class C { [x()]() {} }` -> `let _x; _x = x(); class C { [_x]() {} }` + /// This transform is only required if class has properties or a static block. + fn substitute_temp_var_for_method_computed_key( + &mut self, + method: &mut MethodDefinition<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + let Some(key) = method.key.as_expression_mut() else { return }; + + // TODO: Don't alter key if it's provable evaluating it has no side effects. + // TODO(improve-on-babel): It's unnecessary to create temp vars for method keys unless: + // 1. Properties also have computed keys. + // 2. Some of those properties' computed keys have side effects and require temp vars. + // 3. At least one property satisfying the above is after this method, + // or class contains a static block which is being transformed + // (static blocks are always evaluated after computed keys, regardless of order) + method.key = PropertyKey::from(self.create_computed_key_temp_var(key, ctx)); + } + + /// Convert static block to `Expression`. + /// + /// `static { x = 1; }` -> `x = 1` + /// `static { x = 1; y = 2; } -> `(() => { x = 1; y = 2; })()` + /// + /// TODO: Add tests for this if there aren't any already. + /// Include tests for evaluation order inc that static block goes before class expression + /// unless also static properties, or static block uses class name. + fn convert_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) { + // TODO: Convert `this` and references to class name. + // `x = class C { static { this.C = C; } }` -> `x = (_C = class C {}, _C.C = _C, _C)` + // TODO: Scope of static block contents becomes outer scope, not scope of class. + + // If class expression, assignment in static block moves to a position where it's read from. + // e.g.: `x` here now has read+write `ReferenceFlags`: + // `C = class C { static { x = 1; } }` -> `C = (_C = class C {}, x = 1, _C)` + let expr = ClassStaticBlock::convert_block_to_expression(block, ctx); + self.insert_expr_after_class(expr, ctx); + } + + /// Insert an expression after the class. + fn insert_expr_after_class(&mut self, expr: Expression<'a>, ctx: &mut TraverseCtx<'a>) { + if self.is_declaration { + self.insert_after_stmts.push(ctx.ast.statement_expression(SPAN, expr)); + } else { + self.insert_after_exprs.push(expr); + } + } + + /// Convert computed property/method key to a temp var. + /// + /// Transformation is: + /// * Class declaration: + /// `class C { [x()] = 1; }` -> `let _x; _x = x(); class C { constructor() { this[_x] = 1; } }` + /// * Class expression: + /// `C = class { [x()] = 1; }` -> `let _x; C = (_x = x(), class C { constructor() { this[_x] = 1; } })` + /// + /// This function: + /// * Creates the `let _x;` statement and inserts it. + /// * Creates the `_x = x()` assignments. + /// * Inserts assignments before class declaration, or adds to `state` if class expression. + /// * Returns `_x`. + fn create_computed_key_temp_var( + &mut self, + key: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + let key = ctx.ast.move_expression(key); + + // Bound vars and literals do not need temp var - return unchanged. + // e.g. `let x = 'x'; class C { [x] = 1; }` or `class C { ['x'] = 1; }` + // TODO: Do fuller analysis to detect expressions which cannot have side effects e.g. `'x' + 'y'`. + let cannot_have_side_effects = match &key { + Expression::BooleanLiteral(_) + | Expression::NullLiteral(_) + | Expression::NumericLiteral(_) + | Expression::BigIntLiteral(_) + | Expression::RegExpLiteral(_) + | Expression::StringLiteral(_) + | Expression::ThisExpression(_) => true, + Expression::Identifier(ident) => { + // Cannot have side effects if is bound. + // Additional check that the var is not mutated is required for cases like + // `let x = 1; class { [x] = 1; [++x] = 2; }` + // `++x` is hoisted to before class in output, so `x` in 1st key would get the wrong + // value unless it's hoisted out too. + // TODO: Add an exec test for this odd case. + // TODO(improve-on-babel): That case is rare. + // Test for it in first pass over class elements, and avoid temp vars where possible. + match ctx.symbols().get_reference(ident.reference_id()).symbol_id() { + Some(symbol_id) => { + ctx.symbols().get_flags(symbol_id).intersects(SymbolFlags::ConstVariable) + || ctx + .symbols() + .get_resolved_references(symbol_id) + .all(|reference| !reference.is_write()) + } + None => false, + } + } + _ => false, + }; + if cannot_have_side_effects { + return key; + } + + // We entered transform via `enter_expression` or `enter_statement`, + // so `ctx.current_scope_id()` is the scope outside the class + let parent_scope_id = ctx.current_scope_id(); + // TODO: Handle if is a class expression defined in a function's params. + let binding = + ctx.generate_uid_based_on_node(&key, parent_scope_id, SymbolFlags::BlockScopedVariable); + + self.ctx.var_declarations.insert_let(&binding, None, ctx); + + let assignment = create_assignment(&binding, key, ctx); + self.insert_before.push(assignment); + + binding.create_read_expression(ctx) + } +} + +/// Create `new WeakMap()` expression. +/// +/// Takes an `&mut Option>` which is updated after looking up the binding for `WeakMap`. +/// * `None` = Not looked up yet. +/// * `Some(None)` = Has been looked up, and `WeakMap` is unbound. +/// * `Some(Some(symbol_id))` = Has been looked up, and `WeakMap` has a local binding. +#[expect(clippy::option_option)] +fn create_new_weakmap<'a>( + symbol_id: &mut Option>, + ctx: &mut TraverseCtx<'a>, +) -> Expression<'a> { + let symbol_id = *symbol_id + .get_or_insert_with(|| ctx.scopes().find_binding(ctx.current_scope_id(), "WeakMap")); + let ident = ctx.create_ident_expr(SPAN, Atom::from("WeakMap"), symbol_id, ReferenceFlags::Read); + ctx.ast.expression_new(SPAN, ident, ctx.ast.vec(), NONE) +} diff --git a/crates/oxc_transformer/src/es2022/class_properties/constructor.rs b/crates/oxc_transformer/src/es2022/class_properties/constructor.rs new file mode 100644 index 00000000000000..76ae7cee86c829 --- /dev/null +++ b/crates/oxc_transformer/src/es2022/class_properties/constructor.rs @@ -0,0 +1,656 @@ +//! ES2022: Class Properties +//! Insertion of instance property initializers into constructor. +//! +//! When a class has instance properties / instance private properties, we need to either: +//! 1. Move initialization of these properties into existing constructor, or +//! 2. Add a constructor to the class containing property initializers. +//! +//! Oxc's output uses Babel's helpers (`_defineProperty`, `_classPrivateFieldInitSpec` etc). +//! +//! ## Output vs Babel and ESBuild +//! +//! Oxc's output follows Babel where: +//! 1. the class has no super class, or +//! 2. the class has no constructor, or +//! 3. constructor only contains a single `super()` call at top level of the function. +//! +//! Where a class with superclass has an existing constructor containing 1 or more `super()` calls +//! nested within the constructor, we do more like ESBuild does. We insert a single arrow function +//! `_super` at top of the function and replace all `super()` calls with `_super()`. +//! +//! Input: +//! ```js +//! class C extends S { +//! prop = 1; +//! constructor(yes) { +//! if (yes) { +//! super(2); +//! } else { +//! super(3); +//! } +//! } +//! } +//! ``` +//! +//! Babel output: +//! ```js +//! class C extends S { +//! constructor(yes) { +//! if (yes) { +//! super(2); +//! this.prop = foo(); +//! } else { +//! super(3); +//! this.prop = foo(); +//! } +//! } +//! } +//! ``` +//! [Babel REPL](https://babeljs.io/repl#?code_lz=MYGwhgzhAEDC0FMAeAXBA7AJjAytA3gFDTQAOATgPanQC80AZpZQBQCUA3MdMJehCnIBXYCkrkWATwQQ2BbiQCWDaFJlyiJLdAhDSCCQCZOC6AF9EICAnnaSu_RIDMJ7We7uzQA&presets=&externalPlugins=%40babel%2Fplugin-transform-class-properties%407.25.9&assumptions=%7B%22setPublicClassFields%22%3Atrue%7D) +//! +//! Oxc output: +//! ```js +//! class C extends S { +//! constructor(yes) { +//! var _super = (..._args) => { +//! super(..._args); +//! this.prop = foo(); +//! return this; +//! }; +//! if (yes) { +//! _super(2); +//! } else { +//! _super(3); +//! } +//! } +//! } +//! ``` +//! ESBuild's output: [ESBuild REPL](https://esbuild.github.io/try/#dAAwLjI0LjAALS10YXJnZXQ9ZXMyMDIwAGNsYXNzIEMgZXh0ZW5kcyBTIHsKICBwcm9wID0gZm9vKCk7CiAgY29uc3RydWN0b3IoeWVzKSB7CiAgICBpZiAoeWVzKSB7CiAgICAgIHN1cGVyKDIpOwogICAgfSBlbHNlIHsKICAgICAgc3VwZXIoMyk7CiAgICB9CiAgfQp9) +//! +//! ## `super()` in constructor params +//! +//! Babel handles this case correctly for standard properties, but Babel's approach is problematic for us +//! because Babel outputs the property initializers twice if there are 2 x `super()` calls. +//! We would need to use `CloneIn` and then duplicate all the `ReferenceId`s etc. +//! +//! Instead, we create a `_super` function containing property initializers *outside* the class +//! and convert `super()` calls to `_super(super())`. +//! +//! Input: +//! ```js +//! class C extends S { +//! prop = foo(); +//! constructor(x = super(), y = super()) {} +//! } +//! ``` +//! +//! Oxc output: +//! ```js +//! let _super = function() { +//! this.prop = foo(); +//! return this; +//! }; +//! class C extends S { +//! constructor(x = _super.call(super()), y = _super.call(super())) {} +//! } +//! ``` +//! +//! ESBuild does not `super()` in constructor params correctly: +//! [ESBuild REPL](https://esbuild.github.io/try/#dAAwLjI0LjAALS10YXJnZXQ9ZXMyMDIwAGNsYXNzIEMgZXh0ZW5kcyBTIHsKICBwcm9wID0gZm9vKCk7CiAgY29uc3RydWN0b3IoeCA9IHN1cGVyKCksIHkgPSBzdXBlcigpKSB7fQp9Cg) + +use oxc_allocator::Vec as ArenaVec; +use oxc_ast::{ast::*, visit::walk_mut, VisitMut, NONE}; +use oxc_span::SPAN; +use oxc_syntax::{ + node::NodeId, + scope::{ScopeFlags, ScopeId}, + symbol::SymbolFlags, +}; +use oxc_traverse::{BoundIdentifier, TraverseCtx}; + +use super::{ + utils::{create_assignment, exprs_into_stmts}, + ClassProperties, +}; + +impl<'a, 'ctx> ClassProperties<'a, 'ctx> { + /// Add a constructor to class containing property initializers. + pub(super) fn insert_constructor( + class: &mut Class<'a>, + inits: Vec>, + ctx: &mut TraverseCtx<'a>, + ) { + // Create scope for constructor + let scope_id = ctx.scopes_mut().add_scope( + Some(class.scope_id()), + NodeId::DUMMY, + ScopeFlags::Function | ScopeFlags::Constructor | ScopeFlags::StrictMode, + ); + + // Create statements to go in function body. + let has_super_class = class.super_class.is_some(); + let mut stmts = ctx.ast.vec_with_capacity(inits.len() + usize::from(has_super_class)); + + // Add `super(...args);` statement and `...args` param if class has a super class. + // `constructor(...args) { super(...args); /* prop initialization */ }` + // TODO: One of initializers could access a var called `args` from outer scope. + // Use a UID `_args` instead of `args` here. + let mut params_rest = None; + if has_super_class { + let binding = ctx.generate_binding( + Atom::from("args"), + scope_id, + SymbolFlags::FunctionScopedVariable, + ); + params_rest = + Some(ctx.ast.alloc_binding_rest_element(SPAN, binding.create_binding_pattern(ctx))); + stmts.push(create_super_call_stmt(&binding, ctx)); + } + // TODO: Should these have the span of the original `PropertyDefinition`s? + stmts.extend(exprs_into_stmts(inits, ctx)); + + let ctor = ClassElement::MethodDefinition(ctx.ast.alloc_method_definition( + MethodDefinitionType::MethodDefinition, + SPAN, + ctx.ast.vec(), + PropertyKey::StaticIdentifier( + ctx.ast.alloc_identifier_name(SPAN, Atom::from("constructor")), + ), + ctx.ast.alloc_function_with_scope_id( + FunctionType::FunctionExpression, + SPAN, + None, + false, + false, + false, + NONE, + NONE, + ctx.ast.alloc_formal_parameters( + SPAN, + FormalParameterKind::FormalParameter, + ctx.ast.vec(), + params_rest, + ), + NONE, + Some(ctx.ast.alloc_function_body(SPAN, ctx.ast.vec(), stmts)), + scope_id, + ), + MethodDefinitionKind::Constructor, + false, + false, + false, + false, + None, + )); + + // TODO(improve-on-babel): Could push constructor onto end of elements, instead of inserting as first + class.body.body.insert(0, ctor); + } + + /// Insert property initializers into existing class constructor. + pub(super) fn insert_inits_into_constructor( + &mut self, + class: &mut Class<'a>, + inits: Vec>, + constructor_index: usize, + ctx: &mut TraverseCtx<'a>, + ) { + // TODO: Handle where vars used in property init clash with vars in top scope of constructor. + // (or maybe do that earlier?) + // TODO: Handle private props in constructor params `class C { #x; constructor(x = this.#x) {} }`. + let constructor = match class.body.body.get_mut(constructor_index).unwrap() { + ClassElement::MethodDefinition(constructor) => constructor.as_mut(), + _ => unreachable!(), + }; + debug_assert!(constructor.kind == MethodDefinitionKind::Constructor); + + let constructor_scope_id = constructor.value.scope_id(); + let func = constructor.value.as_mut(); + let body_stmts = &mut func.body.as_mut().unwrap().statements; + + if class.super_class.is_some() { + // Class has super class. Insert inits after `super()`. + self.insert_inits_into_constructor_of_class_with_super_class( + &mut func.params, + body_stmts, + inits, + constructor_scope_id, + ctx, + ); + } else { + // No super class. Insert inits at top of constructor. + body_stmts.splice(0..0, exprs_into_stmts(inits, ctx)); + } + } + + /// Insert property initializers into existing class constructor for class which has super class. + /// + /// * If `super()` appears only as a top-level statement in constructor + /// * Insert initializers as statements after it. + /// * Return `None`. + /// * If `super()` appears in constructor params + /// * Creare a `_super` function *outside* class, which contains initializers. + /// * Replace `super()` in both constructor params and body with `_super.call(super())`. + /// * Return binding for `_super` and `_super` function as `Expression::FunctionExpression`. + /// * If `super()` appears in constructor, but not as a top level statement: + /// * Insert a `_super` function inside constructor, which contains initializers. + /// * Replace `super()` with `_super()`. + /// * Return `None`. + /// + /// See doc comment at top of this file for more details of last 2 cases. + fn insert_inits_into_constructor_of_class_with_super_class( + &mut self, + params: &mut FormalParameters<'a>, + body_stmts: &mut ArenaVec<'a, Statement<'a>>, + inits: Vec>, + constructor_scope_id: ScopeId, + ctx: &mut TraverseCtx<'a>, + ) { + // Find any `super()`s in constructor params and replace with `_super.call(super())` + // TODO: Check if any references to class name and swap them for reference to local binding. + // TODO: Add tests for `super()` in constructor params. + let mut replacer = ConstructorParamsSuperReplacer::new(ctx); + replacer.visit_formal_parameters(params); + + if replacer.super_binding.is_some() { + // `super()` was found in constructor params. + // Replace any `super()`s in constructor body with `_super.call(super())`. + // TODO: Is this correct if super class constructor returns another object? + // ```js + // class S { constructor() { return {}; } } + // class C extends S { prop = 1; constructor(x = super()) {} } + // ``` + replacer.visit_statements(body_stmts); + + let super_binding = replacer.super_binding.as_ref().unwrap(); + + // Create `_super` function + let super_func = ConstructorParamsSuperReplacer::create_super_func(inits, ctx); + + // Insert `_super` function after class. + // Note: Inserting it after class not before, so that other transforms run on it. + // TODO: That doesn't work - other transforms do not run on it. + // TODO: If static block transform is not enabled, it's possible to construct the class + // within the static block `class C { static { new C() } }` and that'd run before `_super` + // is defined. So it needs to go before the class, not after, in that case. + let init = if self.is_declaration { + Some(super_func) + } else { + let assignment = create_assignment(super_binding, super_func, ctx); + // TODO: Why does this end up before class, not after? + self.insert_after_exprs.push(assignment); + None + }; + self.ctx.var_declarations.insert_let(super_binding, init, ctx); + } else { + // No `super()` in constructor params. + // Insert property initializers after `super()` statement, or in a `_super` function. + let mut inserter = ConstructorBodyInitsInserter::new(constructor_scope_id, ctx); + inserter.insert(body_stmts, inits); + } + } +} + +/// Visitor for transforming `super()` in class constructor params. +struct ConstructorParamsSuperReplacer<'a, 'c> { + /// Binding for `_super` function. + /// Initially `None`. Binding is created if `super()` is found. + super_binding: Option>, + ctx: &'c mut TraverseCtx<'a>, +} + +impl<'a, 'c> ConstructorParamsSuperReplacer<'a, 'c> { + fn new(ctx: &'c mut TraverseCtx<'a>) -> Self { + Self { super_binding: None, ctx } + } +} + +impl<'a, 'c> VisitMut<'a> for ConstructorParamsSuperReplacer<'a, 'c> { + /// Replace `super()` with `_super.call(super())`. + // `#[inline]` to make hot path for all other expressions as cheap as possible. + #[inline] + fn visit_expression(&mut self, expr: &mut Expression<'a>) { + if let Expression::CallExpression(call_expr) = expr { + if let Expression::Super(_) = &call_expr.callee { + // Walk `CallExpression`'s arguments here rather than falling through to `walk_expression` + // below to avoid infinite loop as `super()` gets visited over and over + self.visit_arguments(&mut call_expr.arguments); + + let span = call_expr.span; + self.wrap_super(expr, span); + return; + } + } + + walk_mut::walk_expression(self, expr); + } + + // Stop traversing where scope of current `super` ends + #[inline] + fn visit_function(&mut self, _func: &mut Function<'a>, _flags: ScopeFlags) {} + + #[inline] + fn visit_static_block(&mut self, _block: &mut StaticBlock) {} + + #[inline] + fn visit_property_definition(&mut self, prop: &mut PropertyDefinition<'a>) { + // `super()` in computed key of property or method refers to super binding of parent class. + // So visit computed `key`, but not `value`. + // ```js + // class Outer extends OuterSuper { + // constructor( + // x = class Inner extends InnerSuper { + // [super().foo] = 1; // `super()` refers to `Outer`'s super class + // [super().bar]() {} // `super()` refers to `Outer`'s super class + // x = super(); // `super()` refers to `Inner`'s super class, but illegal syntax + // } + // ) {} + // } + // ``` + // Don't visit `type_annotation` field because can't contain `super()`. + // TODO: Are decorators in scope? + self.visit_decorators(&mut prop.decorators); + if prop.computed { + self.visit_property_key(&mut prop.key); + } + } + + #[inline] + fn visit_accessor_property(&mut self, prop: &mut AccessorProperty<'a>) { + // Visit computed `key` but not `value`, for same reasons as `visit_property_definition` above. + // TODO: Are decorators in scope? + self.visit_decorators(&mut prop.decorators); + if prop.computed { + self.visit_property_key(&mut prop.key); + } + } +} + +impl<'a, 'c> ConstructorParamsSuperReplacer<'a, 'c> { + /// Wrap `super()` -> `_super.call(super())` + fn wrap_super(&mut self, expr: &mut Expression<'a>, span: Span) { + let super_binding = self.super_binding.get_or_insert_with(|| { + self.ctx.generate_uid( + "super", + self.ctx.current_scope_id(), + SymbolFlags::FunctionScopedVariable, + ) + }); + + let ctx = &mut *self.ctx; + let super_call = ctx.ast.move_expression(expr); + *expr = ctx.ast.expression_call( + span, + Expression::from(ctx.ast.member_expression_static( + SPAN, + super_binding.create_read_expression(ctx), + ctx.ast.identifier_name(SPAN, Atom::from("call")), + false, + )), + NONE, + ctx.ast.vec1(Argument::from(super_call)), + false, + ); + } + + /// Create `_super` function to go outside class. + /// `function() { ; return this; }` + fn create_super_func(inits: Vec>, ctx: &mut TraverseCtx<'a>) -> Expression<'a> { + let outer_scope_id = ctx.current_scope_id(); + let super_func_scope_id = ctx.scopes_mut().add_scope( + Some(outer_scope_id), + NodeId::DUMMY, + ScopeFlags::Function | ScopeFlags::Arrow | ScopeFlags::StrictMode, + ); + // `return this;` + let return_stmt = ctx.ast.statement_return(SPAN, Some(ctx.ast.expression_this(SPAN))); + // `; return this;` + let body_stmts = ctx.ast.vec_from_iter(exprs_into_stmts(inits, ctx).chain([return_stmt])); + // `function() { ; return this; }` + Expression::FunctionExpression(ctx.ast.alloc_function_with_scope_id( + FunctionType::FunctionExpression, + SPAN, + None, + false, + false, + false, + NONE, + NONE, + ctx.ast.alloc_formal_parameters( + SPAN, + FormalParameterKind::FormalParameter, + ctx.ast.vec(), + NONE, + ), + NONE, + Some(ctx.ast.alloc_function_body(SPAN, ctx.ast.vec(), body_stmts)), + super_func_scope_id, + )) + } +} + +/// Visitor for transforming `super()` in class constructor body. +struct ConstructorBodyInitsInserter<'a, 'c> { + /// Scope of class constructor + constructor_scope_id: ScopeId, + /// Binding for `_super` function. + /// Initially `None`. Binding is created if `super()` is found in position other than top-level, + /// that requires a `_super` function. + super_binding: Option>, + ctx: &'c mut TraverseCtx<'a>, +} + +impl<'a, 'c> ConstructorBodyInitsInserter<'a, 'c> { + fn new(constructor_scope_id: ScopeId, ctx: &'c mut TraverseCtx<'a>) -> Self { + Self { constructor_scope_id, super_binding: None, ctx } + } + + fn insert(&mut self, body_stmts: &mut ArenaVec<'a, Statement<'a>>, inits: Vec>) { + // TODO: Re-parent child scopes of `init`s + let mut body_stmts_iter = body_stmts.iter_mut(); + let mut insert_index = 1; // 1 because want to insert after `super()`, not before + + // It's a runtime error (not a syntax error) for constructor of a class with a super class + // not to contain `super()`. + // So it's legal code and won't cause an error, as long as the class is never constructed! + // In reasonable code, we should never get to end of this loop without finding `super()`, + // but handle this weird case of no `super()` by allowing loop to exit. + // Inits will be inserted in a `_super` function which is never called. That is pointless, + // but exiting this function entirely would leave `Semantic` in an inconsistent state. + // What we get is completely legal output and correct `Semantic`, just longer than it could be. + // But this should never happen in practice, so no point writing special logic to handle it. + for stmt in body_stmts_iter.by_ref() { + // If statement is standalone `super()`, insert inits after `super()`. + // We can avoid a nested `_super` function for this common case. + if let Statement::ExpressionStatement(expr_stmt) = &*stmt { + if let Expression::CallExpression(call_expr) = &expr_stmt.expression { + if let Expression::Super(_) = &call_expr.callee { + body_stmts + .splice(insert_index..insert_index, exprs_into_stmts(inits, self.ctx)); + return; + } + } + } + + // Traverse statement looking for `super()` deeper in the statement + self.visit_statement(stmt); + if self.super_binding.is_some() { + break; + } + + insert_index += 1; + } + + // `super()` found in nested position. There may be more `super()`s in constructor. + // Convert them all to `_super()`. + for stmt in body_stmts_iter { + self.visit_statement(stmt); + } + + // Insert `_super` function at top of constructor + self.insert_super_func(body_stmts, inits); + } + + /// Insert `_super` function at top of constructor. + /// ```js + /// var _super = (..._args) => { + /// super(..._args); + /// + /// return this; + /// }; + /// ``` + fn insert_super_func( + &mut self, + stmts: &mut ArenaVec<'a, Statement<'a>>, + inits: Vec>, + ) { + let ctx = &mut *self.ctx; + + let super_func_scope_id = ctx.scopes_mut().add_scope( + Some(self.constructor_scope_id), + NodeId::DUMMY, + ScopeFlags::Function | ScopeFlags::Arrow | ScopeFlags::StrictMode, + ); + let args_binding = + ctx.generate_uid("args", super_func_scope_id, SymbolFlags::FunctionScopedVariable); + + // `super(..._args); ; return this;` + let super_call = create_super_call_stmt(&args_binding, ctx); + let return_stmt = ctx.ast.statement_return(SPAN, Some(ctx.ast.expression_this(SPAN))); + let body_stmts = ctx.ast.vec_from_iter( + [super_call].into_iter().chain(exprs_into_stmts(inits, ctx)).chain([return_stmt]), + ); + + // `(...args) => { super(..._args); ; return this; }` + let super_func = Expression::ArrowFunctionExpression( + ctx.ast.alloc_arrow_function_expression_with_scope_id( + SPAN, + false, + false, + NONE, + ctx.ast.alloc_formal_parameters( + SPAN, + FormalParameterKind::ArrowFormalParameters, + ctx.ast.vec(), + Some(ctx.ast.alloc_binding_rest_element( + SPAN, + args_binding.create_binding_pattern(ctx), + )), + ), + NONE, + ctx.ast.alloc_function_body(SPAN, ctx.ast.vec(), body_stmts), + super_func_scope_id, + ), + ); + + // `var _super = (...args) => { ... }` + // Note: `super_binding` can be `None` at this point if no `super()` found in constructor + // (see comment above in `insert`). + let super_binding = self + .super_binding + .get_or_insert_with(|| Self::generate_super_binding(self.constructor_scope_id, ctx)); + let super_func_decl = Statement::from(ctx.ast.declaration_variable( + SPAN, + VariableDeclarationKind::Var, + ctx.ast.vec1(ctx.ast.variable_declarator( + SPAN, + VariableDeclarationKind::Var, + super_binding.create_binding_pattern(ctx), + Some(super_func), + false, + )), + false, + )); + + stmts.insert(0, super_func_decl); + } +} + +impl<'a, 'c> VisitMut<'a> for ConstructorBodyInitsInserter<'a, 'c> { + /// Replace `super()` with `_super()`. + // `#[inline]` to make hot path for all other function calls as cheap as possible. + #[inline] + fn visit_call_expression(&mut self, call_expr: &mut CallExpression<'a>) { + if let Expression::Super(super_) = &call_expr.callee { + let span = super_.span; + self.replace_super(call_expr, span); + } + + walk_mut::walk_call_expression(self, call_expr); + } + + // Stop traversing where scope of current `super` ends + #[inline] + fn visit_function(&mut self, _func: &mut Function<'a>, _flags: ScopeFlags) {} + + #[inline] + fn visit_static_block(&mut self, _block: &mut StaticBlock) {} + + #[inline] + fn visit_property_definition(&mut self, prop: &mut PropertyDefinition<'a>) { + // `super()` in computed key of property or method refers to super binding of parent class. + // So visit computed `key`, but not `value`. + // ```js + // class Outer extends OuterSuper { + // constructor() { + // class Inner extends InnerSuper { + // [super().foo] = 1; // `super()` refers to `Outer`'s super class + // [super().bar]() {} // `super()` refers to `Outer`'s super class + // x = super(); // `super()` refers to `Inner`'s super class, but illegal syntax + // } + // } + // } + // ``` + // Don't visit `type_annotation` field because can't contain `super()`. + // TODO: Are decorators in scope? + self.visit_decorators(&mut prop.decorators); + if prop.computed { + self.visit_property_key(&mut prop.key); + } + } + + #[inline] + fn visit_accessor_property(&mut self, prop: &mut AccessorProperty<'a>) { + // Visit computed `key` but not `value`, for same reasons as `visit_property_definition` above. + // TODO: Are decorators in scope? + self.visit_decorators(&mut prop.decorators); + if prop.computed { + self.visit_property_key(&mut prop.key); + } + } +} + +impl<'a, 'c> ConstructorBodyInitsInserter<'a, 'c> { + fn replace_super(&mut self, call_expr: &mut CallExpression<'a>, span: Span) { + let super_binding = self.super_binding.get_or_insert_with(|| { + Self::generate_super_binding(self.constructor_scope_id, self.ctx) + }); + call_expr.callee = super_binding.create_spanned_read_expression(span, self.ctx); + } + + fn generate_super_binding( + constructor_scope_id: ScopeId, + ctx: &mut TraverseCtx<'a>, + ) -> BoundIdentifier<'a> { + ctx.generate_uid("super", constructor_scope_id, SymbolFlags::FunctionScopedVariable) + } +} + +/// `super(...args);` +fn create_super_call_stmt<'a>( + args_binding: &BoundIdentifier<'a>, + ctx: &mut TraverseCtx<'a>, +) -> Statement<'a> { + ctx.ast.statement_expression( + SPAN, + ctx.ast.expression_call( + SPAN, + ctx.ast.expression_super(SPAN), + NONE, + ctx.ast.vec1( + ctx.ast.argument_spread_element(SPAN, args_binding.create_read_expression(ctx)), + ), + false, + ), + ) +} diff --git a/crates/oxc_transformer/src/es2022/class_properties/mod.rs b/crates/oxc_transformer/src/es2022/class_properties/mod.rs new file mode 100644 index 00000000000000..35b2faf2c9cd1d --- /dev/null +++ b/crates/oxc_transformer/src/es2022/class_properties/mod.rs @@ -0,0 +1,296 @@ +//! ES2022: Class Properties +//! +//! This plugin transforms class properties to initializers inside class constructor. +//! +//! > This plugin is included in `preset-env`, in ES2022 +//! +//! ## Example +//! +//! Input: +//! ```js +//! class C { +//! foo = 123; +//! #bar = 456; +//! method() { +//! let bar = this.#bar; +//! this.#bar = bar + 1; +//! } +//! } +//! +//! let x = 123; +//! class D extends S { +//! foo = x; +//! constructor(x) { +//! if (x) { +//! let s = super(x); +//! } else { +//! super(x); +//! } +//! } +//! } +//! ``` +//! +//! Output: +//! ```js +//! var _bar = /*#__PURE__*/ new WeakMap(); +//! class C { +//! constructor() { +//! babelHelpers.defineProperty(this, "foo", 123); +//! babelHelpers.classPrivateFieldInitSpec(this, _bar, 456); +//! } +//! method() { +//! let bar = babelHelpers.classPrivateFieldGet2(_bar, this); +//! babelHelpers.classPrivateFieldSet2(_bar, this, bar + 1); +//! } +//! } +//! +//! let x = 123; +//! class D extends S { +//! constructor(_x) { +//! if (_x) { +//! let s = (super(_x), babelHelpers.defineProperty(this, "foo", x)); +//! } else { +//! super(_x); +//! babelHelpers.defineProperty(this, "foo", x); +//! } +//! } +//! } +//! ``` +//! +//! ## Implementation +//! +//! WORK IN PROGRESS. INCOMPLETE. +//! +//! ### Reference implementation +//! +//! Implementation based on [@babel/plugin-transform-class-properties](https://babel.dev/docs/babel-plugin-transform-class-properties). +//! +//! I (@overlookmotel) wrote this transform without reference to Babel's internal implementation, +//! but aiming to reproduce Babel's output, guided by Babel's test suite. +//! +//! ### Divergence from Babel +//! +//! In a few places, our implementation diverges from Babel, notably inserting property initializers +//! into constructor of a class with multiple `super()` calls (see comments in [`constructor`] module). +//! +//! ### High level overview +//! +//! Transform happens in 3 phases: +//! +//! 1. Check if class contains properties or static blocks, to determine if any transform is necessary +//! (in [`ClassProperties::transform_class`]). +//! 2. Extract class property declarations and static blocks from class and insert in class constructor +//! (instance properties) or before/after the class (static properties + static blocks) +//! (in [`ClassProperties::transform_class`]). +//! 3. Transform private property usages (`this.#prop`) +//! (in [`ClassProperties::transform_private_field_expression`] and other visitors). +//! +//! Implementation is split into several files: +//! +//! * `mod.rs`: Setup, visitor and ancillary types. +//! * `class.rs`: Transform of class body. +//! * `constructor.rs`: Insertion of property initializers into class constructor. +//! * `private.rs`: Transform of private property usages (`this.#prop`). +//! * `utils.rs`: Utility functions. +//! +//! ## References +//! +//! * Babel plugin implementation: +//! * +//! * +//! * +//! * Class properties TC39 proposal: + +use std::hash::BuildHasherDefault; + +use indexmap::IndexMap; +use rustc_hash::FxHasher; +use serde::Deserialize; + +use oxc_allocator::{Address, GetAddress}; +use oxc_ast::ast::*; +use oxc_data_structures::stack::{NonEmptyStack, SparseStack}; +use oxc_traverse::{BoundIdentifier, Traverse, TraverseCtx}; + +use crate::TransformCtx; + +mod class; +mod constructor; +mod private; +mod utils; + +type FxIndexMap = IndexMap>; + +#[derive(Debug, Default, Clone, Copy, Deserialize)] +#[serde(default, rename_all = "camelCase")] +pub struct ClassPropertiesOptions { + #[serde(alias = "loose")] + pub(crate) set_public_class_fields: bool, +} + +/// Class properties transform. +/// +/// See [module docs] for details. +/// +/// [module docs]: self +pub struct ClassProperties<'a, 'ctx> { + // Options + set_public_class_fields: bool, + static_block: bool, + + ctx: &'ctx TransformCtx<'a>, + + // State during whole AST + /// Stack of private props. + /// Pushed to when entering a class (`None` if class has no private props, `Some` if it does). + /// Entries are a mapping from private prop name to binding for temp var. + /// This is then used as lookup when transforming e.g. `this.#x`. + // TODO: The way stack is used is not perfect, because pushing to/popping from it in + // `enter_expression` / `exit_expression`. If another transform replaces the class, + // then stack will get out of sync. + // TODO: Should push to the stack only when entering class body, because `#x` in class `extends` + // clause resolves to `#x` in *outer* class, not the current class. + // TODO(improve-on-babel): Order that temp vars are created in is not important. Use `FxHashMap` instead. + private_props_stack: SparseStack>, + /// Addresses of class expressions being processed, to prevent same class being visited twice. + /// Have to use a stack because the revisit doesn't necessarily happen straight after the first visit. + /// e.g. `c = class C { [class D {}] = 1; }` -> `c = (_D = class D {}, class C { ... })` + class_expression_addresses_stack: NonEmptyStack
, + + // State during transform of class + /// `true` for class declaration, `false` for class expression + is_declaration: bool, + /// Var for class. + /// e.g. `X` in `class X {}`. + /// e.g. `_Class` in `_Class = class {}, _Class.x = 1, _Class` + class_name: ClassName<'a>, + /// Expressions to insert before class + insert_before: Vec>, + /// Expressions to insert after class expression + insert_after_exprs: Vec>, + /// Statements to insert after class declaration + insert_after_stmts: Vec>, +} + +/// Representation of binding for class name. +enum ClassName<'a> { + /// Class has a name. This is the binding. + Binding(BoundIdentifier<'a>), + /// Class is anonymous. + /// This is the name it would have if we need to set class name, in order to reference it. + Name(&'a str), +} + +/// Details of private properties for a class. +struct PrivateProps<'a> { + /// Private properties for class. Indexed by property name. + props: FxIndexMap, PrivateProp<'a>>, + /// Binding for class name + class_name_binding: Option>, + /// `true` for class declaration, `false` for class expression + is_declaration: bool, +} + +/// Details of a private property. +struct PrivateProp<'a> { + binding: BoundIdentifier<'a>, + is_static: bool, +} + +impl<'a, 'ctx> ClassProperties<'a, 'ctx> { + pub fn new( + options: ClassPropertiesOptions, + static_block: bool, + ctx: &'ctx TransformCtx<'a>, + ) -> Self { + // TODO: Raise error if these 2 options are inconsistent + let set_public_class_fields = + options.set_public_class_fields || ctx.assumptions.set_public_class_fields; + + Self { + set_public_class_fields, + static_block, + ctx, + private_props_stack: SparseStack::new(), + class_expression_addresses_stack: NonEmptyStack::new(Address::DUMMY), + // Temporary values - overwritten when entering class + is_declaration: false, + class_name: ClassName::Name(""), + // `Vec`s and `FxHashMap`s which are reused for every class being transformed + insert_before: vec![], + insert_after_exprs: vec![], + insert_after_stmts: vec![], + } + } +} + +impl<'a, 'ctx> Traverse<'a> for ClassProperties<'a, 'ctx> { + fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + // Note: `delete this.#prop` is an early syntax error, so no need to handle transforming it + match expr { + // `class {}` + Expression::ClassExpression(_) => { + self.transform_class_expression(expr, ctx); + } + // `object.#prop` + Expression::PrivateFieldExpression(_) => { + self.transform_private_field_expression(expr, ctx); + } + // `object.#prop()` + Expression::CallExpression(_) => { + self.transform_call_expression(expr, ctx); + } + // `object.#prop = value`, `object.#prop += value`, `object.#prop ??= value` etc + Expression::AssignmentExpression(_) => { + self.transform_assignment_expression(expr, ctx); + } + // `object.#prop++`, `--object.#prop` + Expression::UpdateExpression(_) => { + self.transform_update_expression(expr, ctx); + } + // `object?.#prop` + Expression::ChainExpression(_) => { + // TODO: `transform_chain_expression` is no-op at present + self.transform_chain_expression(expr, ctx); + } + // "object.#prop`xyz`" + Expression::TaggedTemplateExpression(_) => { + // TODO: `transform_tagged_template_expression` is no-op at present + self.transform_tagged_template_expression(expr, ctx); + } + // TODO: `[object.#prop] = value` + // TODO: `({x: object.#prop} = value)` + _ => {} + } + } + + fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { + match stmt { + // `class C {}` + Statement::ClassDeclaration(class) => { + let stmt_address = class.address(); + self.transform_class_declaration(class, stmt_address, ctx); + } + // `export class C {}` + Statement::ExportNamedDeclaration(decl) => { + let stmt_address = decl.address(); + if let Some(Declaration::ClassDeclaration(class)) = &mut decl.declaration { + self.transform_class_declaration(class, stmt_address, ctx); + } + } + // `export default class {}` + Statement::ExportDefaultDeclaration(decl) => { + let stmt_address = decl.address(); + if let ExportDefaultDeclarationKind::ClassDeclaration(class) = &mut decl.declaration + { + self.transform_class_export_default(class, stmt_address, ctx); + } + } + _ => {} + } + } + + fn exit_class(&mut self, class: &mut Class<'a>, _ctx: &mut TraverseCtx<'a>) { + self.transform_class_on_exit(class); + } +} diff --git a/crates/oxc_transformer/src/es2022/class_properties/private.rs b/crates/oxc_transformer/src/es2022/class_properties/private.rs new file mode 100644 index 00000000000000..6f0a9d6a47e702 --- /dev/null +++ b/crates/oxc_transformer/src/es2022/class_properties/private.rs @@ -0,0 +1,984 @@ +//! ES2022: Class Properties +//! Transform of private property uses e.g. `this.#prop`. + +use std::mem; + +use oxc_allocator::Box as ArenaBox; +use oxc_ast::ast::*; +use oxc_span::SPAN; +use oxc_syntax::{ + reference::{ReferenceFlags, ReferenceId}, + symbol::SymbolFlags, +}; +use oxc_traverse::{ast_operations::get_var_name_from_node, BoundIdentifier, TraverseCtx}; + +use crate::common::helper_loader::Helper; + +use super::{ + utils::{create_assignment, create_underscore_ident_name}, + ClassProperties, PrivateProp, +}; + +impl<'a, 'ctx> ClassProperties<'a, 'ctx> { + /// Transform private field expression. + /// + /// Instance prop: `object.#prop` -> `_classPrivateFieldGet2(_prop, object)` + /// Static prop: `object.#prop` -> `_assertClassBrand(Class, object, _prop)._` + // + // `#[inline]` so that compiler sees that `expr` is an `Expression::PrivateFieldExpression`. + #[inline] + pub(super) fn transform_private_field_expression( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + let owned_expr = ctx.ast.move_expression(expr); + let Expression::PrivateFieldExpression(field_expr) = owned_expr else { unreachable!() }; + *expr = self.transform_private_field_expression_impl(field_expr, ctx); + } + + fn transform_private_field_expression_impl( + &mut self, + field_expr: ArenaBox<'a, PrivateFieldExpression<'a>>, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + let prop_details = self.lookup_private_property(&field_expr.field); + // TODO: Should never be `None` - only because implementation is incomplete. + let Some((prop, class_name_binding, is_declaration)) = prop_details else { + return Expression::PrivateFieldExpression(field_expr); + }; + let prop_ident = prop.binding.create_read_expression(ctx); + + // TODO: Move this to top of function once `lookup_private_property` does not return `Option` + let PrivateFieldExpression { span, object, .. } = field_expr.unbox(); + + if prop.is_static { + // TODO: Ensure there are tests for nested classes with references to private static props + // of outer class inside inner class, to make sure we're getting the right `class_name_binding`. + let class_name_binding = class_name_binding.as_ref().unwrap(); + + // If `object` is reference to class name, there's no need for the class brand assertion + if let Some(reference_id) = + Self::shortcut_static_class(is_declaration, class_name_binding, &object, ctx) + { + // `_prop._` + ctx.symbols_mut() + .delete_resolved_reference(class_name_binding.symbol_id, reference_id); + Self::create_underscore_member_expression(prop_ident, span, ctx) + } else { + // `_assertClassBrand(Class, object, _prop)._` + self.create_assert_class_brand_underscore( + class_name_binding.create_read_expression(ctx), + object, + prop_ident, + span, + ctx, + ) + } + } else { + // `_classPrivateFieldGet2(_prop, object)` + self.create_private_field_get(prop_ident, object, span, ctx) + } + } + + /// Check if can use shorter version of static private prop transform. + /// + /// Can if both: + /// 1. Class is a declaration, not an expression. + /// 2. `object` is an `IdentifierReference` referring to class name binding. + /// + /// If can use shorter version, returns `ReferenceId` of the `IdentifierReference`. + // + // TODO(improve-on-babel): No reason not to use the short version for class expressions too. + // TODO: Take `SymbolId` instead of `class_name_binding: &BoundIdentifier<'a>`? + fn shortcut_static_class( + is_declaration: bool, + class_name_binding: &BoundIdentifier<'a>, + object: &Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Option { + if is_declaration { + if let Expression::Identifier(ident) = object { + let reference_id = ident.reference_id(); + if let Some(symbol_id) = ctx.symbols().get_reference(reference_id).symbol_id() { + if symbol_id == class_name_binding.symbol_id { + return Some(reference_id); + } + } + } + } + + None + } + + /// Transform call expression where callee is private field. + /// + /// Instance prop: `object.#prop(arg)` -> `_classPrivateFieldGet2(_prop, object).call(object, arg)` + /// Static prop: `object.#prop(arg)` -> `_assertClassBrand(Class, object, _prop)._.call(object, arg)` + /// + /// Output in both cases contains a `CallExpression`, so mutate existing `CallExpression` + /// rather than creating a new one. + // + // `#[inline]` so that compiler sees that `expr` is an `Expression::CallExpression` + #[inline] + pub(super) fn transform_call_expression( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + let Expression::CallExpression(call_expr) = expr else { unreachable!() }; + if matches!(&call_expr.callee, Expression::PrivateFieldExpression(_)) { + self.transform_call_expression_impl(expr, ctx); + }; + } + + fn transform_call_expression_impl( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + // Unfortunately no way to make compiler see that these branches are provably unreachable. + // This function is much too large inline, because `transform_static_assignment_expression` + // and `transform_instance_assignment_expression` are inlined into it. + let Expression::CallExpression(call_expr) = expr else { unreachable!() }; + let Expression::PrivateFieldExpression(field_expr) = &mut call_expr.callee else { + unreachable!() + }; + let prop_details = self.lookup_private_property(&field_expr.field); + // TODO: Should never be `None` - only because implementation is incomplete. + let Some((prop, class_name_binding, is_declaration)) = prop_details else { return }; + let prop_ident = prop.binding.create_read_expression(ctx); + + let object = ctx.ast.move_expression(&mut field_expr.object); + + // Get replacement for callee + let (callee, object) = if prop.is_static { + // TODO: If `object` is reference to class name, and class is declaration, use shortcut `_prop._.call(Class)`. + // TODO(improve-on-babel): No reason not to apply these shortcuts for class expressions too. + + // `object.#prop(arg)` -> `_assertClassBrand(Class, object, _prop)._.call(object, arg)` + // or shortcut `_prop._.call(object, arg)` + let class_name_binding = class_name_binding.as_ref().unwrap(); + let class_ident = class_name_binding.create_read_expression(ctx); + + // If `object` is reference to class name, there's no need for the class brand assertion + // TODO: Combine this check with `duplicate_object`. Both check if `object` is an identifier, + // and look up the `SymbolId` + if Self::shortcut_static_class(is_declaration, class_name_binding, &object, ctx) + .is_some() + { + // `_prop._` + let callee = + Self::create_underscore_member_expression(prop_ident, field_expr.span, ctx); + (callee, object) + } else { + // Make 2 copies of `object` + let (object1, object2) = self.duplicate_object(object, ctx); + + // `_assertClassBrand(Class, object, _prop)._` + // TODO: Ensure there are tests for nested classes with references to private static props + // of outer class inside inner class, to make sure we're getting the right `class_name_binding`. + let assert_obj = self.create_assert_class_brand_underscore( + class_ident, + object1, + prop_ident, + field_expr.span, + ctx, + ); + (assert_obj, object2) + } + } else { + // `object.#prop(arg)` -> `_classPrivateFieldGet2(_prop, object).call(object, arg)` + // Make 2 copies of `object` + let (object1, object2) = self.duplicate_object(object, ctx); + + // `_classPrivateFieldGet2(_prop, object)` + let get_call = self.create_private_field_get(prop_ident, object1, field_expr.span, ctx); + (get_call, object2) + }; + + // Substitute `.call` as callee of call expression + call_expr.callee = Expression::from(ctx.ast.member_expression_static( + SPAN, + callee, + ctx.ast.identifier_name(SPAN, Atom::from("call")), + false, + )); + // Add `object` to call arguments + call_expr.arguments.insert(0, Argument::from(object)); + } + + /// Transform assignment to private field. + // + // `#[inline]` so that compiler sees that `expr` is an `Expression::AssignmentExpression` + #[inline] + pub(super) fn transform_assignment_expression( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + let Expression::AssignmentExpression(assign_expr) = expr else { unreachable!() }; + if matches!(&assign_expr.left, AssignmentTarget::PrivateFieldExpression(_)) { + self.transform_assignment_expression_impl(expr, ctx); + }; + } + + fn transform_assignment_expression_impl( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + // Unfortunately no way to make compiler see that these branches are provably unreachable. + // This function is much too large inline, because `transform_static_assignment_expression` + // and `transform_instance_assignment_expression` are inlined into it. + let Expression::AssignmentExpression(assign_expr) = expr else { unreachable!() }; + let AssignmentTarget::PrivateFieldExpression(field_expr) = &mut assign_expr.left else { + unreachable!() + }; + + let prop_details = self.lookup_private_property(&field_expr.field); + // TODO: Should never be `None` - only because implementation is incomplete. + let Some((prop, class_name_binding, is_declaration)) = prop_details else { return }; + + // Note: `transform_static_assignment_expression` and `transform_instance_assignment_expression` + // are marked `#[inline]`, so hopefully compiler will see these clones of `BoundIdentifier`s + // can be elided. + // Can't break this up into separate functions otherwise, as `&BoundIdentifier`s keep `&self` ref + // taken by `lookup_private_property` alive. + let prop_binding = prop.binding.clone(); + + if prop.is_static { + let class_name_binding = class_name_binding.as_ref().unwrap().clone(); + self.transform_static_assignment_expression( + expr, + prop_binding, + class_name_binding, + is_declaration, + ctx, + ); + } else { + self.transform_instance_assignment_expression(expr, prop_binding, ctx); + } + } + + /// Transform assignment expression with static private prop as assignee. + /// + /// * `object.#prop = value` + /// -> `_prop._ = _assertClassBrand(Class, object, value)` + /// * `object.#prop += value` + /// -> `_prop._ = _assertClassBrand(Class, object, _assertClassBrand(Class, object, _prop)._ + value)` + /// * `object.#prop &&= value` + /// -> `_assertClassBrand(Class, object, _prop)._ && (_prop._ = _assertClassBrand(Class, object, value))` + /// + /// Output in all cases contains an `AssignmentExpression`, so mutate existing `AssignmentExpression` + /// rather than creating a new one. + // + // `#[inline]` so that compiler sees `expr` is an `Expression::AssignmentExpression` with + // `AssignmentTarget::PrivateFieldExpression` on left, and that clones in + // `transform_assignment_expression` can be elided. + #[inline] + #[expect(clippy::needless_pass_by_value)] + fn transform_static_assignment_expression( + &mut self, + expr: &mut Expression<'a>, + prop_binding: BoundIdentifier<'a>, + class_name_binding: BoundIdentifier<'a>, + is_declaration: bool, + ctx: &mut TraverseCtx<'a>, + ) { + let Expression::AssignmentExpression(assign_expr) = expr else { unreachable!() }; + let operator = assign_expr.operator; + let AssignmentTarget::PrivateFieldExpression(field_expr) = &mut assign_expr.left else { + unreachable!() + }; + + // Check if object (`object` in `object.#prop`) is a reference to class name + // TODO: Combine this check with `duplicate_object`. Both check if `object` is an identifier, + // and look up the `SymbolId`. + let object_reference_id = Self::shortcut_static_class( + is_declaration, + &class_name_binding, + &field_expr.object, + ctx, + ); + + // If `object` is reference to class name, there's no need for the class brand assertion. + // `Class.#prop = value` -> `_prop._ = value` + // `Class.#prop += value` -> `_prop._ = _prop._ + value` + // `Class.#prop &&= value` -> `_prop._ && (_prop._ = 1)` + // TODO(improve-on-babel): These shortcuts could be shorter - just swap `Class.#prop` for `_prop._`. + // Or does that behave slightly differently if `Class.#prop` is an object with `valueOf` method? + if let Some(reference_id) = object_reference_id { + // Replace left side of assignment with `_prop._` + let field_expr_span = field_expr.span; + assign_expr.left = Self::create_underscore_member_expr_target( + prop_binding.create_read_expression(ctx), + field_expr_span, + ctx, + ); + + // Delete reference for `object` as `object.#prop` has been removed + ctx.symbols_mut().delete_resolved_reference(class_name_binding.symbol_id, reference_id); + + if operator == AssignmentOperator::Assign { + // `Class.#prop = value` -> `_prop._ = value` + // Left side already replaced with `_prop._`. Nothing further to do. + } else { + let prop_obj = Self::create_underscore_member_expression( + prop_binding.create_read_expression(ctx), + field_expr_span, + ctx, + ); + + if let Some(operator) = operator.to_binary_operator() { + // `Class.#prop += value` -> `_prop._ = _prop._ + value` + let value = ctx.ast.move_expression(&mut assign_expr.right); + assign_expr.operator = AssignmentOperator::Assign; + assign_expr.right = ctx.ast.expression_binary(SPAN, prop_obj, operator, value); + } else if let Some(operator) = operator.to_logical_operator() { + // `Class.#prop &&= value` -> `_prop._ && (_prop._ = 1)` + let span = assign_expr.span; + assign_expr.span = SPAN; + assign_expr.operator = AssignmentOperator::Assign; + let right = ctx.ast.move_expression(expr); + *expr = ctx.ast.expression_logical(span, prop_obj, operator, right); + } else { + // The above covers all types of `AssignmentOperator` + unreachable!(); + } + } + } else { + // Substitute left side of assignment with `_prop._`, and get owned `object` from old left side + let assignee = Self::create_underscore_member_expr_target( + prop_binding.create_read_expression(ctx), + SPAN, + ctx, + ); + let old_assignee = mem::replace(&mut assign_expr.left, assignee); + let field_expr = match old_assignee { + AssignmentTarget::PrivateFieldExpression(field_expr) => field_expr.unbox(), + _ => unreachable!(), + }; + let object = field_expr.object; + + let class_ident = class_name_binding.create_read_expression(ctx); + let value = ctx.ast.move_expression(&mut assign_expr.right); + + if operator == AssignmentOperator::Assign { + // Replace right side of assignment with `_assertClassBrand(Class, object, _prop)` + // TODO: Ensure there are tests for nested classes with references to private static props + // of outer class inside inner class, to make sure we're getting the right `class_name_binding`. + assign_expr.right = self.create_assert_class_brand(class_ident, object, value, ctx); + } else { + let class_ident = class_name_binding.create_read_expression(ctx); + let value = ctx.ast.move_expression(&mut assign_expr.right); + + // Make 2 copies of `object` + let (object1, object2) = self.duplicate_object(object, ctx); + + let prop_ident = prop_binding.create_read_expression(ctx); + let class_ident2 = class_name_binding.create_read_expression(ctx); + + if let Some(operator) = operator.to_binary_operator() { + // `object.#prop += value` + // -> `_prop._ = _assertClassBrand(Class, object, _assertClassBrand(Class, object, _prop)._ + value)` + + // `_assertClassBrand(Class, object, _prop)._` + let get_expr = self.create_assert_class_brand_underscore( + class_ident, + object2, + prop_ident, + SPAN, + ctx, + ); + // `_assertClassBrand(Class, object, _prop)._ + value` + let value = ctx.ast.expression_binary(SPAN, get_expr, operator, value); + // `_assertClassBrand(Class, object, _assertClassBrand(Class, object, _prop)._ + value)` + assign_expr.right = + self.create_assert_class_brand(class_ident2, object1, value, ctx); + } else if let Some(operator) = operator.to_logical_operator() { + // `object.#prop &&= value` + // -> `_assertClassBrand(Class, object, _prop)._ && (_prop._ = _assertClassBrand(Class, object, value))` + + // `_assertClassBrand(Class, object, _prop)._` + let left = self.create_assert_class_brand_underscore( + class_ident, + object1, + prop_ident, + SPAN, + ctx, + ); + // Mutate existing assignment expression to `_prop._ = _assertClassBrand(Class, object, value)` + // and take ownership of it + let span = assign_expr.span; + assign_expr.span = SPAN; + assign_expr.operator = AssignmentOperator::Assign; + assign_expr.right = + self.create_assert_class_brand(class_ident2, object2, value, ctx); + let right = ctx.ast.move_expression(expr); + // `_assertClassBrand(Class, object, _prop)._ && (_prop._ = _assertClassBrand(Class, object, value))` + *expr = ctx.ast.expression_logical(span, left, operator, right); + } else { + // The above covers all types of `AssignmentOperator` + unreachable!(); + } + } + } + } + + /// Transform assignment expression with instance private prop as assignee. + /// + /// * `object.#prop = value` + /// -> `_classPrivateFieldSet2(_prop, object, value)` + /// * `object.#prop += value` + /// -> `_classPrivateFieldSet2(_prop, object, _classPrivateFieldGet2(_prop, object) + value)` + /// * `object.#prop &&= value` + /// -> `_classPrivateFieldGet2(_prop, object) && _classPrivateFieldSet2(_prop, object, value)` + /// + /// Output in all cases contains an `AssignmentExpression`, so mutate existing `AssignmentExpression` + /// rather than creating a new one. + // + // `#[inline]` so that compiler sees `expr` is an `Expression::AssignmentExpression` with + // `AssignmentTarget::PrivateFieldExpression` on left, and that clones in + // `transform_assignment_expression` can be elided. + #[inline] + #[expect(clippy::needless_pass_by_value)] + fn transform_instance_assignment_expression( + &mut self, + expr: &mut Expression<'a>, + prop_binding: BoundIdentifier<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + let assign_expr = match ctx.ast.move_expression(expr) { + Expression::AssignmentExpression(assign_expr) => assign_expr.unbox(), + _ => unreachable!(), + }; + let AssignmentExpression { span, operator, right: value, left } = assign_expr; + let object = match left { + AssignmentTarget::PrivateFieldExpression(field_expr) => field_expr.unbox().object, + _ => unreachable!(), + }; + + let prop_ident = prop_binding.create_read_expression(ctx); + + // TODO: Different output in for statements e.g. `private/1-helpermemberexpressionfunction/input.js` + + if operator == AssignmentOperator::Assign { + // `object.#prop = value` -> `_classPrivateFieldSet2(_prop, object, value)` + *expr = self.create_private_field_set(prop_ident, object, value, span, ctx); + } else { + // Make 2 copies of `object` + let (object1, object2) = self.duplicate_object(object, ctx); + + let prop_ident2 = prop_binding.create_read_expression(ctx); + + if let Some(operator) = operator.to_binary_operator() { + // `object.#prop += value` + // -> `_classPrivateFieldSet2(_prop, object, _classPrivateFieldGet2(_prop, object) + value)` + + // `_classPrivateFieldGet2(_prop, object)` + let get_call = self.create_private_field_get(prop_ident, object2, SPAN, ctx); + + // `_classPrivateFieldGet2(_prop, object) + value` + let value = ctx.ast.expression_binary(SPAN, get_call, operator, value); + + // `_classPrivateFieldSet2(_prop, object, _classPrivateFieldGet2(_prop, object) + value)` + *expr = self.create_private_field_set(prop_ident2, object1, value, span, ctx); + } else if let Some(operator) = operator.to_logical_operator() { + // `object.#prop &&= value` + // -> `_classPrivateFieldGet2(_prop, object) && _classPrivateFieldSet2(_prop, object, value)` + + // `_classPrivateFieldGet2(_prop, object)` + let get_call = self.create_private_field_get(prop_ident, object1, SPAN, ctx); + + // `_classPrivateFieldSet2(_prop, object, value)` + let set_call = + self.create_private_field_set(prop_ident2, object2, value, SPAN, ctx); + // `_classPrivateFieldGet2(_prop, object) && _classPrivateFieldSet2(_prop, object, value)` + *expr = ctx.ast.expression_logical(span, get_call, operator, set_call); + } else { + // The above covers all types of `AssignmentOperator` + unreachable!(); + } + } + } + + /// Transform update expression (`++` or `--`) where argument is private field. + /// + /// Instance prop: + /// + /// * `++object.#prop` -> + /// ```js + /// _classPrivateFieldSet( + /// _prop, object, + /// (_object$prop = _classPrivateFieldGet(_prop, object), ++_object$prop) + /// ), + /// ``` + /// + /// * `object.#prop++` -> + /// ```js + /// ( + /// _classPrivateFieldSet( + /// _prop, object, + /// ( + /// _object$prop = _classPrivateFieldGet(_prop, object), + /// _object$prop2 = _object$prop++, + /// _object$prop + /// ) + /// ), + /// _object$prop2 + /// ) + /// ``` + /// + /// Static prop: + /// + /// * `++object.#prop++` -> + /// ```js + /// _prop._ = _assertClassBrand( + /// Class, object, + /// (_object$prop = _assertClassBrand(Class, object, _prop)._, ++_object$prop) + /// ) + /// ``` + /// + /// * `object.#prop++` -> + /// ```js + /// ( + /// _prop._ = _assertClassBrand( + /// Class, object, + /// ( + /// _object$prop = _assertClassBrand(Class, object, _prop)._, + /// _object$prop2 = _object$prop++, + /// _object$prop + /// ) + /// ), + /// _object$prop2 + /// ) + /// ``` + /// + /// Output in all cases contains an `UpdateExpression`, so mutate existing `UpdateExpression` + /// rather than creating a new one. + // + // `#[inline]` so that compiler sees that `expr` is an `Expression::UpdateExpression` + #[inline] + pub(super) fn transform_update_expression( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + let Expression::UpdateExpression(update_expr) = expr else { unreachable!() }; + if matches!(&update_expr.argument, SimpleAssignmentTarget::PrivateFieldExpression(_)) { + self.transform_update_expression_impl(expr, ctx); + }; + } + + // TODO: Split up this function into 2 halves for static and instance props + fn transform_update_expression_impl( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + // Unfortunately no way to make compiler see that these branches are provably unreachable. + // This function is much too large inline, because `transform_static_assignment_expression` + // and `transform_instance_assignment_expression` are inlined into it. + let Expression::UpdateExpression(update_expr) = expr else { unreachable!() }; + let field_expr = match &mut update_expr.argument { + SimpleAssignmentTarget::PrivateFieldExpression(field_expr) => field_expr.as_mut(), + _ => unreachable!(), + }; + + let prop_details = self.lookup_private_property(&field_expr.field); + // TODO: Should never be `None` - only because implementation is incomplete. + let Some((prop, class_name_binding, is_declaration)) = prop_details else { return }; + let prop_ident = prop.binding.create_read_expression(ctx); + let prop_ident2 = prop.binding.create_read_expression(ctx); + + let temp_var_name_base = get_var_name_from_node(field_expr); + let temp_binding = ctx.generate_uid_in_current_scope( + &temp_var_name_base, + SymbolFlags::FunctionScopedVariable, + ); + + // TODO(improve-on-babel): Could avoid `move_expression` here and replace `update_expr.argument` instead. + // Only doing this first to match the order Babel creates temp vars. + let object = ctx.ast.move_expression(&mut field_expr.object); + + if prop.is_static { + // TODO: If `object` is reference to class name, and class is declaration, use shortcuts: + // `++Class.#prop` -> `_prop._ = ((_Class$prop = _prop._), ++_Class$prop)` + // `Class.#prop++` -> `_prop._ = (_Class$prop = _prop._, _Class$prop2 = _Class$prop++, _Class$prop), _Class$prop2` + // TODO(improve-on-babel): These shortcuts could be shorter - just `_prop._++` / `++_prop._`. + // Or does that behave slightly differently if `Class.#prop` is an object with `valueOf` method? + // TODO(improve-on-babel): No reason not to apply these shortcuts for class expressions too. + + // ``` + // _prop._ = _assertClassBrand( + // Class, object, + // (_object$prop = _assertClassBrand(Class, object, _prop)._, ++_object$prop) + // ) + // ``` + let class_name_binding = class_name_binding.as_ref().unwrap().clone(); + + // Check if object (`object` in `object.#prop`) is a reference to class name + // TODO: Combine this check with `duplicate_object`. Both check if `object` is an identifier, + // and look up the `SymbolId`. + let object_reference_id = + Self::shortcut_static_class(is_declaration, &class_name_binding, &object, ctx); + + // `_assertClassBrand(Class, object, _prop)._` or `_prop._` + let (get_expr, object) = if let Some(reference_id) = object_reference_id { + // Delete reference for `object` as `object.#prop` is being removed + ctx.symbols_mut() + .delete_resolved_reference(class_name_binding.symbol_id, reference_id); + + // `_prop._` + let get_expr = Self::create_underscore_member_expression(prop_ident, SPAN, ctx); + (get_expr, object) + } else { + // Make 2 copies of `object` + let (object1, object2) = self.duplicate_object(object, ctx); + + // `_assertClassBrand(Class, object, _prop)._` + let get_call = self.create_assert_class_brand_underscore( + class_name_binding.create_read_expression(ctx), + object2, + prop_ident, + SPAN, + ctx, + ); + (get_call, object1) + }; + // `_object$prop = _assertClassBrand(Class, object, _prop)._` + self.ctx.var_declarations.insert_var(&temp_binding, None, ctx); + let assignment = create_assignment(&temp_binding, get_expr, ctx); + + // `++_object$prop` / `_object$prop++` (reusing existing `UpdateExpression`) + let UpdateExpression { span, prefix, .. } = **update_expr; + update_expr.span = SPAN; + update_expr.argument = temp_binding.create_read_write_simple_target(ctx); + let update_expr = ctx.ast.move_expression(expr); + + if prefix { + // Source = `++object.#prop` (prefix `++`) + + // `(_object$prop = _assertClassBrand(Class, object, _prop)._, ++_object$prop)` + let mut value = ctx + .ast + .expression_sequence(SPAN, ctx.ast.vec_from_array([assignment, update_expr])); + + // `_assertClassBrand(Class, object, )` + if object_reference_id.is_none() { + let class_ident = class_name_binding.create_read_expression(ctx); + value = self.create_assert_class_brand(class_ident, object, value, ctx); + } + + // `_prop._ = ` + *expr = ctx.ast.expression_assignment( + span, + AssignmentOperator::Assign, + Self::create_underscore_member_expr_target(prop_ident2, SPAN, ctx), + value, + ); + } else { + // Source = `object.#prop++` (postfix `++`) + + // `_object$prop2 = _object$prop++` + let temp_binding2 = ctx.generate_uid_in_current_scope( + &temp_var_name_base, + SymbolFlags::FunctionScopedVariable, + ); + self.ctx.var_declarations.insert_var(&temp_binding2, None, ctx); + let assignment2 = create_assignment(&temp_binding2, update_expr, ctx); + + // `(_object$prop = _assertClassBrand(Class, object, _prop)._, _object$prop2 = _object$prop++, _object$prop)` + let mut value = ctx.ast.expression_sequence( + SPAN, + ctx.ast.vec_from_array([ + assignment, + assignment2, + temp_binding.create_read_expression(ctx), + ]), + ); + + // `_assertClassBrand(Class, object, )` + if object_reference_id.is_none() { + let class_ident = class_name_binding.create_read_expression(ctx); + value = self.create_assert_class_brand(class_ident, object, value, ctx); + } + + // `_prop._ = ` + let assignment3 = ctx.ast.expression_assignment( + SPAN, + AssignmentOperator::Assign, + Self::create_underscore_member_expr_target(prop_ident2, SPAN, ctx), + value, + ); + + // `(_prop._ = , _object$prop2)` + // TODO(improve-on-babel): Final `_object$prop2` is only needed if this expression + // is consumed (i.e. not in an `ExpressionStatement`) + *expr = ctx.ast.expression_sequence( + span, + ctx.ast + .vec_from_array([assignment3, temp_binding2.create_read_expression(ctx)]), + ); + } + } else { + // Make 2 copies of `object` + let (object1, object2) = self.duplicate_object(object, ctx); + + // `_classPrivateFieldGet(_prop, object)` + let get_call = self.create_private_field_get(prop_ident, object2, SPAN, ctx); + + // `_object$prop = _classPrivateFieldGet(_prop, object)` + self.ctx.var_declarations.insert_var(&temp_binding, None, ctx); + let assignment = create_assignment(&temp_binding, get_call, ctx); + + // `++_object$prop` / `_object$prop++` (reusing existing `UpdateExpression`) + let UpdateExpression { span, prefix, .. } = **update_expr; + update_expr.span = SPAN; + update_expr.argument = temp_binding.create_read_write_simple_target(ctx); + let update_expr = ctx.ast.move_expression(expr); + + if prefix { + // Source = `++object.#prop` (prefix `++`) + // `(_object$prop = _classPrivateFieldGet(_prop, object), ++_object$prop)` + let value = ctx + .ast + .expression_sequence(SPAN, ctx.ast.vec_from_array([assignment, update_expr])); + // `_classPrivateFieldSet(_prop, object, )` + *expr = self.create_private_field_set(prop_ident2, object1, value, span, ctx); + } else { + // Source = `object.#prop++` (postfix `++`) + // `_object$prop2 = _object$prop++` + let temp_binding2 = ctx.generate_uid_in_current_scope( + &temp_var_name_base, + SymbolFlags::FunctionScopedVariable, + ); + self.ctx.var_declarations.insert_var(&temp_binding2, None, ctx); + let assignment2 = create_assignment(&temp_binding2, update_expr, ctx); + + // `(_object$prop = _classPrivateFieldGet(_prop, object), _object$prop2 = _object$prop++, _object$prop)` + let value = ctx.ast.expression_sequence( + SPAN, + ctx.ast.vec_from_array([ + assignment, + assignment2, + temp_binding.create_read_expression(ctx), + ]), + ); + + // `_classPrivateFieldSet(_prop, object, )` + let set_call = + self.create_private_field_set(prop_ident2, object1, value, span, ctx); + // `(_classPrivateFieldSet(_prop, object, ), _object$prop2)` + // TODO(improve-on-babel): Final `_object$prop2` is only needed if this expression + // is consumed (i.e. not in an `ExpressionStatement`) + *expr = ctx.ast.expression_sequence( + span, + ctx.ast.vec_from_array([set_call, temp_binding2.create_read_expression(ctx)]), + ); + } + } + } + + /// Transform chain expression where includes a private field. + // + // `#[inline]` so that compiler sees that `expr` is an `Expression::ChainExpression` + #[inline] + #[expect(clippy::unused_self)] + pub(super) fn transform_chain_expression( + &mut self, + expr: &mut Expression<'a>, + _ctx: &mut TraverseCtx<'a>, + ) { + let Expression::ChainExpression(_chain_expr) = expr else { unreachable!() }; + + // TODO: `object?.#prop` + // TODO: `object?.#prop()` + } + + /// Transform tagged template expression where tag is a private field. + /// + /// "object.#prop`xyz`" -> "_classPrivateFieldGet(_prop, object).bind(object)`xyz`" + // + // `#[inline]` so that compiler sees that `expr` is an `Expression::TaggedTemplateExpression` + #[inline] + #[expect(clippy::unused_self)] + pub(super) fn transform_tagged_template_expression( + &mut self, + expr: &mut Expression<'a>, + _ctx: &mut TraverseCtx<'a>, + ) { + let Expression::TaggedTemplateExpression(_tagged_temp_expr) = expr else { unreachable!() }; + + // TODO: "object.#prop`xyz`" + // See `private/tagged-template` fixture. + } + + /// Duplicate object to be used in get/set pair. + /// + /// If `object` may have side effects, create a temp var `_object` and assign to it. + /// + /// * `this` -> (`this`, `this`) + /// * Bound identifier `object` -> `object`, `object` + /// * Unbound identifier `object` -> `_object = object`, `_object` + /// * Anything else `foo()` -> `_foo = foo()`, `_foo` + /// + /// Returns 2 `Expression`s. The first must be inserted into output first. + fn duplicate_object( + &mut self, + object: Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> (Expression<'a>, Expression<'a>) { + // TODO: Handle if in a function's params + let temp_var_binding = match &object { + Expression::Identifier(ident) => { + let reference = ctx.symbols_mut().get_reference_mut(ident.reference_id()); + if let Some(symbol_id) = reference.symbol_id() { + // Reading bound identifier cannot have side effects, so no need for temp var + let binding = BoundIdentifier::new(ident.name.clone(), symbol_id); + let object1 = binding.create_spanned_read_expression(ident.span, ctx); + return (object1, object); + } + + // Previously `x += 1` (`x` read + write), but moving to `_x = x` (`x` read only) + *reference.flags_mut() = ReferenceFlags::Read; + + ctx.generate_uid_in_current_scope(&ident.name, SymbolFlags::FunctionScopedVariable) + } + Expression::ThisExpression(this) => { + // Reading `this` cannot have side effects, so no need for temp var + let object1 = ctx.ast.expression_this(this.span); + return (object1, object); + } + _ => ctx.generate_uid_in_current_scope_based_on_node( + &object, + SymbolFlags::FunctionScopedVariable, + ), + }; + + self.ctx.var_declarations.insert_var(&temp_var_binding, None, ctx); + + let object1 = create_assignment(&temp_var_binding, object, ctx); + let object2 = temp_var_binding.create_read_expression(ctx); + + (object1, object2) + } + + /// `_classPrivateFieldGet2(_prop, object)` + fn create_private_field_get( + &self, + prop_ident: Expression<'a>, + object: Expression<'a>, + span: Span, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + self.ctx.helper_call_expr( + Helper::ClassPrivateFieldGet2, + span, + ctx.ast.vec_from_array([Argument::from(prop_ident), Argument::from(object)]), + ctx, + ) + } + + /// `_classPrivateFieldSet2(_prop, object, value)` + fn create_private_field_set( + &self, + prop_ident: Expression<'a>, + object: Expression<'a>, + value: Expression<'a>, + span: Span, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + self.ctx.helper_call_expr( + Helper::ClassPrivateFieldSet2, + span, + ctx.ast.vec_from_array([ + Argument::from(prop_ident), + Argument::from(object), + Argument::from(value), + ]), + ctx, + ) + } + + /// `_assertClassBrand(Class, object, value)` or `_assertClassBrand(Class, object, _prop)` + fn create_assert_class_brand( + &self, + class_ident: Expression<'a>, + object: Expression<'a>, + value_or_prop_ident: Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + self.ctx.helper_call_expr( + Helper::AssertClassBrand, + SPAN, + ctx.ast.vec_from_array([ + Argument::from(class_ident), + Argument::from(object), + Argument::from(value_or_prop_ident), + ]), + ctx, + ) + } + + /// `_assertClassBrand(Class, object, _prop)._` + fn create_assert_class_brand_underscore( + &self, + class_ident: Expression<'a>, + object: Expression<'a>, + prop_ident: Expression<'a>, + span: Span, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + let func_call = self.create_assert_class_brand(class_ident, object, prop_ident, ctx); + Self::create_underscore_member_expression(func_call, span, ctx) + } + + /// Lookup details of private property referred to by `ident`. + fn lookup_private_property( + &self, + ident: &PrivateIdentifier<'a>, + ) -> Option<(&PrivateProp<'a>, &Option>, /* is_declaration */ bool)> { + // Check for binding in closest class first, then enclosing classes + // TODO: Check there are tests for bindings in enclosing classes. + for private_props in self.private_props_stack.as_slice().iter().rev() { + if let Some(prop) = private_props.props.get(&ident.name) { + return Some(( + prop, + &private_props.class_name_binding, + private_props.is_declaration, + )); + } + } + // TODO: This should be unreachable. Only returning `None` because implementation is incomplete. + None + } + + /// Create `._` assignment target. + fn create_underscore_member_expr_target( + object: Expression<'a>, + span: Span, + ctx: &mut TraverseCtx<'a>, + ) -> AssignmentTarget<'a> { + AssignmentTarget::from(Self::create_underscore_member_expr(object, span, ctx)) + } + + /// Create `._` expression. + fn create_underscore_member_expression( + object: Expression<'a>, + span: Span, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + Expression::from(Self::create_underscore_member_expr(object, span, ctx)) + } + + /// Create `._` member expression. + fn create_underscore_member_expr( + object: Expression<'a>, + span: Span, + ctx: &mut TraverseCtx<'a>, + ) -> MemberExpression<'a> { + ctx.ast.member_expression_static(span, object, create_underscore_ident_name(ctx), false) + } +} diff --git a/crates/oxc_transformer/src/es2022/class_properties/utils.rs b/crates/oxc_transformer/src/es2022/class_properties/utils.rs new file mode 100644 index 00000000000000..3b7e923dfffbe5 --- /dev/null +++ b/crates/oxc_transformer/src/es2022/class_properties/utils.rs @@ -0,0 +1,55 @@ +//! ES2022: Class Properties +//! Utility functions. + +use oxc_ast::ast::*; +use oxc_span::SPAN; +use oxc_syntax::reference::ReferenceFlags; +use oxc_traverse::{BoundIdentifier, TraverseCtx}; + +/// Create assignment to a binding. +pub(super) fn create_assignment<'a>( + binding: &BoundIdentifier<'a>, + value: Expression<'a>, + ctx: &mut TraverseCtx<'a>, +) -> Expression<'a> { + ctx.ast.expression_assignment( + SPAN, + AssignmentOperator::Assign, + binding.create_target(ReferenceFlags::Write, ctx), + value, + ) +} + +/// Create `var` declaration. +pub(super) fn create_variable_declaration<'a>( + binding: &BoundIdentifier<'a>, + init: Expression<'a>, + ctx: &mut TraverseCtx<'a>, +) -> Statement<'a> { + let kind = VariableDeclarationKind::Var; + let declarator = ctx.ast.variable_declarator( + SPAN, + kind, + binding.create_binding_pattern(ctx), + Some(init), + false, + ); + Statement::from(ctx.ast.declaration_variable(SPAN, kind, ctx.ast.vec1(declarator), false)) +} + +/// Convert an iterator of `Expression`s into an iterator of `Statement::ExpressionStatement`s. +pub(super) fn exprs_into_stmts<'a, 'c, E>( + exprs: E, + ctx: &'c TraverseCtx<'a>, +) -> impl Iterator> + 'c +where + E: IntoIterator>, + ::IntoIter: 'c, +{ + exprs.into_iter().map(|expr| ctx.ast.statement_expression(SPAN, expr)) +} + +/// Create `IdentifierName` for `_`. +pub(super) fn create_underscore_ident_name<'a>(ctx: &mut TraverseCtx<'a>) -> IdentifierName<'a> { + ctx.ast.identifier_name(SPAN, Atom::from("_")) +} diff --git a/crates/oxc_transformer/src/es2022/class_static_block.rs b/crates/oxc_transformer/src/es2022/class_static_block.rs index 3b85ffc7c6dca0..a29a5a8dc1f54c 100644 --- a/crates/oxc_transformer/src/es2022/class_static_block.rs +++ b/crates/oxc_transformer/src/es2022/class_static_block.rs @@ -130,7 +130,10 @@ impl ClassStaticBlock { /// Convert static block to expression which will be value of private field. /// `static { foo }` -> `foo` /// `static { foo; bar; }` -> `(() => { foo; bar; })()` - fn convert_block_to_expression<'a>( + /// + /// This function also used by `ClassProperties` transform. + /// TODO: Make this function non-pub if no longer use it for `ClassProperties`. + pub fn convert_block_to_expression<'a>( block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>, ) -> Expression<'a> { diff --git a/crates/oxc_transformer/src/es2022/mod.rs b/crates/oxc_transformer/src/es2022/mod.rs index c0ad06a5fb0838..4f4779097c5e10 100644 --- a/crates/oxc_transformer/src/es2022/mod.rs +++ b/crates/oxc_transformer/src/es2022/mod.rs @@ -14,31 +14,51 @@ use class_static_block::ClassStaticBlock; pub use options::ES2022Options; pub struct ES2022<'a, 'ctx> { - options: ES2022Options, // Plugins - class_static_block: ClassStaticBlock, + class_static_block: Option, class_properties: Option>, } impl<'a, 'ctx> ES2022<'a, 'ctx> { pub fn new(options: ES2022Options, ctx: &'ctx TransformCtx<'a>) -> Self { - Self { - options, - class_static_block: ClassStaticBlock::new(), - class_properties: options - .class_properties - .map(|options| ClassProperties::new(options, ctx)), - } + // Class properties transform performs the static block transform differently. + // So only enable static block transform if class properties transform is disabled. + let (class_static_block, class_properties) = + if let Some(properties_options) = options.class_properties { + let class_properties = + ClassProperties::new(properties_options, options.class_static_block, ctx); + (None, Some(class_properties)) + } else { + let class_static_block = + if options.class_static_block { Some(ClassStaticBlock::new()) } else { None }; + (class_static_block, None) + }; + Self { class_static_block, class_properties } } } impl<'a, 'ctx> Traverse<'a> for ES2022<'a, 'ctx> { + fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + if let Some(class_properties) = &mut self.class_properties { + class_properties.enter_expression(expr, ctx); + } + } + + fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { + if let Some(class_properties) = &mut self.class_properties { + class_properties.enter_statement(stmt, ctx); + } + } + fn enter_class_body(&mut self, body: &mut ClassBody<'a>, ctx: &mut TraverseCtx<'a>) { - if self.options.class_static_block { - self.class_static_block.enter_class_body(body, ctx); + if let Some(class_static_block) = &mut self.class_static_block { + class_static_block.enter_class_body(body, ctx); } + } + + fn exit_class(&mut self, class: &mut Class<'a>, ctx: &mut TraverseCtx<'a>) { if let Some(class_properties) = &mut self.class_properties { - class_properties.enter_class_body(body, ctx); + class_properties.exit_class(class, ctx); } } } diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index d20c05ce6909ea..8bc0f6da400750 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -241,6 +241,10 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> { } } + fn exit_class(&mut self, class: &mut Class<'a>, ctx: &mut TraverseCtx<'a>) { + self.x2_es2022.exit_class(class, ctx); + } + fn enter_class_body(&mut self, body: &mut ClassBody<'a>, ctx: &mut TraverseCtx<'a>) { if let Some(typescript) = self.x0_typescript.as_mut() { typescript.enter_class_body(body, ctx); @@ -271,6 +275,7 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> { if let Some(typescript) = self.x0_typescript.as_mut() { typescript.enter_expression(expr, ctx); } + self.x2_es2022.enter_expression(expr, ctx); self.x2_es2021.enter_expression(expr, ctx); self.x2_es2020.enter_expression(expr, ctx); self.x2_es2018.enter_expression(expr, ctx); @@ -504,6 +509,7 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> { if let Some(typescript) = self.x0_typescript.as_mut() { typescript.enter_statement(stmt, ctx); } + self.x2_es2022.enter_statement(stmt, ctx); self.x2_es2018.enter_statement(stmt, ctx); } diff --git a/crates/oxc_transformer/tests/integrations/snapshots/es_target.snap b/crates/oxc_transformer/tests/integrations/snapshots/es_target.snap index d667de94c59324..639b97e1269cd6 100644 --- a/crates/oxc_transformer/tests/integrations/snapshots/es_target.snap +++ b/crates/oxc_transformer/tests/integrations/snapshots/es_target.snap @@ -74,6 +74,5 @@ a || (a = b); ########## 9 es2021 class foo { static {} } ---------- -class foo { - static #_ = (() => {})(); -} +class foo {} +(() => {})(); diff --git a/tasks/transform_conformance/snapshots/babel.snap.md b/tasks/transform_conformance/snapshots/babel.snap.md index a7767c69b1b2b3..dda7a89cc0eef3 100644 --- a/tasks/transform_conformance/snapshots/babel.snap.md +++ b/tasks/transform_conformance/snapshots/babel.snap.md @@ -1,6 +1,6 @@ commit: 54a8389f -Passed: 327/582 +Passed: 405/846 # All Passed: * babel-plugin-transform-class-static-block @@ -276,6 +276,739 @@ x Output mismatch x Output mismatch +# babel-plugin-transform-class-properties (78/264) +* assumption-constantSuper/complex-super-class/input.js +x Output mismatch + +* assumption-constantSuper/static-field/input.js +x Output mismatch + +* assumption-noDocumentAll/optional-chain-before-member-call/input.js +x Output mismatch + +* assumption-noDocumentAll/optional-chain-cast-to-boolean/input.js +x Output mismatch + +* assumption-noUninitializedPrivateFieldAccess/static-private/input.js +x Output mismatch + +* assumption-setPublicClassFields/computed/input.js +x Output mismatch + +* assumption-setPublicClassFields/constructor-collision/input.js +x Output mismatch + +* assumption-setPublicClassFields/foobar/input.js +Scope children mismatch: +after transform: ScopeId(1): [ScopeId(2), ScopeId(3)] +rebuilt : ScopeId(1): [ScopeId(2)] +Scope children mismatch: +after transform: ScopeId(2): [] +rebuilt : ScopeId(2): [ScopeId(3)] +Scope parent mismatch: +after transform: ScopeId(3): Some(ScopeId(1)) +rebuilt : ScopeId(3): Some(ScopeId(2)) + +* assumption-setPublicClassFields/regression-T7364/input.mjs +Scope children mismatch: +after transform: ScopeId(1): [ScopeId(2), ScopeId(7)] +rebuilt : ScopeId(1): [ScopeId(2)] +Scope children mismatch: +after transform: ScopeId(7): [] +rebuilt : ScopeId(2): [ScopeId(3)] +Scope parent mismatch: +after transform: ScopeId(2): Some(ScopeId(1)) +rebuilt : ScopeId(3): Some(ScopeId(2)) +Scope children mismatch: +after transform: ScopeId(3): [ScopeId(4), ScopeId(8)] +rebuilt : ScopeId(4): [ScopeId(5)] +Scope children mismatch: +after transform: ScopeId(8): [] +rebuilt : ScopeId(5): [ScopeId(6)] +Scope parent mismatch: +after transform: ScopeId(4): Some(ScopeId(3)) +rebuilt : ScopeId(6): Some(ScopeId(5)) +Scope children mismatch: +after transform: ScopeId(5): [ScopeId(6), ScopeId(9)] +rebuilt : ScopeId(7): [ScopeId(8)] +Scope children mismatch: +after transform: ScopeId(9): [] +rebuilt : ScopeId(8): [ScopeId(9)] +Scope parent mismatch: +after transform: ScopeId(6): Some(ScopeId(5)) +rebuilt : ScopeId(9): Some(ScopeId(8)) + +* assumption-setPublicClassFields/static-class-binding/input.js +x Output mismatch + +* assumption-setPublicClassFields/static-infer-name/input.js +x Output mismatch + +* assumption-setPublicClassFields/static-super/input.js +x Output mismatch + +* assumption-setPublicClassFields/static-super-loose/input.js +x Output mismatch + +* assumption-setPublicClassFields/static-this/input.js +x Output mismatch + +* assumption-setPublicClassFields/super-expression/input.js +x Output mismatch + +* assumption-setPublicClassFields/super-with-collision/input.js +x Output mismatch + +* class-name-tdz/general/input.js +x Output mismatch + +* class-name-tdz/static-edgest-case/input.js +x Output mismatch + +* class-name-tdz/static-general/input.js +x Output mismatch + +* class-name-tdz/static-loose/input.js +x Output mismatch + +* compile-to-class/constructor-collision-ignores-types/input.js +Unresolved references mismatch: +after transform: ["T", "babelHelpers"] +rebuilt : ["babelHelpers"] + +* compile-to-class/constructor-collision-ignores-types-loose/input.js +Unresolved references mismatch: +after transform: ["T"] +rebuilt : [] + +* nested-class/super-call-in-decorator/input.js +x Output mismatch + +* nested-class/super-property-in-accessor-key/input.js +x Output mismatch + +* nested-class/super-property-in-decorator/input.js +x Output mismatch + +* private/1-helpermemberexpressionfunction/input.js +x Output mismatch + +* private/call/input.js +Scope children mismatch: +after transform: ScopeId(1): [ScopeId(2), ScopeId(3), ScopeId(4)] +rebuilt : ScopeId(1): [ScopeId(2), ScopeId(4)] +Scope children mismatch: +after transform: ScopeId(4): [] +rebuilt : ScopeId(2): [ScopeId(3)] +Scope parent mismatch: +after transform: ScopeId(2): Some(ScopeId(1)) +rebuilt : ScopeId(3): Some(ScopeId(2)) + +* private/class-shadow-builtins/input.mjs +x Output mismatch + +* private/constructor-collision/input.js +x Output mismatch + +* private/derived-multiple-supers/input.js +x Output mismatch + +* private/destructuring-array-pattern/input.js +x Output mismatch + +* private/destructuring-array-pattern-1/input.js +x Output mismatch + +* private/destructuring-array-pattern-2/input.js +x Output mismatch + +* private/destructuring-array-pattern-3/input.js +x Output mismatch + +* private/destructuring-array-pattern-static/input.js +x Output mismatch + +* private/destructuring-object-pattern/input.js +x Output mismatch + +* private/destructuring-object-pattern-1/input.js +x Output mismatch + +* private/destructuring-object-pattern-2/input.js +x Output mismatch + +* private/destructuring-object-pattern-3/input.js +x Output mismatch + +* private/destructuring-object-pattern-static/input.js +x Output mismatch + +* private/extracted-this/input.js +x Output mismatch + +* private/foobar/input.js +Scope children mismatch: +after transform: ScopeId(1): [ScopeId(2), ScopeId(3)] +rebuilt : ScopeId(1): [ScopeId(2)] +Scope children mismatch: +after transform: ScopeId(2): [] +rebuilt : ScopeId(2): [ScopeId(3)] +Scope parent mismatch: +after transform: ScopeId(3): Some(ScopeId(1)) +rebuilt : ScopeId(3): Some(ScopeId(2)) + +* private/nested-class-computed-redeclared/input.js +x Output mismatch + +* private/nested-class-extends-computed/input.js +x Output mismatch + +* private/nested-class-extends-computed-redeclared/input.js +x Output mismatch + +* private/optional-chain-before-member-call/input.js +x Output mismatch + +* private/optional-chain-before-member-call-with-transform/input.js +x Output mismatch + +* private/optional-chain-before-property/input.js +x Output mismatch + +* private/optional-chain-before-property-with-transform/input.js +x Output mismatch + +* private/optional-chain-cast-to-boolean/input.js +x Output mismatch + +* private/optional-chain-delete-property/input.js +x Output mismatch + +* private/optional-chain-delete-property-with-transform/input.js +x Output mismatch + +* private/optional-chain-in-function-param/input.js +x Output mismatch + +* private/optional-chain-in-function-param-with-transform/input.js +x Output mismatch + +* private/optional-chain-member-optional-call/input.js +x Output mismatch + +* private/optional-chain-member-optional-call-spread-arguments/input.js +x Output mismatch + +* private/optional-chain-member-optional-call-with-transform/input.js +x Output mismatch + +* private/optional-chain-optional-member-call/input.js +x Output mismatch + +* private/optional-chain-optional-member-call-with-transform/input.js +x Output mismatch + +* private/optional-chain-optional-property/input.js +x Output mismatch + +* private/optional-chain-optional-property-with-transform/input.js +x Output mismatch + +* private/parenthesized-optional-member-call/input.js +x Output mismatch + +* private/parenthesized-optional-member-call-with-transform/input.js +x Output mismatch + +* private/regression-T7364/input.mjs +x Output mismatch + +* private/static-call/input.js +Scope children mismatch: +after transform: ScopeId(0): [ScopeId(1)] +rebuilt : ScopeId(0): [ScopeId(1), ScopeId(3)] +Scope children mismatch: +after transform: ScopeId(1): [ScopeId(2), ScopeId(3)] +rebuilt : ScopeId(1): [ScopeId(2)] +Scope flags mismatch: +after transform: ScopeId(2): ScopeFlags(StrictMode | Function) +rebuilt : ScopeId(3): ScopeFlags(Function) +Scope parent mismatch: +after transform: ScopeId(2): Some(ScopeId(1)) +rebuilt : ScopeId(3): Some(ScopeId(0)) +Symbol reference IDs mismatch for "Foo": +after transform: SymbolId(0): [ReferenceId(1), ReferenceId(4)] +rebuilt : SymbolId(0): [ReferenceId(1)] + +* private/static-class-binding/input.js +x Output mismatch + +* private/static-infer-name/input.js +x Output mismatch + +* private/static-self-field/input.js +x Output mismatch + +* private/static-shadow/input.js +x Output mismatch + +* private/static-this/input.js +x Output mismatch + +* private/super-expression/input.js +x Output mismatch + +* private/tagged-template/input.js +x Output mismatch + +* private-loose/assignment/input.js +x Output mismatch + +* private-loose/call/input.js +x Output mismatch + +* private-loose/canonical/input.js +x Output mismatch + +* private-loose/class-shadow-builtins/input.mjs +x Output mismatch + +* private-loose/constructor-collision/input.js +x Output mismatch + +* private-loose/declaration-order/input.js +x Output mismatch + +* private-loose/derived/input.js +x Output mismatch + +* private-loose/derived-multiple-supers/input.js +x Output mismatch + +* private-loose/destructuring-array-pattern/input.js +x Output mismatch + +* private-loose/destructuring-array-pattern-1/input.js +x Output mismatch + +* private-loose/destructuring-array-pattern-2/input.js +x Output mismatch + +* private-loose/destructuring-array-pattern-3/input.js +x Output mismatch + +* private-loose/destructuring-array-pattern-static/input.js +x Output mismatch + +* private-loose/destructuring-object-pattern/input.js +x Output mismatch + +* private-loose/destructuring-object-pattern-1/input.js +x Output mismatch + +* private-loose/destructuring-object-pattern-2/input.js +x Output mismatch + +* private-loose/destructuring-object-pattern-3/input.js +x Output mismatch + +* private-loose/destructuring-object-pattern-static/input.js +x Output mismatch + +* private-loose/extracted-this/input.js +x Output mismatch + +* private-loose/foobar/input.js +x Output mismatch + +* private-loose/instance/input.js +x Output mismatch + +* private-loose/instance-undefined/input.js +x Output mismatch + +* private-loose/logical-assignment/input.js +x Output mismatch + +* private-loose/multiple/input.js +x Output mismatch + +* private-loose/native-classes/input.js +x Output mismatch + +* private-loose/nested-class/input.js +x Output mismatch + +* private-loose/nested-class-computed/input.js +x Output mismatch + +* private-loose/nested-class-computed-redeclared/input.js +x Output mismatch + +* private-loose/nested-class-extends-computed/input.js +x Output mismatch + +* private-loose/nested-class-extends-computed-redeclared/input.js +x Output mismatch + +* private-loose/nested-class-other-redeclared/input.js +x Output mismatch + +* private-loose/nested-class-redeclared/input.js +x Output mismatch + +* private-loose/non-block-arrow-func/input.mjs +x Output mismatch + +* private-loose/optional-chain-before-member-call/input.js +x Output mismatch + +* private-loose/optional-chain-before-member-call-with-transform/input.js +x Output mismatch + +* private-loose/optional-chain-before-property/input.js +x Output mismatch + +* private-loose/optional-chain-before-property-with-transform/input.js +x Output mismatch + +* private-loose/optional-chain-cast-to-boolean/input.js +x Output mismatch + +* private-loose/optional-chain-delete-property/input.js +x Output mismatch + +* private-loose/optional-chain-delete-property-with-transform/input.js +x Output mismatch + +* private-loose/optional-chain-in-function-param/input.js +x Output mismatch + +* private-loose/optional-chain-in-function-param-with-transform/input.js +x Output mismatch + +* private-loose/optional-chain-member-optional-call/input.js +x Output mismatch + +* private-loose/optional-chain-member-optional-call-spread-arguments/input.js +x Output mismatch + +* private-loose/optional-chain-member-optional-call-with-transform/input.js +x Output mismatch + +* private-loose/optional-chain-optional-member-call/input.js +x Output mismatch + +* private-loose/optional-chain-optional-member-call-with-transform/input.js +x Output mismatch + +* private-loose/optional-chain-optional-property/input.js +x Output mismatch + +* private-loose/optional-chain-optional-property-with-transform/input.js +x Output mismatch + +* private-loose/parenthesized-optional-member-call/input.js +x Output mismatch + +* private-loose/parenthesized-optional-member-call-with-transform/input.js +x Output mismatch + +* private-loose/preserve-comments/input.js +x Output mismatch + +* private-loose/private-in-derived/input.js +x Output mismatch + +* private-loose/reevaluated/input.js +x Output mismatch + +* private-loose/reference-in-other-property/input.js +x Output mismatch + +* private-loose/static/input.js +x Output mismatch + +* private-loose/static-call/input.js +x Output mismatch + +* private-loose/static-class-binding/input.js +x Output mismatch + +* private-loose/static-export/input.mjs +x Output mismatch + +* private-loose/static-infer-name/input.js +x Output mismatch + +* private-loose/static-inherited/input.js +x Output mismatch + +* private-loose/static-shadow/input.js +x Output mismatch + +* private-loose/static-this/input.js +x Output mismatch + +* private-loose/static-undefined/input.js +x Output mismatch + +* private-loose/super-expression/input.js +x Output mismatch + +* private-loose/super-statement/input.js +x Output mismatch + +* private-loose/update/input.js +x Output mismatch + +* public/arrow-static-this-without-transform/input.js +x Output mismatch + +* public/call/input.js +Scope children mismatch: +after transform: ScopeId(1): [ScopeId(2), ScopeId(3), ScopeId(4)] +rebuilt : ScopeId(1): [ScopeId(2), ScopeId(4)] +Scope children mismatch: +after transform: ScopeId(4): [] +rebuilt : ScopeId(2): [ScopeId(3)] +Scope parent mismatch: +after transform: ScopeId(2): Some(ScopeId(1)) +rebuilt : ScopeId(3): Some(ScopeId(2)) + +* public/class-shadow-builtins/input.mjs +x Output mismatch + +* public/computed/input.js +x Output mismatch + +* public/computed-toPrimitive/input.js +x Output mismatch + +* public/constructor-collision/input.js +x Output mismatch + +* public/delete-super-property/input.js +x Output mismatch + +* public/delete-this/input.js +x Output mismatch + +* public/derived-multiple-supers/input.js +x Output mismatch + +* public/derived-super-in-default-params/input.js +x Output mismatch + +* public/derived-super-in-default-params-complex/input.js +x Output mismatch + +* public/derived-super-in-default-params-in-arrow/input.js +x Output mismatch + +* public/extracted-this/input.js +x Output mismatch + +* public/foobar/input.js +Scope children mismatch: +after transform: ScopeId(1): [ScopeId(2), ScopeId(3)] +rebuilt : ScopeId(1): [ScopeId(2)] +Scope children mismatch: +after transform: ScopeId(2): [] +rebuilt : ScopeId(2): [ScopeId(3)] +Scope parent mismatch: +after transform: ScopeId(3): Some(ScopeId(1)) +rebuilt : ScopeId(3): Some(ScopeId(2)) + +* public/non-block-arrow-func/input.mjs +x Output mismatch + +* public/regression-T2983/input.mjs +x Output mismatch + +* public/regression-T7364/input.mjs +Scope children mismatch: +after transform: ScopeId(1): [ScopeId(2), ScopeId(7)] +rebuilt : ScopeId(1): [ScopeId(2)] +Scope children mismatch: +after transform: ScopeId(7): [] +rebuilt : ScopeId(2): [ScopeId(3)] +Scope parent mismatch: +after transform: ScopeId(2): Some(ScopeId(1)) +rebuilt : ScopeId(3): Some(ScopeId(2)) +Scope children mismatch: +after transform: ScopeId(3): [ScopeId(4), ScopeId(8)] +rebuilt : ScopeId(4): [ScopeId(5)] +Scope children mismatch: +after transform: ScopeId(8): [] +rebuilt : ScopeId(5): [ScopeId(6)] +Scope parent mismatch: +after transform: ScopeId(4): Some(ScopeId(3)) +rebuilt : ScopeId(6): Some(ScopeId(5)) +Scope children mismatch: +after transform: ScopeId(5): [ScopeId(6), ScopeId(9)] +rebuilt : ScopeId(7): [ScopeId(8)] +Scope children mismatch: +after transform: ScopeId(9): [] +rebuilt : ScopeId(8): [ScopeId(9)] +Scope parent mismatch: +after transform: ScopeId(6): Some(ScopeId(5)) +rebuilt : ScopeId(9): Some(ScopeId(8)) + +* public/static-class-binding/input.js +x Output mismatch + +* public/static-export/input.mjs +x Output mismatch + +* public/static-infer-name/input.js +x Output mismatch + +* public/static-super/input.js +x Output mismatch + +* public/static-this/input.js +x Output mismatch + +* public/super-expression/input.js +x Output mismatch + +* public/super-with-collision/input.js +x Output mismatch + +* public-loose/arrow-static-this-without-transform/input.js +x Output mismatch + +* public-loose/class-shadow-builtins/input.mjs +x Output mismatch + +* public-loose/computed/input.js +x Output mismatch + +* public-loose/constructor-collision/input.js +x Output mismatch + +* public-loose/foobar/input.js +x Output mismatch + +* public-loose/non-block-arrow-func/input.mjs +x Output mismatch + +* public-loose/regression-T2983/input.mjs +x Output mismatch + +* public-loose/regression-T7364/input.mjs +Scope children mismatch: +after transform: ScopeId(1): [ScopeId(2), ScopeId(7)] +rebuilt : ScopeId(1): [ScopeId(2)] +Scope children mismatch: +after transform: ScopeId(7): [] +rebuilt : ScopeId(2): [ScopeId(3)] +Scope parent mismatch: +after transform: ScopeId(2): Some(ScopeId(1)) +rebuilt : ScopeId(3): Some(ScopeId(2)) +Scope children mismatch: +after transform: ScopeId(3): [ScopeId(4), ScopeId(8)] +rebuilt : ScopeId(4): [ScopeId(5)] +Scope children mismatch: +after transform: ScopeId(8): [] +rebuilt : ScopeId(5): [ScopeId(6)] +Scope parent mismatch: +after transform: ScopeId(4): Some(ScopeId(3)) +rebuilt : ScopeId(6): Some(ScopeId(5)) +Scope children mismatch: +after transform: ScopeId(5): [ScopeId(6), ScopeId(9)] +rebuilt : ScopeId(7): [ScopeId(8)] +Scope children mismatch: +after transform: ScopeId(9): [] +rebuilt : ScopeId(8): [ScopeId(9)] +Scope parent mismatch: +after transform: ScopeId(6): Some(ScopeId(5)) +rebuilt : ScopeId(9): Some(ScopeId(8)) + +* public-loose/static-class-binding/input.js +x Output mismatch + +* public-loose/static-export/input.mjs +x Output mismatch + +* public-loose/static-infer-name/input.js +x Output mismatch + +* public-loose/static-super/input.js +x Output mismatch + +* public-loose/static-this/input.js +x Output mismatch + +* public-loose/super-expression/input.js +x Output mismatch + +* public-loose/super-with-collision/input.js +x Output mismatch + +* regression/6153/input.js +x Output mismatch + +* regression/6154/input.js +x Output mismatch + +* regression/7371/input.js +x Output mismatch + +* regression/7951/input.mjs +x Output mismatch + +* regression/8882/input.js +Bindings mismatch: +after transform: ScopeId(0): ["classes"] +rebuilt : ScopeId(0): ["_A", "_bar", "classes"] +Bindings mismatch: +after transform: ScopeId(2): ["_A", "_bar", "_i"] +rebuilt : ScopeId(2): ["_i"] +Symbol scope ID mismatch for "_A": +after transform: SymbolId(3): ScopeId(2) +rebuilt : SymbolId(2): ScopeId(0) +Symbol scope ID mismatch for "_bar": +after transform: SymbolId(4): ScopeId(2) +rebuilt : SymbolId(3): ScopeId(0) + +* regression/T2983/input.mjs +x Output mismatch + +* regression/T7364/input.mjs +Scope children mismatch: +after transform: ScopeId(1): [ScopeId(2), ScopeId(7)] +rebuilt : ScopeId(1): [ScopeId(2)] +Scope children mismatch: +after transform: ScopeId(7): [] +rebuilt : ScopeId(2): [ScopeId(3)] +Scope parent mismatch: +after transform: ScopeId(2): Some(ScopeId(1)) +rebuilt : ScopeId(3): Some(ScopeId(2)) +Scope children mismatch: +after transform: ScopeId(3): [ScopeId(4), ScopeId(8)] +rebuilt : ScopeId(4): [ScopeId(5)] +Scope children mismatch: +after transform: ScopeId(8): [] +rebuilt : ScopeId(5): [ScopeId(6)] +Scope parent mismatch: +after transform: ScopeId(4): Some(ScopeId(3)) +rebuilt : ScopeId(6): Some(ScopeId(5)) +Scope children mismatch: +after transform: ScopeId(5): [ScopeId(6), ScopeId(9)] +rebuilt : ScopeId(7): [ScopeId(8)] +Scope children mismatch: +after transform: ScopeId(9): [] +rebuilt : ScopeId(8): [ScopeId(9)] +Scope parent mismatch: +after transform: ScopeId(6): Some(ScopeId(5)) +rebuilt : ScopeId(9): Some(ScopeId(8)) + +* regression/multiple-super-in-termary/input.js +x Output mismatch + + # babel-plugin-transform-nullish-coalescing-operator (5/12) * assumption-noDocumentAll/transform/input.js x Output mismatch diff --git a/tasks/transform_conformance/snapshots/babel_exec.snap.md b/tasks/transform_conformance/snapshots/babel_exec.snap.md index 08c761a7cc66e5..651dd00bc91b35 100644 --- a/tasks/transform_conformance/snapshots/babel_exec.snap.md +++ b/tasks/transform_conformance/snapshots/babel_exec.snap.md @@ -1,7 +1,7 @@ commit: 54a8389f node: v22.11.0 -⎯⎯⎯⎯⎯⎯ Failed Suites 1 ⎯⎯⎯⎯⎯⎯⎯ +⎯⎯⎯⎯⎯⎯ Failed Suites 57 ⎯⎯⎯⎯⎯⎯ FAIL fixtures/babel-plugin-transform-arrow-functions-test-fixtures-arrow-functions-implicit-var-arguments-exec.test.js [ fixtures/babel-plugin-transform-arrow-functions-test-fixtures-arrow-functions-implicit-var-arguments-exec.test.js ] Error: 'eval' and 'arguments' cannot be used as a binding identifier in strict mode @@ -11,9 +11,1499 @@ Error: 'eval' and 'arguments' cannot be used as a binding identifier in strict m ❯ ssrTransformScript ../../node_modules/.pnpm/vite@5.4.11_@types+node@22.9.1/node_modules/vite/dist/node/chunks/dep-CB_7IfJ-.js:52381:11 ❯ loadAndTransform ../../node_modules/.pnpm/vite@5.4.11_@types+node@22.9.1/node_modules/vite/dist/node/chunks/dep-CB_7IfJ-.js:51979:72 -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/7]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/98]⎯ -⎯⎯⎯⎯⎯⎯⎯ Failed Tests 6 ⎯⎯⎯⎯⎯⎯⎯ + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-assumption-noDocumentAll-optional-chain-before-member-call-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-assumption-noDocumentAll-optional-chain-before-member-call-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-assumption-noUninitializedPrivateFieldAccess-static-private-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-assumption-noUninitializedPrivateFieldAccess-static-private-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-assumption-setPublicClassFields-static-super-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-assumption-setPublicClassFields-static-super-exec.test.js ] + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-public-delete-super-property-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-public-delete-super-property-exec.test.js ] + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-public-loose-static-super-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-public-loose-static-super-exec.test.js ] + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-public-static-super-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-public-static-super-exec.test.js ] +Error: Invalid access to super + ❯ getRollupError ../../node_modules/.pnpm/rollup@4.27.3/node_modules/rollup/dist/es/shared/parseAst.js:396:41 + ❯ convertProgram ../../node_modules/.pnpm/rollup@4.27.3/node_modules/rollup/dist/es/shared/parseAst.js:1084:26 + ❯ parseAstAsync ../../node_modules/.pnpm/rollup@4.27.3/node_modules/rollup/dist/es/shared/parseAst.js:2070:106 + ❯ ssrTransformScript ../../node_modules/.pnpm/vite@5.4.11_@types+node@22.9.1/node_modules/vite/dist/node/chunks/dep-CB_7IfJ-.js:52381:11 + ❯ loadAndTransform ../../node_modules/.pnpm/vite@5.4.11_@types+node@22.9.1/node_modules/vite/dist/node/chunks/dep-CB_7IfJ-.js:51979:72 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-nested-class-super-property-in-accessor-key-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-nested-class-super-property-in-accessor-key-exec.test.js ] +Error: Unexpected token `[`. Expected * for generator, private key, identifier or async + ❯ getRollupError ../../node_modules/.pnpm/rollup@4.27.3/node_modules/rollup/dist/es/shared/parseAst.js:396:41 + ❯ convertProgram ../../node_modules/.pnpm/rollup@4.27.3/node_modules/rollup/dist/es/shared/parseAst.js:1084:26 + ❯ parseAstAsync ../../node_modules/.pnpm/rollup@4.27.3/node_modules/rollup/dist/es/shared/parseAst.js:2070:106 + ❯ ssrTransformScript ../../node_modules/.pnpm/vite@5.4.11_@types+node@22.9.1/node_modules/vite/dist/node/chunks/dep-CB_7IfJ-.js:52381:11 + ❯ loadAndTransform ../../node_modules/.pnpm/vite@5.4.11_@types+node@22.9.1/node_modules/vite/dist/node/chunks/dep-CB_7IfJ-.js:51979:72 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-access-before-declaration-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-access-before-declaration-exec.test.js ] +SyntaxError: Private field '#p' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-1-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-1-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[7/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-2-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-2-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[8/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-3-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-3-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[9/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[10/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-static-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-array-pattern-static-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[11/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-1-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-1-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[12/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-2-exec-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-2-exec-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[13/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-3-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-3-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[14/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[15/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-static-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-destructuring-object-pattern-static-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[16/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-access-before-declaration-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-access-before-declaration-exec.test.js ] +SyntaxError: Private field '#p' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[17/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-1-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-1-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[18/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-2-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-2-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[19/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-3-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-3-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[20/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[21/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-static-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-array-pattern-static-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[22/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-1-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-1-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[23/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-2-exec-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-2-exec-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[24/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-3-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-3-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[25/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[26/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-static-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-destructuring-object-pattern-static-exec.test.js ] +SyntaxError: Private field '#client' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[27/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-nested-class-computed-redeclared-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-nested-class-computed-redeclared-exec.test.js ] +SyntaxError: Private field '#foo' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[28/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-member-call-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-member-call-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[29/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-member-call-with-transform-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-member-call-with-transform-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[30/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-property-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-property-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[31/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[32/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-with-transform-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-with-transform-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[33/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-member-optional-call-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-member-optional-call-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[34/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-member-optional-call-with-transform-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-member-optional-call-with-transform-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[35/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-member-call-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-member-call-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[36/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-member-call-with-transform-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-member-call-with-transform-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[37/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-property-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-property-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[38/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-parenthesized-optional-member-call-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-parenthesized-optional-member-call-exec.test.js ] +SyntaxError: Private field '#m' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[39/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-parenthesized-optional-member-call-with-transform-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-parenthesized-optional-member-call-with-transform-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[40/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-nested-class-computed-redeclared-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-nested-class-computed-redeclared-exec.test.js ] +SyntaxError: Private field '#foo' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[41/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-member-call-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-member-call-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[42/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-member-call-with-transform-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-member-call-with-transform-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[43/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-property-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-property-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[44/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-in-function-param-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-in-function-param-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[45/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-in-function-param-with-transform-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-in-function-param-with-transform-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[46/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-member-optional-call-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-member-optional-call-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[47/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-member-optional-call-with-transform-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-member-optional-call-with-transform-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[48/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-member-call-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-member-call-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[49/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-member-call-with-transform-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-member-call-with-transform-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[50/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-property-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-property-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[51/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-parenthesized-optional-member-call-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-parenthesized-optional-member-call-exec.test.js ] +SyntaxError: Private field '#m' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[52/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-parenthesized-optional-member-call-with-transform-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-parenthesized-optional-member-call-with-transform-exec.test.js ] +SyntaxError: Private field '#x' must be declared in an enclosing class +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[53/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-regression-7371-exec.test.js [ fixtures/babel-plugin-transform-class-properties-test-fixtures-regression-7371-exec.test.js ] +SyntaxError: 'super' keyword unexpected here +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[54/98]⎯ + +⎯⎯⎯⎯⎯⎯ Failed Tests 41 ⎯⎯⎯⎯⎯⎯⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-assumption-noDocumentAll-optional-chain-cast-to-boolean-exec.test.js > exec +TypeError: Private element is not present on this object + ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 + ❯ Function.testIf fixtures/babel-plugin-transform-class-properties-test-fixtures-assumption-noDocumentAll-optional-chain-cast-to-boolean-exec.test.js:7:8 + 5| class C { + 6| static testIf(o) { + 7| if (_assertClassBrand(C, o, _a)._.b.c.d) { + | ^ + 8| return true; + 9| } + ❯ Function.testNullish fixtures/babel-plugin-transform-class-properties-test-fixtures-assumption-noDocumentAll-optional-chain-cast-to-boolean-exec.test.js:89:14 + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-assumption-noDocumentAll-optional-chain-cast-to-boolean-exec.test.js:105:4 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[55/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-assumption-setPublicClassFields-static-class-binding-exec.test.js > exec +AssertionError: expected null to be [Function A] // Object.is equality + +- Expected: +[Function A] + ++ Received: +null + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-assumption-setPublicClassFields-static-class-binding-exec.test.js:10:22 + 8| A = null; + 9| expect(oldA.self).toBe(oldA); + 10| expect(oldA.getA()).toBe(oldA); + | ^ + 11| }) + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[56/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-assumption-setPublicClassFields-static-infer-name-exec.test.js > exec +AssertionError: expected '_Class' to be 'Foo' // Object.is equality + +Expected: "Foo" +Received: "_Class" + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-assumption-setPublicClassFields-static-infer-name-exec.test.js:8:19 + 6| expect(Foo.num).toBe(0); + 7| expect(Foo.num = 1).toBe(1); + 8| expect(Foo.name).toBe("Foo"); + | ^ + 9| }) + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[57/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-assumption-setPublicClassFields-static-this-exec.test.js > exec +AssertionError: expected { Object: [Function Object], …(133), …(6) } to be [Function A] // Object.is equality + +- Expected: +[Function A] + ++ Received: +Object { + "__vitest_environment__": "node", + "__vitest_required__": Object { + "timers": Object { + "_unrefActive": [Function deprecated], + "active": [Function deprecated], + "clearImmediate": [Function clearImmediate], + "clearInterval": [Function clearInterval], + "clearTimeout": [Function clearTimeout], + "enroll": [Function deprecated], + "promises": Object { + "scheduler": Scheduler { + Symbol(kScheduler): true, + }, + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + }, + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + "unenroll": [Function deprecated], + }, + "util": Object { + "MIMEParams": [Function MIMEParams], + "MIMEType": [Function MIMEType], + "TextDecoder": [Function TextDecoder], + "TextEncoder": [Function TextEncoder], + "_errnoException": [Function _errnoException], + "_exceptionWithHostPort": [Function _exceptionWithHostPort], + "_extend": [Function deprecated], + "aborted": [Function aborted], + "callbackify": [Function callbackify], + "debug": [Function debuglog], + "debuglog": [Function debuglog], + "deprecate": [Function deprecate], + "format": [Function format], + "formatWithOptions": [Function formatWithOptions], + "getCallSite": [Function getCallSite], + "getSystemErrorMap": [Function getSystemErrorMap], + "getSystemErrorName": [Function getSystemErrorName], + "inherits": [Function inherits], + "inspect": [Function inspect], + "isArray": [Function deprecated], + "isBoolean": [Function deprecated], + "isBuffer": [Function deprecated], + "isDate": [Function deprecated], + "isDeepStrictEqual": [Function isDeepStrictEqual], + "isError": [Function deprecated], + "isFunction": [Function deprecated], + "isNull": [Function deprecated], + "isNullOrUndefined": [Function deprecated], + "isNumber": [Function deprecated], + "isObject": [Function deprecated], + "isPrimitive": [Function deprecated], + "isRegExp": [Function deprecated], + "isString": [Function deprecated], + "isSymbol": [Function deprecated], + "isUndefined": [Function deprecated], + "log": [Function deprecated], + "parseArgs": [Function parseArgs], + "parseEnv": [Function parseEnv], + "promisify": [Function promisify], + "stripVTControlCharacters": [Function stripVTControlCharacters], + "styleText": [Function styleText], + "toUSVString": [Function toUSVString], + "transferableAbortController": [Function transferableAbortController], + "transferableAbortSignal": [Function transferableAbortSignal], + "types": Object { + "isAnyArrayBuffer": [Function isAnyArrayBuffer], + "isArgumentsObject": [Function isArgumentsObject], + "isArrayBuffer": [Function isArrayBuffer], + "isArrayBufferView": [Function isView], + "isAsyncFunction": [Function isAsyncFunction], + "isBigInt64Array": [Function isBigInt64Array], + "isBigIntObject": [Function isBigIntObject], + "isBigUint64Array": [Function isBigUint64Array], + "isBooleanObject": [Function isBooleanObject], + "isBoxedPrimitive": [Function isBoxedPrimitive], + "isCryptoKey": [Function value], + "isDataView": [Function isDataView], + "isDate": [Function isDate], + "isExternal": [Function isExternal], + "isFloat32Array": [Function isFloat32Array], + "isFloat64Array": [Function isFloat64Array], + "isGeneratorFunction": [Function isGeneratorFunction], + "isGeneratorObject": [Function isGeneratorObject], + "isInt16Array": [Function isInt16Array], + "isInt32Array": [Function isInt32Array], + "isInt8Array": [Function isInt8Array], + "isKeyObject": [Function value], + "isMap": [Function isMap], + "isMapIterator": [Function isMapIterator], + "isModuleNamespaceObject": [Function isModuleNamespaceObject], + "isNativeError": [Function isNativeError], + "isNumberObject": [Function isNumberObject], + "isPromise": [Function isPromise], + "isProxy": [Function isProxy], + "isRegExp": [Function isRegExp], + "isSet": [Function isSet], + "isSetIterator": [Function isSetIterator], + "isSharedArrayBuffer": [Function isSharedArrayBuffer], + "isStringObject": [Function isStringObject], + "isSymbolObject": [Function isSymbolObject], + "isTypedArray": [Function isTypedArray], + "isUint16Array": [Function isUint16Array], + "isUint32Array": [Function isUint32Array], + "isUint8Array": [Function isUint8Array], + "isUint8ClampedArray": [Function isUint8ClampedArray], + "isWeakMap": [Function isWeakMap], + "isWeakSet": [Function isWeakSet], + }, + }, + }, + "atob": [Function atob], + "btoa": [Function btoa], + "clearImmediate": [Function clearImmediate], + "clearInterval": [Function clearInterval], + "clearTimeout": [Function clearTimeout], + "crypto": Crypto {}, + "fetch": [Function fetch], + "global": [Circular], + "navigator": Navigator {}, + "performance": Performance { + Symbol(kEvents): Map {}, + Symbol(events.maxEventTargetListeners): 10, + Symbol(events.maxEventTargetListenersWarned): false, + Symbol(kHandlers): Map {}, + Symbol(performance): true, + }, + "queueMicrotask": [Function queueMicrotask], + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + "structuredClone": [Function structuredClone], + Symbol(vitest:SAFE_TIMERS): Object { + "clearImmediate": [Function clearImmediate], + "clearInterval": [Function clearInterval], + "clearTimeout": [Function clearTimeout], + "nextTick": [Function nextTick], + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + }, +} + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-assumption-setPublicClassFields-static-this-exec.test.js:8:15 + 6| A.getA = () => this; + 7| const { self, getA } = A; + 8| expect(self).toBe(A); + | ^ + 9| expect(getA()).toBe(A); + 10| const oldA = A; + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[58/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-nested-class-super-call-in-decorator-exec.test.js > exec +AssertionError: expected undefined to be 'hello' // Object.is equality + +- Expected: +"hello" + ++ Received: +undefined + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-nested-class-super-call-in-decorator-exec.test.js:21:28 + 19| } + 20| } + 21| expect(new Outer().hello).toBe("hello"); + | ^ + 22| }) + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[59/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-nested-class-super-property-in-decorator-exec.test.js > exec +AssertionError: expected undefined to be 'hello' // Object.is equality + +- Expected: +"hello" + ++ Received: +undefined + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-nested-class-super-property-in-decorator-exec.test.js:22:28 + 20| } + 21| } + 22| expect(new Outer().hello).toBe("hello"); + | ^ + 23| }) + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[60/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-constructor-collision-exec.test.js > exec +AssertionError: expected undefined to be 'bar' // Object.is equality + +- Expected: +"bar" + ++ Received: +undefined + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-constructor-collision-exec.test.js:18:19 + 16| } + 17| const f = new Foo(); + 18| expect(f.test()).toBe(foo); + | ^ + 19| expect("bar" in f).toBe(false); + 20| }) + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[61/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-constructor-collision-exec.test.js > exec +AssertionError: expected undefined to be 'bar' // Object.is equality + +- Expected: +"bar" + ++ Received: +undefined + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-constructor-collision-exec.test.js:18:19 + 16| } + 17| const f = new Foo(); + 18| expect(f.test()).toBe(foo); + | ^ + 19| expect("bar" in f).toBe(false); + 20| }) + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[62/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-nested-class-extends-computed-exec.test.js > exec +AssertionError: expected [Function] to not throw an error but 'TypeError: Private element is not pre…' was thrown + +- Expected: +undefined + ++ Received: +"TypeError: Private element is not present on this object" + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-nested-class-extends-computed-exec.test.js:30:9 + 28| expect(() => { + 29| f.test(); + 30| }).not.toThrow(); + | ^ + 31| }) + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[63/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-property-with-transform-exec.test.js > exec +TypeError: Private element is not present on this object + ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 + ❯ Function.test fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-property-with-transform-exec.test.js:31:166 + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-before-property-with-transform-exec.test.js:110:6 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[64/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-cast-to-boolean-exec.test.js > exec +TypeError: Private element is not present on this object + ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 + ❯ Function.testIf fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-cast-to-boolean-exec.test.js:7:8 + 5| class C { + 6| static testIf(o) { + 7| if (_assertClassBrand(C, o, _a)._.b.c.d) { + | ^ + 8| return true; + 9| } + ❯ Function.testNullish fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-cast-to-boolean-exec.test.js:89:14 + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-cast-to-boolean-exec.test.js:105:4 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[65/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-delete-property-exec.test.js > exec +TypeError: Private element is not present on this object + ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 + ❯ Function.testNull fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-delete-property-exec.test.js:56:18 + 54| return deep; + 55| } + 56| expect(delete _assertClassBrand(Foo, deep?.very.o?.Foo, _self)._.un… + | ^ + 57| expect(delete _assertClassBrand(Foo, o?.Foo, _self)._.unicorn).toBe… + 58| expect(delete _assertClassBrand(Foo, o?.Foo, _self)._.self.unicorn)… + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-delete-property-exec.test.js:92:6 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[66/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-delete-property-with-transform-exec.test.js > exec +AssertionError: expected function to throw an error, but it didn't + ❯ Function.test fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-delete-property-with-transform-exec.test.js:41:7 + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-delete-property-with-transform-exec.test.js:158:6 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[67/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-property-with-transform-exec.test.js > exec +TypeError: Private element is not present on this object + ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 + ❯ Function.test fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-property-with-transform-exec.test.js:34:269 + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-optional-property-with-transform-exec.test.js:113:6 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[68/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-static-class-binding-exec.test.js > exec +AssertionError: expected null to be [Function A] // Object.is equality + +- Expected: +[Function A] + ++ Received: +null + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-static-class-binding-exec.test.js:17:32 + 15| A = null; + 16| expect(oldA.extract().self).toBe(oldA); + 17| expect(oldA.extract().getA()).toBe(oldA); + | ^ + 18| }) + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[69/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-static-shadow-exec.test.js > exec +TypeError: e.has is not a function + ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:2:44 + ❯ func fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-static-shadow-exec.test.js:10:12 + 8| const func = () => { + 9| const Test = 3; + 10| return _assertClassBrand(Test, this, _x)._ + Test; + | ^ + 11| }; + 12| return func() + Test; + ❯ Function.method fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-static-shadow-exec.test.js:12:11 + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-static-shadow-exec.test.js:16:14 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[70/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-static-this-exec.test.js > exec +AssertionError: expected { Object: [Function Object], …(133), …(6) } to be [Function A] // Object.is equality + +- Expected: +[Function A] + ++ Received: +Object { + "__vitest_environment__": "node", + "__vitest_required__": Object { + "timers": Object { + "_unrefActive": [Function deprecated], + "active": [Function deprecated], + "clearImmediate": [Function clearImmediate], + "clearInterval": [Function clearInterval], + "clearTimeout": [Function clearTimeout], + "enroll": [Function deprecated], + "promises": Object { + "scheduler": Scheduler { + Symbol(kScheduler): true, + }, + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + }, + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + "unenroll": [Function deprecated], + }, + "util": Object { + "MIMEParams": [Function MIMEParams], + "MIMEType": [Function MIMEType], + "TextDecoder": [Function TextDecoder], + "TextEncoder": [Function TextEncoder], + "_errnoException": [Function _errnoException], + "_exceptionWithHostPort": [Function _exceptionWithHostPort], + "_extend": [Function deprecated], + "aborted": [Function aborted], + "callbackify": [Function callbackify], + "debug": [Function debuglog], + "debuglog": [Function debuglog], + "deprecate": [Function deprecate], + "format": [Function format], + "formatWithOptions": [Function formatWithOptions], + "getCallSite": [Function getCallSite], + "getSystemErrorMap": [Function getSystemErrorMap], + "getSystemErrorName": [Function getSystemErrorName], + "inherits": [Function inherits], + "inspect": [Function inspect], + "isArray": [Function deprecated], + "isBoolean": [Function deprecated], + "isBuffer": [Function deprecated], + "isDate": [Function deprecated], + "isDeepStrictEqual": [Function isDeepStrictEqual], + "isError": [Function deprecated], + "isFunction": [Function deprecated], + "isNull": [Function deprecated], + "isNullOrUndefined": [Function deprecated], + "isNumber": [Function deprecated], + "isObject": [Function deprecated], + "isPrimitive": [Function deprecated], + "isRegExp": [Function deprecated], + "isString": [Function deprecated], + "isSymbol": [Function deprecated], + "isUndefined": [Function deprecated], + "log": [Function deprecated], + "parseArgs": [Function parseArgs], + "parseEnv": [Function parseEnv], + "promisify": [Function promisify], + "stripVTControlCharacters": [Function stripVTControlCharacters], + "styleText": [Function styleText], + "toUSVString": [Function toUSVString], + "transferableAbortController": [Function transferableAbortController], + "transferableAbortSignal": [Function transferableAbortSignal], + "types": Object { + "isAnyArrayBuffer": [Function isAnyArrayBuffer], + "isArgumentsObject": [Function isArgumentsObject], + "isArrayBuffer": [Function isArrayBuffer], + "isArrayBufferView": [Function isView], + "isAsyncFunction": [Function isAsyncFunction], + "isBigInt64Array": [Function isBigInt64Array], + "isBigIntObject": [Function isBigIntObject], + "isBigUint64Array": [Function isBigUint64Array], + "isBooleanObject": [Function isBooleanObject], + "isBoxedPrimitive": [Function isBoxedPrimitive], + "isCryptoKey": [Function value], + "isDataView": [Function isDataView], + "isDate": [Function isDate], + "isExternal": [Function isExternal], + "isFloat32Array": [Function isFloat32Array], + "isFloat64Array": [Function isFloat64Array], + "isGeneratorFunction": [Function isGeneratorFunction], + "isGeneratorObject": [Function isGeneratorObject], + "isInt16Array": [Function isInt16Array], + "isInt32Array": [Function isInt32Array], + "isInt8Array": [Function isInt8Array], + "isKeyObject": [Function value], + "isMap": [Function isMap], + "isMapIterator": [Function isMapIterator], + "isModuleNamespaceObject": [Function isModuleNamespaceObject], + "isNativeError": [Function isNativeError], + "isNumberObject": [Function isNumberObject], + "isPromise": [Function isPromise], + "isProxy": [Function isProxy], + "isRegExp": [Function isRegExp], + "isSet": [Function isSet], + "isSetIterator": [Function isSetIterator], + "isSharedArrayBuffer": [Function isSharedArrayBuffer], + "isStringObject": [Function isStringObject], + "isSymbolObject": [Function isSymbolObject], + "isTypedArray": [Function isTypedArray], + "isUint16Array": [Function isUint16Array], + "isUint32Array": [Function isUint32Array], + "isUint8Array": [Function isUint8Array], + "isUint8ClampedArray": [Function isUint8ClampedArray], + "isWeakMap": [Function isWeakMap], + "isWeakSet": [Function isWeakSet], + }, + }, + }, + "atob": [Function atob], + "btoa": [Function btoa], + "clearImmediate": [Function clearImmediate], + "clearInterval": [Function clearInterval], + "clearTimeout": [Function clearTimeout], + "crypto": Crypto {}, + "fetch": [Function fetch], + "global": [Circular], + "navigator": Navigator {}, + "performance": Performance { + Symbol(kEvents): Map {}, + Symbol(events.maxEventTargetListeners): 10, + Symbol(events.maxEventTargetListenersWarned): false, + Symbol(kHandlers): Map {}, + Symbol(performance): true, + }, + "queueMicrotask": [Function queueMicrotask], + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + "structuredClone": [Function structuredClone], + Symbol(vitest:SAFE_TIMERS): Object { + "clearImmediate": [Function clearImmediate], + "clearInterval": [Function clearInterval], + "clearTimeout": [Function clearTimeout], + "nextTick": [Function nextTick], + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + }, +} + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-loose-static-this-exec.test.js:15:15 + 13| var _getA = { _: () => this }; + 14| const { self, getA } = A.extract(); + 15| expect(self).toBe(A); + | ^ + 16| expect(getA()).toBe(A); + 17| const oldA = A; + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[71/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-nested-class-extends-computed-exec.test.js > exec +AssertionError: expected [Function] to not throw an error but 'TypeError: Private element is not pre…' was thrown + +- Expected: +undefined + ++ Received: +"TypeError: Private element is not present on this object" + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-nested-class-extends-computed-exec.test.js:31:9 + 29| expect(() => { + 30| f.test(); + 31| }).not.toThrow(); + | ^ + 32| }) + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[72/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-property-with-transform-exec.test.js > exec +TypeError: Private element is not present on this object + ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 + ❯ Function.test fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-property-with-transform-exec.test.js:32:166 + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-before-property-with-transform-exec.test.js:111:6 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[73/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-cast-to-boolean-exec.test.js > exec +TypeError: Private element is not present on this object + ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 + ❯ Function.testIf fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-cast-to-boolean-exec.test.js:7:8 + 5| class C { + 6| static testIf(o) { + 7| if (_assertClassBrand(C, o, _a)._.b.c.d) { + | ^ + 8| return true; + 9| } + ❯ Function.testNullish fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-cast-to-boolean-exec.test.js:89:14 + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-cast-to-boolean-exec.test.js:105:4 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[74/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-delete-property-exec.test.js > exec +TypeError: Private element is not present on this object + ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 + ❯ Function.testNull fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-delete-property-exec.test.js:57:18 + 55| return deep; + 56| } + 57| expect(delete _assertClassBrand(Foo, deep?.very.o?.Foo, _self)._.un… + | ^ + 58| expect(delete _assertClassBrand(Foo, o?.Foo, _self)._.unicorn).toBe… + 59| expect(delete _assertClassBrand(Foo, o?.Foo, _self)._.self.unicorn)… + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-delete-property-exec.test.js:93:6 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[75/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-delete-property-with-transform-exec.test.js > exec +AssertionError: expected function to throw an error, but it didn't + ❯ Function.test fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-delete-property-with-transform-exec.test.js:41:7 + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-delete-property-with-transform-exec.test.js:158:6 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[76/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-property-with-transform-exec.test.js > exec +TypeError: Private element is not present on this object + ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:3:9 + ❯ Function.test fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-property-with-transform-exec.test.js:35:269 + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-optional-chain-optional-property-with-transform-exec.test.js:114:6 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[77/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-static-class-binding-exec.test.js > exec +AssertionError: expected null to be [Function A] // Object.is equality + +- Expected: +[Function A] + ++ Received: +null + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-static-class-binding-exec.test.js:17:32 + 15| A = null; + 16| expect(oldA.extract().self).toBe(oldA); + 17| expect(oldA.extract().getA()).toBe(oldA); + | ^ + 18| }) + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[78/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-static-self-field-exec.test.js > exec +ReferenceError: Foo is not defined + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-static-self-field-exec.test.js:14:15 + 12| }; + 13| } + 14| }, _x = { _: Foo }, _defineProperty(_Foo, "y", Foo), _Foo); + | ^ + 15| const { x, y } = f.extract(); + 16| expect(x).toBe(f); + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[79/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-static-shadow-exec.test.js > exec +TypeError: e.has is not a function + ❯ _assertClassBrand ../../node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:2:44 + ❯ func fixtures/babel-plugin-transform-class-properties-test-fixtures-private-static-shadow-exec.test.js:10:12 + 8| const func = () => { + 9| const Test = 3; + 10| return _assertClassBrand(Test, this, _x)._ + Test; + | ^ + 11| }; + 12| return func() + Test; + ❯ Function.method fixtures/babel-plugin-transform-class-properties-test-fixtures-private-static-shadow-exec.test.js:12:11 + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-static-shadow-exec.test.js:16:14 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[80/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-static-this-exec.test.js > exec +AssertionError: expected { Object: [Function Object], …(133), …(6) } to be [Function A] // Object.is equality + +- Expected: +[Function A] + ++ Received: +Object { + "__vitest_environment__": "node", + "__vitest_required__": Object { + "timers": Object { + "_unrefActive": [Function deprecated], + "active": [Function deprecated], + "clearImmediate": [Function clearImmediate], + "clearInterval": [Function clearInterval], + "clearTimeout": [Function clearTimeout], + "enroll": [Function deprecated], + "promises": Object { + "scheduler": Scheduler { + Symbol(kScheduler): true, + }, + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + }, + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + "unenroll": [Function deprecated], + }, + "util": Object { + "MIMEParams": [Function MIMEParams], + "MIMEType": [Function MIMEType], + "TextDecoder": [Function TextDecoder], + "TextEncoder": [Function TextEncoder], + "_errnoException": [Function _errnoException], + "_exceptionWithHostPort": [Function _exceptionWithHostPort], + "_extend": [Function deprecated], + "aborted": [Function aborted], + "callbackify": [Function callbackify], + "debug": [Function debuglog], + "debuglog": [Function debuglog], + "deprecate": [Function deprecate], + "format": [Function format], + "formatWithOptions": [Function formatWithOptions], + "getCallSite": [Function getCallSite], + "getSystemErrorMap": [Function getSystemErrorMap], + "getSystemErrorName": [Function getSystemErrorName], + "inherits": [Function inherits], + "inspect": [Function inspect], + "isArray": [Function deprecated], + "isBoolean": [Function deprecated], + "isBuffer": [Function deprecated], + "isDate": [Function deprecated], + "isDeepStrictEqual": [Function isDeepStrictEqual], + "isError": [Function deprecated], + "isFunction": [Function deprecated], + "isNull": [Function deprecated], + "isNullOrUndefined": [Function deprecated], + "isNumber": [Function deprecated], + "isObject": [Function deprecated], + "isPrimitive": [Function deprecated], + "isRegExp": [Function deprecated], + "isString": [Function deprecated], + "isSymbol": [Function deprecated], + "isUndefined": [Function deprecated], + "log": [Function deprecated], + "parseArgs": [Function parseArgs], + "parseEnv": [Function parseEnv], + "promisify": [Function promisify], + "stripVTControlCharacters": [Function stripVTControlCharacters], + "styleText": [Function styleText], + "toUSVString": [Function toUSVString], + "transferableAbortController": [Function transferableAbortController], + "transferableAbortSignal": [Function transferableAbortSignal], + "types": Object { + "isAnyArrayBuffer": [Function isAnyArrayBuffer], + "isArgumentsObject": [Function isArgumentsObject], + "isArrayBuffer": [Function isArrayBuffer], + "isArrayBufferView": [Function isView], + "isAsyncFunction": [Function isAsyncFunction], + "isBigInt64Array": [Function isBigInt64Array], + "isBigIntObject": [Function isBigIntObject], + "isBigUint64Array": [Function isBigUint64Array], + "isBooleanObject": [Function isBooleanObject], + "isBoxedPrimitive": [Function isBoxedPrimitive], + "isCryptoKey": [Function value], + "isDataView": [Function isDataView], + "isDate": [Function isDate], + "isExternal": [Function isExternal], + "isFloat32Array": [Function isFloat32Array], + "isFloat64Array": [Function isFloat64Array], + "isGeneratorFunction": [Function isGeneratorFunction], + "isGeneratorObject": [Function isGeneratorObject], + "isInt16Array": [Function isInt16Array], + "isInt32Array": [Function isInt32Array], + "isInt8Array": [Function isInt8Array], + "isKeyObject": [Function value], + "isMap": [Function isMap], + "isMapIterator": [Function isMapIterator], + "isModuleNamespaceObject": [Function isModuleNamespaceObject], + "isNativeError": [Function isNativeError], + "isNumberObject": [Function isNumberObject], + "isPromise": [Function isPromise], + "isProxy": [Function isProxy], + "isRegExp": [Function isRegExp], + "isSet": [Function isSet], + "isSetIterator": [Function isSetIterator], + "isSharedArrayBuffer": [Function isSharedArrayBuffer], + "isStringObject": [Function isStringObject], + "isSymbolObject": [Function isSymbolObject], + "isTypedArray": [Function isTypedArray], + "isUint16Array": [Function isUint16Array], + "isUint32Array": [Function isUint32Array], + "isUint8Array": [Function isUint8Array], + "isUint8ClampedArray": [Function isUint8ClampedArray], + "isWeakMap": [Function isWeakMap], + "isWeakSet": [Function isWeakSet], + }, + }, + }, + "atob": [Function atob], + "btoa": [Function btoa], + "clearImmediate": [Function clearImmediate], + "clearInterval": [Function clearInterval], + "clearTimeout": [Function clearTimeout], + "crypto": Crypto {}, + "fetch": [Function fetch], + "global": [Circular], + "navigator": Navigator {}, + "performance": Performance { + Symbol(kEvents): Map {}, + Symbol(events.maxEventTargetListeners): 10, + Symbol(events.maxEventTargetListenersWarned): false, + Symbol(kHandlers): Map {}, + Symbol(performance): true, + }, + "queueMicrotask": [Function queueMicrotask], + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + "structuredClone": [Function structuredClone], + Symbol(vitest:SAFE_TIMERS): Object { + "clearImmediate": [Function clearImmediate], + "clearInterval": [Function clearInterval], + "clearTimeout": [Function clearTimeout], + "nextTick": [Function nextTick], + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + }, +} + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-static-this-exec.test.js:15:15 + 13| var _getA = { _: () => this }; + 14| const { self, getA } = A.extract(); + 15| expect(self).toBe(A); + | ^ + 16| expect(getA()).toBe(A); + 17| const oldA = A; + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[81/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-private-tagged-template-exec.test.js > exec +AssertionError: expected undefined to be Foo{} // Object.is equality + +- Expected: +Foo {} + ++ Received: +undefined + + ❯ new Foo fixtures/babel-plugin-transform-class-properties-test-fixtures-private-tagged-template-exec.test.js:18:22 + 16| expect(receiver).toBe(this); + 17| const receiver2 = _classPrivateFieldGet(_tag, this)`tagged template… + 18| expect(receiver2).toBe(this); + | ^ + 19| } + 20| } + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-private-tagged-template-exec.test.js:21:2 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[82/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-public-computed-toPrimitive-exec.test.js > exec +AssertionError: expected [Function] to throw error including '@@toPrimitive must return a primitive…' but got 'Cannot convert object to primitive va…' + +Expected: "@@toPrimitive must return a primitive value." +Received: "Cannot convert object to primitive value" + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-public-computed-toPrimitive-exec.test.js:37:5 + 35| return 0; + 36| } + 37| }).toThrow("@@toPrimitive must return a primitive value."); + | ^ + 38| expect(() => class { + 39| static get [arrayLike]() { + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[83/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-public-loose-static-class-binding-exec.test.js > exec +AssertionError: expected null to be [Function A] // Object.is equality + +- Expected: +[Function A] + ++ Received: +null + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-public-loose-static-class-binding-exec.test.js:10:22 + 8| A = null; + 9| expect(oldA.self).toBe(oldA); + 10| expect(oldA.getA()).toBe(oldA); + | ^ + 11| }) + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[84/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-public-loose-static-infer-name-exec.test.js > exec +AssertionError: expected '_Class' to be 'Foo' // Object.is equality + +Expected: "Foo" +Received: "_Class" + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-public-loose-static-infer-name-exec.test.js:8:19 + 6| expect(Foo.num).toBe(0); + 7| expect(Foo.num = 1).toBe(1); + 8| expect(Foo.name).toBe("Foo"); + | ^ + 9| }) + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[85/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-public-loose-static-this-exec.test.js > exec +AssertionError: expected { Object: [Function Object], …(133), …(6) } to be [Function A] // Object.is equality + +- Expected: +[Function A] + ++ Received: +Object { + "__vitest_environment__": "node", + "__vitest_required__": Object { + "timers": Object { + "_unrefActive": [Function deprecated], + "active": [Function deprecated], + "clearImmediate": [Function clearImmediate], + "clearInterval": [Function clearInterval], + "clearTimeout": [Function clearTimeout], + "enroll": [Function deprecated], + "promises": Object { + "scheduler": Scheduler { + Symbol(kScheduler): true, + }, + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + }, + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + "unenroll": [Function deprecated], + }, + "util": Object { + "MIMEParams": [Function MIMEParams], + "MIMEType": [Function MIMEType], + "TextDecoder": [Function TextDecoder], + "TextEncoder": [Function TextEncoder], + "_errnoException": [Function _errnoException], + "_exceptionWithHostPort": [Function _exceptionWithHostPort], + "_extend": [Function deprecated], + "aborted": [Function aborted], + "callbackify": [Function callbackify], + "debug": [Function debuglog], + "debuglog": [Function debuglog], + "deprecate": [Function deprecate], + "format": [Function format], + "formatWithOptions": [Function formatWithOptions], + "getCallSite": [Function getCallSite], + "getSystemErrorMap": [Function getSystemErrorMap], + "getSystemErrorName": [Function getSystemErrorName], + "inherits": [Function inherits], + "inspect": [Function inspect], + "isArray": [Function deprecated], + "isBoolean": [Function deprecated], + "isBuffer": [Function deprecated], + "isDate": [Function deprecated], + "isDeepStrictEqual": [Function isDeepStrictEqual], + "isError": [Function deprecated], + "isFunction": [Function deprecated], + "isNull": [Function deprecated], + "isNullOrUndefined": [Function deprecated], + "isNumber": [Function deprecated], + "isObject": [Function deprecated], + "isPrimitive": [Function deprecated], + "isRegExp": [Function deprecated], + "isString": [Function deprecated], + "isSymbol": [Function deprecated], + "isUndefined": [Function deprecated], + "log": [Function deprecated], + "parseArgs": [Function parseArgs], + "parseEnv": [Function parseEnv], + "promisify": [Function promisify], + "stripVTControlCharacters": [Function stripVTControlCharacters], + "styleText": [Function styleText], + "toUSVString": [Function toUSVString], + "transferableAbortController": [Function transferableAbortController], + "transferableAbortSignal": [Function transferableAbortSignal], + "types": Object { + "isAnyArrayBuffer": [Function isAnyArrayBuffer], + "isArgumentsObject": [Function isArgumentsObject], + "isArrayBuffer": [Function isArrayBuffer], + "isArrayBufferView": [Function isView], + "isAsyncFunction": [Function isAsyncFunction], + "isBigInt64Array": [Function isBigInt64Array], + "isBigIntObject": [Function isBigIntObject], + "isBigUint64Array": [Function isBigUint64Array], + "isBooleanObject": [Function isBooleanObject], + "isBoxedPrimitive": [Function isBoxedPrimitive], + "isCryptoKey": [Function value], + "isDataView": [Function isDataView], + "isDate": [Function isDate], + "isExternal": [Function isExternal], + "isFloat32Array": [Function isFloat32Array], + "isFloat64Array": [Function isFloat64Array], + "isGeneratorFunction": [Function isGeneratorFunction], + "isGeneratorObject": [Function isGeneratorObject], + "isInt16Array": [Function isInt16Array], + "isInt32Array": [Function isInt32Array], + "isInt8Array": [Function isInt8Array], + "isKeyObject": [Function value], + "isMap": [Function isMap], + "isMapIterator": [Function isMapIterator], + "isModuleNamespaceObject": [Function isModuleNamespaceObject], + "isNativeError": [Function isNativeError], + "isNumberObject": [Function isNumberObject], + "isPromise": [Function isPromise], + "isProxy": [Function isProxy], + "isRegExp": [Function isRegExp], + "isSet": [Function isSet], + "isSetIterator": [Function isSetIterator], + "isSharedArrayBuffer": [Function isSharedArrayBuffer], + "isStringObject": [Function isStringObject], + "isSymbolObject": [Function isSymbolObject], + "isTypedArray": [Function isTypedArray], + "isUint16Array": [Function isUint16Array], + "isUint32Array": [Function isUint32Array], + "isUint8Array": [Function isUint8Array], + "isUint8ClampedArray": [Function isUint8ClampedArray], + "isWeakMap": [Function isWeakMap], + "isWeakSet": [Function isWeakSet], + }, + }, + }, + "atob": [Function atob], + "btoa": [Function btoa], + "clearImmediate": [Function clearImmediate], + "clearInterval": [Function clearInterval], + "clearTimeout": [Function clearTimeout], + "crypto": Crypto {}, + "fetch": [Function fetch], + "global": [Circular], + "navigator": Navigator {}, + "performance": Performance { + Symbol(kEvents): Map {}, + Symbol(events.maxEventTargetListeners): 10, + Symbol(events.maxEventTargetListenersWarned): false, + Symbol(kHandlers): Map {}, + Symbol(performance): true, + }, + "queueMicrotask": [Function queueMicrotask], + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + "structuredClone": [Function structuredClone], + Symbol(vitest:SAFE_TIMERS): Object { + "clearImmediate": [Function clearImmediate], + "clearInterval": [Function clearInterval], + "clearTimeout": [Function clearTimeout], + "nextTick": [Function nextTick], + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + }, +} + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-public-loose-static-this-exec.test.js:8:15 + 6| A.getA = () => this; + 7| const { self, getA } = A; + 8| expect(self).toBe(A); + | ^ + 9| expect(getA()).toBe(A); + 10| const oldA = A; + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[86/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-public-static-class-binding-exec.test.js > exec +AssertionError: expected null to be [Function A] // Object.is equality + +- Expected: +[Function A] + ++ Received: +null + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-public-static-class-binding-exec.test.js:11:22 + 9| A = null; + 10| expect(oldA.self).toBe(oldA); + 11| expect(oldA.getA()).toBe(oldA); + | ^ + 12| }) + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[87/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-public-static-infer-name-exec.test.js > exec +AssertionError: expected '_Class' to be 'Foo' // Object.is equality + +Expected: "Foo" +Received: "_Class" + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-public-static-infer-name-exec.test.js:9:19 + 7| expect(Foo.num).toBe(0); + 8| expect(Foo.num = 1).toBe(1); + 9| expect(Foo.name).toBe("Foo"); + | ^ + 10| }) + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[88/98]⎯ + + FAIL fixtures/babel-plugin-transform-class-properties-test-fixtures-public-static-this-exec.test.js > exec +AssertionError: expected { Object: [Function Object], …(133), …(6) } to be [Function A] // Object.is equality + +- Expected: +[Function A] + ++ Received: +Object { + "__vitest_environment__": "node", + "__vitest_required__": Object { + "timers": Object { + "_unrefActive": [Function deprecated], + "active": [Function deprecated], + "clearImmediate": [Function clearImmediate], + "clearInterval": [Function clearInterval], + "clearTimeout": [Function clearTimeout], + "enroll": [Function deprecated], + "promises": Object { + "scheduler": Scheduler { + Symbol(kScheduler): true, + }, + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + }, + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + "unenroll": [Function deprecated], + }, + "util": Object { + "MIMEParams": [Function MIMEParams], + "MIMEType": [Function MIMEType], + "TextDecoder": [Function TextDecoder], + "TextEncoder": [Function TextEncoder], + "_errnoException": [Function _errnoException], + "_exceptionWithHostPort": [Function _exceptionWithHostPort], + "_extend": [Function deprecated], + "aborted": [Function aborted], + "callbackify": [Function callbackify], + "debug": [Function debuglog], + "debuglog": [Function debuglog], + "deprecate": [Function deprecate], + "format": [Function format], + "formatWithOptions": [Function formatWithOptions], + "getCallSite": [Function getCallSite], + "getSystemErrorMap": [Function getSystemErrorMap], + "getSystemErrorName": [Function getSystemErrorName], + "inherits": [Function inherits], + "inspect": [Function inspect], + "isArray": [Function deprecated], + "isBoolean": [Function deprecated], + "isBuffer": [Function deprecated], + "isDate": [Function deprecated], + "isDeepStrictEqual": [Function isDeepStrictEqual], + "isError": [Function deprecated], + "isFunction": [Function deprecated], + "isNull": [Function deprecated], + "isNullOrUndefined": [Function deprecated], + "isNumber": [Function deprecated], + "isObject": [Function deprecated], + "isPrimitive": [Function deprecated], + "isRegExp": [Function deprecated], + "isString": [Function deprecated], + "isSymbol": [Function deprecated], + "isUndefined": [Function deprecated], + "log": [Function deprecated], + "parseArgs": [Function parseArgs], + "parseEnv": [Function parseEnv], + "promisify": [Function promisify], + "stripVTControlCharacters": [Function stripVTControlCharacters], + "styleText": [Function styleText], + "toUSVString": [Function toUSVString], + "transferableAbortController": [Function transferableAbortController], + "transferableAbortSignal": [Function transferableAbortSignal], + "types": Object { + "isAnyArrayBuffer": [Function isAnyArrayBuffer], + "isArgumentsObject": [Function isArgumentsObject], + "isArrayBuffer": [Function isArrayBuffer], + "isArrayBufferView": [Function isView], + "isAsyncFunction": [Function isAsyncFunction], + "isBigInt64Array": [Function isBigInt64Array], + "isBigIntObject": [Function isBigIntObject], + "isBigUint64Array": [Function isBigUint64Array], + "isBooleanObject": [Function isBooleanObject], + "isBoxedPrimitive": [Function isBoxedPrimitive], + "isCryptoKey": [Function value], + "isDataView": [Function isDataView], + "isDate": [Function isDate], + "isExternal": [Function isExternal], + "isFloat32Array": [Function isFloat32Array], + "isFloat64Array": [Function isFloat64Array], + "isGeneratorFunction": [Function isGeneratorFunction], + "isGeneratorObject": [Function isGeneratorObject], + "isInt16Array": [Function isInt16Array], + "isInt32Array": [Function isInt32Array], + "isInt8Array": [Function isInt8Array], + "isKeyObject": [Function value], + "isMap": [Function isMap], + "isMapIterator": [Function isMapIterator], + "isModuleNamespaceObject": [Function isModuleNamespaceObject], + "isNativeError": [Function isNativeError], + "isNumberObject": [Function isNumberObject], + "isPromise": [Function isPromise], + "isProxy": [Function isProxy], + "isRegExp": [Function isRegExp], + "isSet": [Function isSet], + "isSetIterator": [Function isSetIterator], + "isSharedArrayBuffer": [Function isSharedArrayBuffer], + "isStringObject": [Function isStringObject], + "isSymbolObject": [Function isSymbolObject], + "isTypedArray": [Function isTypedArray], + "isUint16Array": [Function isUint16Array], + "isUint32Array": [Function isUint32Array], + "isUint8Array": [Function isUint8Array], + "isUint8ClampedArray": [Function isUint8ClampedArray], + "isWeakMap": [Function isWeakMap], + "isWeakSet": [Function isWeakSet], + }, + }, + }, + "atob": [Function atob], + "btoa": [Function btoa], + "clearImmediate": [Function clearImmediate], + "clearInterval": [Function clearInterval], + "clearTimeout": [Function clearTimeout], + "crypto": Crypto {}, + "fetch": [Function fetch], + "global": [Circular], + "navigator": Navigator {}, + "performance": Performance { + Symbol(kEvents): Map {}, + Symbol(events.maxEventTargetListeners): 10, + Symbol(events.maxEventTargetListenersWarned): false, + Symbol(kHandlers): Map {}, + Symbol(performance): true, + }, + "queueMicrotask": [Function queueMicrotask], + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + "structuredClone": [Function structuredClone], + Symbol(vitest:SAFE_TIMERS): Object { + "clearImmediate": [Function clearImmediate], + "clearInterval": [Function clearInterval], + "clearTimeout": [Function clearTimeout], + "nextTick": [Function nextTick], + "setImmediate": [Function setImmediate], + "setInterval": [Function setInterval], + "setTimeout": [Function setTimeout], + }, +} + + ❯ fixtures/babel-plugin-transform-class-properties-test-fixtures-public-static-this-exec.test.js:9:15 + 7| _defineProperty(A, "getA", () => this); + 8| const { self, getA } = A; + 9| expect(self).toBe(A); + | ^ + 10| expect(getA()).toBe(A); + 11| const oldA = A; + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[89/98]⎯ FAIL fixtures/babel-plugin-transform-optional-chaining-test-fixtures-assumption-noDocumentAll-parenthesized-expression-member-call-exec.test.js > exec TypeError: Cannot read properties of undefined (reading 'x') @@ -27,7 +1517,7 @@ TypeError: Cannot read properties of undefined (reading 'x') ❯ Foo.test fixtures/babel-plugin-transform-optional-chaining-test-fixtures-assumption-noDocumentAll-parenthesized-expression-member-call-exec.test.js:25:63 ❯ fixtures/babel-plugin-transform-optional-chaining-test-fixtures-assumption-noDocumentAll-parenthesized-expression-member-call-exec.test.js:68:12 -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/7]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[90/98]⎯ FAIL fixtures/babel-plugin-transform-optional-chaining-test-fixtures-general-parenthesized-expression-member-call-exec.test.js > exec TypeError: Cannot read properties of undefined (reading 'x') @@ -41,7 +1531,7 @@ TypeError: Cannot read properties of undefined (reading 'x') ❯ Foo.test fixtures/babel-plugin-transform-optional-chaining-test-fixtures-general-parenthesized-expression-member-call-exec.test.js:25:63 ❯ fixtures/babel-plugin-transform-optional-chaining-test-fixtures-general-parenthesized-expression-member-call-exec.test.js:68:12 -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/7]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[91/98]⎯ FAIL fixtures/babel-plugin-transform-optional-chaining-test-fixtures-general-parenthesized-expression-member-call-loose-exec.test.js > exec TypeError: Cannot read properties of undefined (reading 'x') @@ -55,7 +1545,7 @@ TypeError: Cannot read properties of undefined (reading 'x') ❯ Foo.test fixtures/babel-plugin-transform-optional-chaining-test-fixtures-general-parenthesized-expression-member-call-loose-exec.test.js:25:63 ❯ fixtures/babel-plugin-transform-optional-chaining-test-fixtures-general-parenthesized-expression-member-call-loose-exec.test.js:68:12 -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/7]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[92/98]⎯ FAIL fixtures/babel-preset-env-test-fixtures-plugins-integration-issue-15170-exec.test.js > exec AssertionError: expected [Function] to not throw an error but 'ReferenceError: x is not defined' was thrown @@ -73,7 +1563,7 @@ undefined | ^ 7| }) -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/7]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[93/98]⎯ FAIL fixtures/babel-preset-env-test-fixtures-sanity-check-es2015-constants-exec.test.js > exec TypeError: Assignment to constant variable. @@ -84,7 +1574,7 @@ TypeError: Assignment to constant variable. | ^ 6| }) -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/7]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[94/98]⎯ FAIL fixtures/babel-preset-env-test-fixtures-sanity-regex-dot-all-exec.test.js > exec AssertionError: expected false to be true // Object.is equality @@ -103,5 +1593,5 @@ AssertionError: expected false to be true // Object.is equality 11| expect(/hello.world/su.test(input)).toBe(true); 12| }) -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[7/7]⎯ +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[95/98]⎯ diff --git a/tasks/transform_conformance/src/constants.rs b/tasks/transform_conformance/src/constants.rs index a25f3183a49414..4423ddb4fbdae2 100644 --- a/tasks/transform_conformance/src/constants.rs +++ b/tasks/transform_conformance/src/constants.rs @@ -3,7 +3,7 @@ pub const PLUGINS: &[&str] = &[ // // ES2024 // "babel-plugin-transform-unicode-sets-regex", // // ES2022 - // "babel-plugin-transform-class-properties", + "babel-plugin-transform-class-properties", "babel-plugin-transform-class-static-block", // "babel-plugin-transform-private-methods", // "babel-plugin-transform-private-property-in-object", @@ -63,7 +63,6 @@ pub const PLUGINS: &[&str] = &[ pub const PLUGINS_NOT_SUPPORTED_YET: &[&str] = &[ "proposal-decorators", - "transform-class-properties", "transform-classes", "transform-destructuring", "transform-modules-commonjs",