From 9778298fdb976716b9e2fcef625a255107b924bd Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:24:20 +0000 Subject: [PATCH] feat(transformer): class properties transform (#7011) Add class properties transform. Implementation is incomplete. Notable missing parts: * Scopes are not updated where property initializers move from class body into class constructor / `_super` function. * Does not handle binding shadowing problems when property initializers move from class body into class constructor. * `this` and references to class name in static property initializers need to be transformed to point to a temp var. * Not all usages of private properties are supported (see below). * Code which is moved to outside of class body is not transformed by other transforms for class declarations (works OK for class expressions). This includes static property initializers, static blocks, and computed property/method keys. * Only basic checks for whether computed property/method keys may have side effects. * Numerous other small issues noted in TODO comments through the code. ### Private properties Currently does not handle the following usages of private properties: ```js class Class { #prop; static #static; method() { object?.#prop; object?.#prop(); [object.#prop] = [1]; ({x: object.#prop} = {x: 1}); object.#prop`xyz`; object?.#static; object?.#static(); [object.#static] = [1]; ({x: object.#static} = {x: 1}); object.#static`xyz`; } } ``` --- .../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 323154111b11b..c09c984269b36 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 7701c15a5378c..66fb24af9e44c 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 577b505d625dd..143b711d06642 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 becd2424c9f24..0000000000000 --- 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 0000000000000..747b3914ea48e --- /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 0000000000000..76ae7cee86c82 --- /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 0000000000000..35b2faf2c9cd1 --- /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 0000000000000..6f0a9d6a47e70 --- /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 0000000000000..3b7e923dfffbe --- /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 3b85ffc7c6dca..a29a5a8dc1f54 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 c0ad06a5fb083..4f4779097c5e1 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 d20c05ce6909e..8bc0f6da40075 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 d667de94c5932..639b97e1269cd 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 a7767c69b1b2b..dda7a89cc0eef 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 08c761a7cc66e..651dd00bc91b3 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 a25f3183a4941..4423ddb4fbdae 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",