diff --git a/crates/oxc_transformer/src/common/helper_loader.rs b/crates/oxc_transformer/src/common/helper_loader.rs index 60a783e2679a6e..e66e6dc2f81ec7 100644 --- a/crates/oxc_transformer/src/common/helper_loader.rs +++ b/crates/oxc_transformer/src/common/helper_loader.rs @@ -153,6 +153,8 @@ pub enum Helper { ClassPrivateFieldSet2, AssertClassBrand, ToSetter, + ClassPrivateFieldLooseKey, + ClassPrivateFieldLooseBase, } impl Helper { @@ -174,6 +176,8 @@ impl Helper { Self::ClassPrivateFieldSet2 => "classPrivateFieldSet2", Self::AssertClassBrand => "assertClassBrand", Self::ToSetter => "toSetter", + Self::ClassPrivateFieldLooseKey => "classPrivateFieldLooseKey", + Self::ClassPrivateFieldLooseBase => "classPrivateFieldLooseBase", } } } diff --git a/crates/oxc_transformer/src/compiler_assumptions.rs b/crates/oxc_transformer/src/compiler_assumptions.rs index 143b711d06642f..191d756ebfcb68 100644 --- a/crates/oxc_transformer/src/compiler_assumptions.rs +++ b/crates/oxc_transformer/src/compiler_assumptions.rs @@ -66,7 +66,6 @@ pub struct CompilerAssumptions { pub private_fields_as_symbols: bool, #[serde(default)] - #[deprecated = "Not Implemented"] pub private_fields_as_properties: bool, #[serde(default)] diff --git a/crates/oxc_transformer/src/es2022/class_properties/class.rs b/crates/oxc_transformer/src/es2022/class_properties/class.rs index 597d4afd5fbf65..8a0da34579f7ac 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/class.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/class.rs @@ -104,29 +104,40 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { let mut exprs = ctx.ast.vec_with_capacity(expr_count); - // Insert `_prop = new WeakMap()` expressions for private instance props. + // Insert `_prop = new WeakMap()` expressions for private instance props + // (or `_prop = _classPrivateFieldLooseKey("prop")` if loose mode). // 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, ctx); - - if prop.is_static { - return None; - } + // Insert `var _prop;` declarations 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;` + // TODO(improve-on-babel): Simplify this. + if self.private_fields_as_properties { + exprs.extend(private_props.props.iter().map(|(name, prop)| { + // Insert `var _prop;` declaration + self.ctx.var_declarations.insert_var(&prop.binding, ctx); + + // `_prop = _classPrivateFieldLooseKey("prop")` + let value = self.create_private_prop_key_loose(name, ctx); + create_assignment(&prop.binding, value, ctx) + })); + } else { + let mut weakmap_symbol_id = None; + exprs.extend(private_props.props.values().filter_map(|prop| { + // Insert `var _prop;` declaration + self.ctx.var_declarations.insert_var(&prop.binding, ctx); - // `_prop = new WeakMap()` - Some(create_assignment( - &prop.binding, - create_new_weakmap(&mut weakmap_symbol_id, ctx), - ctx, - )) - })); + if prop.is_static { + return None; + } + + // `_prop = new WeakMap()` + let value = create_new_weakmap(&mut weakmap_symbol_id, ctx); + Some(create_assignment(&prop.binding, value, ctx)) + })); + } } // Insert computed key initializers @@ -216,23 +227,31 @@ impl<'a, 'ctx> ClassProperties<'a, '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.private_fields_as_properties { + self.ctx.statement_injector.insert_many_before( + &stmt_address, + private_props.props.iter().map(|(name, prop)| { + // `var _prop = _classPrivateFieldLooseKey("prop");` + let value = self.create_private_prop_key_loose(name, ctx); + create_variable_declaration(&prop.binding, value, ctx) + }), + ); + } else { + // 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();` + let value = create_new_weakmap(&mut weakmap_symbol_id, ctx); + Some(create_variable_declaration(&prop.binding, value, ctx)) + }), + ); + } } if !self.insert_after_stmts.is_empty() { @@ -242,6 +261,24 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { } } + /// `_classPrivateFieldLooseKey("prop")` + fn create_private_prop_key_loose( + &self, + name: &Atom<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + self.ctx.helper_call_expr( + Helper::ClassPrivateFieldLooseKey, + SPAN, + ctx.ast.vec1(Argument::from(ctx.ast.expression_string_literal( + SPAN, + name.clone(), + None, + ))), + ctx, + ) + } + /// 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 @@ -600,12 +637,87 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { self.ctx.helper_call_expr(Helper::DefineProperty, SPAN, arguments, ctx) } - /// Create `_classPrivateFieldInitSpec(this, _prop, value)` to be inserted into class constructor. + /// Create init assignment for private instance prop, to be inserted into class constructor. + /// + /// Loose: `Object.defineProperty(this, _prop, {writable: true, value: value})` + /// Not loose: `_classPrivateFieldInitSpec(this, _prop, value)` fn create_private_instance_init_assignment( &mut self, ident: &PrivateIdentifier<'a>, value: Expression<'a>, ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + if self.private_fields_as_properties { + let this = ctx.ast.expression_this(SPAN); + self.create_private_init_assignment_loose(ident, value, this, ctx) + } else { + self.create_private_instance_init_assignment_not_loose(ident, value, ctx) + } + } + + /// `Object.defineProperty(, _prop, {writable: true, value: value})` + fn create_private_init_assignment_loose( + &mut self, + ident: &PrivateIdentifier<'a>, + value: Expression<'a>, + assignee: Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + // `Object.defineProperty` + let object_symbol_id = ctx.scopes().find_binding(ctx.current_scope_id(), "Object"); + let object = ctx.create_ident_expr( + SPAN, + Atom::from("Object"), + object_symbol_id, + ReferenceFlags::Read, + ); + let property = ctx.ast.identifier_name(SPAN, "defineProperty"); + let callee = + Expression::from(ctx.ast.member_expression_static(SPAN, object, property, false)); + + // `{writable: true, value: }` + let prop_def = ctx.ast.expression_object( + SPAN, + ctx.ast.vec_from_array([ + ctx.ast.object_property_kind_object_property( + SPAN, + PropertyKind::Init, + ctx.ast.property_key_identifier_name(SPAN, Atom::from("writable")), + ctx.ast.expression_boolean_literal(SPAN, true), + false, + false, + false, + ), + ctx.ast.object_property_kind_object_property( + SPAN, + PropertyKind::Init, + ctx.ast.property_key_identifier_name(SPAN, Atom::from("value")), + value, + false, + false, + false, + ), + ]), + None, + ); + + 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(assignee), + Argument::from(prop.binding.create_read_expression(ctx)), + Argument::from(prop_def), + ]); + // TODO: Should this have span of original `PropertyDefinition`? + ctx.ast.expression_call(SPAN, callee, NONE, arguments, false) + } + + /// `_classPrivateFieldInitSpec(this, _prop, value)` + fn create_private_instance_init_assignment_not_loose( + &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]; @@ -619,13 +731,58 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { } /// Insert after class: + /// + /// Not loose: /// * Class declaration: `var _prop = {_: value};` /// * Class expression: `_prop = {_: value}` + /// + /// Loose: + /// `Object.defineProperty(Class, _prop, {writable: true, value: value});` fn insert_private_static_init_assignment( &mut self, ident: &PrivateIdentifier<'a>, value: Expression<'a>, ctx: &mut TraverseCtx<'a>, + ) { + if self.private_fields_as_properties { + self.insert_private_static_init_assignment_loose(ident, value, ctx); + } else { + self.insert_private_static_init_assignment_not_loose(ident, value, ctx); + } + } + + /// Insert after class: + /// `Object.defineProperty(Class, _prop, {writable: true, value: value});` + fn insert_private_static_init_assignment_loose( + &mut self, + ident: &PrivateIdentifier<'a>, + value: Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + // TODO: This logic appears elsewhere. De-duplicate it. + let class_binding = if self.is_declaration { + // Class declarations always have a name except `export default class {}`. + // For default export, binding is created when static prop found in 1st pass. + self.class_bindings.name.as_ref().unwrap() + } else { + // Binding is created when static prop found in 1st pass. + self.class_bindings.temp.as_ref().unwrap() + }; + + let assignee = class_binding.create_read_expression(ctx); + let assignment = self.create_private_init_assignment_loose(ident, value, assignee, ctx); + self.insert_expr_after_class(assignment, ctx); + } + + /// Insert after class: + /// + /// * Class declaration: `var _prop = {_: value};` + /// * Class expression: `_prop = {_: value}` + fn insert_private_static_init_assignment_not_loose( + &mut self, + ident: &PrivateIdentifier<'a>, + value: Expression<'a>, + ctx: &mut TraverseCtx<'a>, ) { // `_prop = {_: value}` let property = ctx.ast.object_property_kind_object_property( @@ -791,6 +948,9 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { /// * `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. +/// +/// This is an optimization to avoid looking up the symbol for `WeakMap` over and over when defining +/// multiple private properties. #[expect(clippy::option_option)] fn create_new_weakmap<'a>( symbol_id: &mut Option>, diff --git a/crates/oxc_transformer/src/es2022/class_properties/mod.rs b/crates/oxc_transformer/src/es2022/class_properties/mod.rs index 08f27dc1e9317b..2fb2b6f348ebec 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/mod.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/mod.rs @@ -181,6 +181,8 @@ pub struct ClassProperties<'a, 'ctx> { // /// If `true`, set properties with `=`, instead of `_defineProperty` helper. set_public_class_fields: bool, + /// If `true`, record private properties as string keys + private_fields_as_properties: bool, /// If `true`, transform static blocks. transform_static_blocks: bool, @@ -227,9 +229,13 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { ) -> Self { // TODO: Raise error if these 2 options are inconsistent let set_public_class_fields = options.loose || ctx.assumptions.set_public_class_fields; + // TODO: Raise error if these 2 options are inconsistent + let private_fields_as_properties = + options.loose || ctx.assumptions.private_fields_as_properties; Self { set_public_class_fields, + private_fields_as_properties, transform_static_blocks, ctx, private_props_stack: PrivatePropsStack::default(), diff --git a/crates/oxc_transformer/src/es2022/class_properties/private.rs b/crates/oxc_transformer/src/es2022/class_properties/private.rs index e2da02cb53a062..a4176f301a839d 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/private.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/private.rs @@ -14,7 +14,7 @@ use oxc_traverse::{ ast_operations::get_var_name_from_node, Ancestor, BoundIdentifier, TraverseCtx, }; -use crate::common::helper_loader::Helper; +use crate::{common::helper_loader::Helper, TransformCtx}; use super::{ private_props::ResolvedPrivateProp, @@ -28,8 +28,11 @@ use super::{ 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)._` + /// Not loose: + /// * Instance prop: `object.#prop` -> `_classPrivateFieldGet2(_prop, object)` + /// * Static prop: `object.#prop` -> `_assertClassBrand(Class, object, _prop)._` + /// + /// Loose: `object.#prop` -> `_classPrivateFieldLooseBase(object, _prop)[_prop]` // // `#[inline]` so that compiler sees that `expr` is an `Expression::PrivateFieldExpression`. #[inline] @@ -56,11 +59,23 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { }; let ResolvedPrivateProp { prop_binding, class_bindings, is_static, is_declaration } = prop_details; - 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 self.private_fields_as_properties { + // `_classPrivateFieldLooseBase(object, _prop)[_prop]` + return Expression::from(Self::create_private_field_member_expr_loose( + object, + prop_binding, + span, + self.ctx, + ctx, + )); + } + + let prop_ident = prop_binding.create_read_expression(ctx); + if 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_bindings`. @@ -132,10 +147,14 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { /// 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)` + /// Not loose: + /// * Instance prop: `object.#prop(arg)` -> `_classPrivateFieldGet2(_prop, object).call(object, arg)` + /// * Static prop: `object.#prop(arg)` -> `_assertClassBrand(Class, object, _prop)._.call(object, arg)` + /// + /// Loose: + /// * `object.#prop(arg)` -> `_classPrivateFieldLooseBase(object, _prop)[_prop](arg)` /// - /// Output in both cases contains a `CallExpression`, so mutate existing `CallExpression` + /// Output in all 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` @@ -162,6 +181,23 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { unreachable!() }; + if self.private_fields_as_properties { + // `object.#prop(arg)` -> `_classPrivateFieldLooseBase(object, _prop)[_prop](arg)` + let prop_details = self.private_props_stack.find(&field_expr.field); + // TODO: Should never be `None` - only because implementation is incomplete. + let Some(ResolvedPrivateProp { prop_binding, .. }) = prop_details else { return }; + + let object = ctx.ast.move_expression(&mut field_expr.object); + call_expr.callee = Expression::from(Self::create_private_field_member_expr_loose( + object, + prop_binding, + field_expr.span, + self.ctx, + ctx, + )); + return; + } + // TODO: Should never be `None` - only because implementation is incomplete. let Some((callee, object)) = self.transform_private_field_callee(field_expr, ctx) else { return; @@ -300,6 +336,12 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>, ) { + // If `private_fields_as_properties` assumption is enabled, `transform_assignment_target` + // can handle the transform + if self.private_fields_as_properties { + return; + } + // Unfortunately no way to make compiler see that these branches are provably unreachable. // This function is much too large to inline, because `transform_static_assignment_expression` // and `transform_instance_assignment_expression` are inlined into it. @@ -589,7 +631,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { /// Transform update expression (`++` or `--`) where argument is private field. /// - /// Instance prop: + /// Instance prop (not loose): /// /// * `++object.#prop` -> /// ```js @@ -614,7 +656,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { /// ) /// ``` /// - /// Static prop: + /// Static prop (not loose): /// /// * `++object.#prop` -> /// ```js @@ -639,6 +681,10 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { /// ) /// ``` /// + /// Loose: + /// `++object.#prop` -> `++_classPrivateFieldLooseBase(object, _prop)[_prop]` + /// `object.#prop++` -> `_classPrivateFieldLooseBase(object, _prop)[_prop]++` + /// /// Output in all cases contains an `UpdateExpression`, so mutate existing `UpdateExpression` /// rather than creating a new one. // @@ -675,6 +721,20 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { let ResolvedPrivateProp { prop_binding, class_bindings, is_static, is_declaration } = prop_details; + if self.private_fields_as_properties { + // `object.#prop++` -> `_classPrivateFieldLooseBase(object, _prop)[_prop]++` + let object = ctx.ast.move_expression(&mut field_expr.object); + let replacement = Self::create_private_field_member_expr_loose( + object, + prop_binding, + field_expr.span, + self.ctx, + ctx, + ); + update_expr.argument = SimpleAssignmentTarget::from(replacement); + return; + } + let prop_ident = prop_binding.create_read_expression(ctx); let prop_ident2 = prop_binding.create_read_expression(ctx); @@ -1362,17 +1422,29 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { /// Transform tagged template expression where tag is a private field. /// - /// Instance prop: - /// * "object.#prop`xyz`" - /// -> "_classPrivateFieldGet(_prop, object).bind(object)`xyz`" - /// * "object.obj.#prop`xyz`" - /// -> "_classPrivateFieldGet(_prop, _object$obj = object.obj).bind(_object$obj)`xyz`" + /// Not loose: /// - /// Static prop: - /// * "object.#prop`xyz`" - /// -> "_assertClassBrand(Class, object, _prop)._.bind(object)`xyz`" - /// * "object.obj.#prop`xyz`" - /// -> "_assertClassBrand(Class, (_object$obj = object.obj), _prop)._.bind(_object$obj)`xyz`" + /// * Instance prop: + /// * "object.#prop`xyz`" + /// -> "_classPrivateFieldGet(_prop, object).bind(object)`xyz`" + /// * "object.obj.#prop`xyz`" + /// -> "_classPrivateFieldGet(_prop, _object$obj = object.obj).bind(_object$obj)`xyz`" + /// + /// * Static prop: + /// * "object.#prop`xyz`" + /// -> "_assertClassBrand(Class, object, _prop)._.bind(object)`xyz`" + /// * "object.obj.#prop`xyz`" + /// -> "_assertClassBrand(Class, (_object$obj = object.obj), _prop)._.bind(_object$obj)`xyz`" + /// + /// Loose: + /// + /// ```js + /// object.#prop`xyz` + /// ``` + /// -> + /// ```js + /// _classPrivateFieldLooseBase(object, _prop)[_prop]`xyz` + /// ``` // // `#[inline]` so that compiler sees that `expr` is an `Expression::TaggedTemplateExpression` #[inline] @@ -1381,6 +1453,15 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>, ) { + // If `private_fields_as_properties` assumption is enabled, `transform_private_field_expression` + // can handle the transform. + // Babel adds an additional `.bind(object)`: + // "object.#prop`xyz`" -> "_classPrivateFieldLooseBase(object, _prop)[_prop].bind(object)`xyz`" + // But this is not needed, so we omit it. + if self.private_fields_as_properties { + return; + } + let Expression::TaggedTemplateExpression(tagged_temp_expr) = expr else { unreachable!() }; let Expression::PrivateFieldExpression(field_expr) = &mut tagged_temp_expr.tag else { return; @@ -1538,6 +1619,34 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { (object1, references) } + /// `_classPrivateFieldLooseBase(object, _prop)[_prop]`. + /// + /// Takes `&TransformCtx` instead of `&self` to allow passing a `&BoundIdentifier<'a>` returned by + /// `self.private_props_stack.find()`, which takes a partial mut borrow of `self`. + fn create_private_field_member_expr_loose( + object: Expression<'a>, + prop_binding: &BoundIdentifier<'a>, + span: Span, + transform_ctx: &TransformCtx<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> MemberExpression<'a> { + let call_expr = transform_ctx.helper_call_expr( + Helper::ClassPrivateFieldLooseBase, + SPAN, + ctx.ast.vec_from_array([ + Argument::from(object), + Argument::from(prop_binding.create_read_expression(ctx)), + ]), + ctx, + ); + ctx.ast.member_expression_computed( + span, + call_expr, + prop_binding.create_read_expression(ctx), + false, + ) + } + /// `_classPrivateFieldGet2(_prop, object)` fn create_private_field_get( &self, diff --git a/tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/private-loose/derived-multiple-supers/output.js b/tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/private-loose/derived-multiple-supers/output.js new file mode 100644 index 00000000000000..50a15c428037b2 --- /dev/null +++ b/tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/private-loose/derived-multiple-supers/output.js @@ -0,0 +1,14 @@ +var _bar = babelHelpers.classPrivateFieldLooseKey("bar"); +class Foo extends Bar { + constructor() { + var _super = (..._args) => (super(..._args), Object.defineProperty(this, _bar, { + writable: true, + value: "foo" + }), this); + if (condition) { + _super(); + } else { + _super(); + } + } +} diff --git a/tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/private-loose/foobar/options.json b/tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/private-loose/foobar/options.json new file mode 100644 index 00000000000000..af5c06805e8025 --- /dev/null +++ b/tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/private-loose/foobar/options.json @@ -0,0 +1,10 @@ +{ + "plugins": [ + [ + "transform-class-properties", + { + "loose": true + } + ] + ] +} diff --git a/tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/private-loose/foobar/reason.txt b/tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/private-loose/foobar/reason.txt new file mode 100644 index 00000000000000..2637988f084f02 --- /dev/null +++ b/tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/private-loose/foobar/reason.txt @@ -0,0 +1,2 @@ +Disable arrow functions transform in `options.json` because it malfunctions. +But these fixtures aren't to test arrow functions transform. diff --git a/tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/private-loose/nested-class-extends-computed-redeclared/output.js b/tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/private-loose/nested-class-extends-computed-redeclared/output.js new file mode 100644 index 00000000000000..a01bfb9c1b51dc --- /dev/null +++ b/tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/private-loose/nested-class-extends-computed-redeclared/output.js @@ -0,0 +1,31 @@ +var _foo = /*#__PURE__*/babelHelpers.classPrivateFieldLooseKey("foo"); +class Foo { + constructor() { + Object.defineProperty(this, _foo, { + writable: true, + value: 1 + }); + } + test() { + var _foo3; + let _this$foo; + var _foo2 = /*#__PURE__*/babelHelpers.classPrivateFieldLooseKey("foo"); + class Nested extends (_foo3 = /*#__PURE__*/babelHelpers.classPrivateFieldLooseKey("foo"), _this$foo = babelHelpers.classPrivateFieldLooseBase(this, _foo3)[_foo3], class { + constructor() { + Object.defineProperty(this, _foo3, { + writable: true, + value: 2 + }); + this[_this$foo] = 2; + } + }) { + constructor(...args) { + super(...args); + Object.defineProperty(this, _foo2, { + writable: true, + value: 3 + }); + } + } + } +} diff --git a/tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/private-loose/super-expression/output.js b/tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/private-loose/super-expression/output.js new file mode 100644 index 00000000000000..5d4dcbc89da96a --- /dev/null +++ b/tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/private-loose/super-expression/output.js @@ -0,0 +1,14 @@ +var _bar = /*#__PURE__*/babelHelpers.classPrivateFieldLooseKey("bar"); +class Foo extends Bar { + constructor() { + var _super = (..._args) => ( + super(..._args), + Object.defineProperty(this, _bar, { + writable: true, + value: "foo" + }), + this + ); + foo(_super()); + } +} diff --git a/tasks/transform_conformance/snapshots/babel.snap.md b/tasks/transform_conformance/snapshots/babel.snap.md index b62198554f3aec..6e88c96acb0b13 100644 --- a/tasks/transform_conformance/snapshots/babel.snap.md +++ b/tasks/transform_conformance/snapshots/babel.snap.md @@ -1,6 +1,6 @@ commit: 54a8389f -Passed: 441/846 +Passed: 479/846 # All Passed: * babel-plugin-transform-class-static-block @@ -276,7 +276,7 @@ x Output mismatch x Output mismatch -# babel-plugin-transform-class-properties (114/264) +# babel-plugin-transform-class-properties (152/264) * assumption-constantSuper/complex-super-class/input.js x Output mismatch @@ -588,14 +588,16 @@ Scope parent mismatch: after transform: ScopeId(2): Some(ScopeId(1)) rebuilt : ScopeId(2): Some(ScopeId(0)) -* private-loose/assignment/input.js -x Output mismatch - * private-loose/call/input.js -x Output mismatch - -* private-loose/canonical/input.js -x Output mismatch +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-loose/class-shadow-builtins/input.mjs x Output mismatch @@ -603,72 +605,23 @@ 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 +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-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 @@ -676,16 +629,18 @@ x Output mismatch 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 +Bindings mismatch: +after transform: ScopeId(2): ["Nested", "_foo2"] +rebuilt : ScopeId(3): ["Nested", "_foo2", "_foo3", "_this$foo"] +Bindings mismatch: +after transform: ScopeId(3): ["_foo3", "_this$foo"] +rebuilt : ScopeId(4): [] +Symbol scope ID mismatch for "_foo3": +after transform: SymbolId(5): ScopeId(3) +rebuilt : SymbolId(2): ScopeId(3) +Symbol scope ID mismatch for "_this$foo": +after transform: SymbolId(6): ScopeId(3) +rebuilt : SymbolId(3): ScopeId(3) * private-loose/optional-chain-before-member-call/input.js x Output mismatch @@ -702,9 +657,6 @@ 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 @@ -717,9 +669,6 @@ 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 @@ -741,53 +690,50 @@ 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 +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)) * private-loose/static-class-binding/input.js -x Output mismatch - -* private-loose/static-export/input.mjs -x Output mismatch +Scope children mismatch: +after transform: ScopeId(0): [ScopeId(1)] +rebuilt : ScopeId(0): [ScopeId(1), ScopeId(2)] +Scope children mismatch: +after transform: ScopeId(1): [ScopeId(2)] +rebuilt : ScopeId(1): [] +Scope flags mismatch: +after transform: ScopeId(2): ScopeFlags(StrictMode | Function | Arrow) +rebuilt : ScopeId(2): ScopeFlags(Function | Arrow) +Scope parent mismatch: +after transform: ScopeId(2): Some(ScopeId(1)) +rebuilt : ScopeId(2): Some(ScopeId(0)) * 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 +Scope children mismatch: +after transform: ScopeId(0): [ScopeId(1)] +rebuilt : ScopeId(0): [ScopeId(1), ScopeId(2)] +Scope children mismatch: +after transform: ScopeId(1): [ScopeId(2)] +rebuilt : ScopeId(1): [] +Scope flags mismatch: +after transform: ScopeId(2): ScopeFlags(StrictMode | Function | Arrow) +rebuilt : ScopeId(2): ScopeFlags(Function | Arrow) +Scope parent mismatch: +after transform: ScopeId(2): Some(ScopeId(1)) +rebuilt : ScopeId(2): Some(ScopeId(0)) * public/call/input.js Scope children mismatch: diff --git a/tasks/transform_conformance/snapshots/babel_exec.snap.md b/tasks/transform_conformance/snapshots/babel_exec.snap.md index fcbc6425e8893f..87fe948a7cec7e 100644 --- a/tasks/transform_conformance/snapshots/babel_exec.snap.md +++ b/tasks/transform_conformance/snapshots/babel_exec.snap.md @@ -2,7 +2,7 @@ commit: 54a8389f node: v22.12.0 -Passed: 188 of 215 (87.44%) +Passed: 186 of 215 (86.51%) Failures: @@ -31,33 +31,47 @@ AssertionError: expected undefined to be 'hello' // Object.is equality AssertionError: expected undefined to be 'bar' // Object.is equality at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-constructor-collision-exec.test.js:18:19 -./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-access-before-declaration-exec.test.js -AssertionError: expected [Function] to throw error matching /attempted to use private field on non…/ but got 'Cannot read properties of undefined (…' - at Proxy. (./node_modules/.pnpm/@vitest+expect@2.1.2/node_modules/@vitest/expect/dist/index.js:1438:21) - at Proxy. (./node_modules/.pnpm/@vitest+expect@2.1.2/node_modules/@vitest/expect/dist/index.js:923:17) - at Proxy.methodWrapper (./node_modules/.pnpm/chai@5.1.2/node_modules/chai/chai.js:1610:25) - at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-access-before-declaration-exec.test.js:14:5 - ./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-constructor-collision-exec.test.js AssertionError: expected undefined to be 'bar' // Object.is equality - at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-constructor-collision-exec.test.js:18:19 + at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-constructor-collision-exec.test.js:21:19 ./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-nested-class-computed-redeclared-exec.test.js Private field '#foo' must be declared in an enclosing class ./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-nested-class-extends-computed-exec.test.js -AssertionError: expected [Function] to not throw an error but 'TypeError: Private element is not pre…' was thrown +AssertionError: expected [Function] to not throw an error but 'TypeError: attempted to use private f…' was thrown at Proxy. (./node_modules/.pnpm/@vitest+expect@2.1.2/node_modules/@vitest/expect/dist/index.js:1438:21) at Proxy. (./node_modules/.pnpm/@vitest+expect@2.1.2/node_modules/@vitest/expect/dist/index.js:923:17) at Proxy.methodWrapper (./node_modules/.pnpm/chai@5.1.2/node_modules/chai/chai.js:1610:25) - at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-nested-class-extends-computed-exec.test.js:30:9 - -./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-static-shadow-exec.test.js -TypeError: e.has is not a function - at _assertClassBrand (./node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/assertClassBrand.js:2:44) - at func (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-static-shadow-exec.test.js:10:12) - at Function.method (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-static-shadow-exec.test.js:12:11) - at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-static-shadow-exec.test.js:16:14 + at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-nested-class-extends-computed-exec.test.js:36:9 + +./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-with-transform-exec.test.js +TypeError: Cannot convert undefined or null to object + at hasOwnProperty () + at _classPrivateFieldBase (./node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/classPrivateFieldLooseBase.js:2:26) + at value (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-with-transform-exec.test.js:63:11) + at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-with-transform-exec.test.js:44:198 + at j (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-with-transform-exec.test.js:45:6) + at Function.test (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-with-transform-exec.test.js:52:11) + at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-in-function-param-with-transform-exec.test.js:71:6 + +./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-member-optional-call-with-transform-exec.test.js +TypeError: Cannot convert undefined or null to object + at hasOwnProperty () + at _classPrivateFieldBase (./node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/classPrivateFieldLooseBase.js:2:26) + at value (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-member-optional-call-with-transform-exec.test.js:123:11) + at Function.test (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-member-optional-call-with-transform-exec.test.js:24:134) + at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-optional-chain-member-optional-call-with-transform-exec.test.js:131:6 + +./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-parenthesized-optional-member-call-exec.test.js +TypeError: Cannot read properties of undefined (reading 'bind') + at Foo.test (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-parenthesized-optional-member-call-exec.test.js:20:59) + at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-parenthesized-optional-member-call-exec.test.js:78:12 + +./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-parenthesized-optional-member-call-with-transform-exec.test.js +TypeError: Cannot read properties of undefined (reading 'bind') + at Foo.test (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-parenthesized-optional-member-call-with-transform-exec.test.js:20:59) + at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-loose-parenthesized-optional-member-call-with-transform-exec.test.js:78:12 ./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-nested-class-computed-redeclared-exec.test.js Private field '#foo' must be declared in an enclosing class diff --git a/tasks/transform_conformance/snapshots/oxc.snap.md b/tasks/transform_conformance/snapshots/oxc.snap.md index 50fe5267aa7985..705815574a5665 100644 --- a/tasks/transform_conformance/snapshots/oxc.snap.md +++ b/tasks/transform_conformance/snapshots/oxc.snap.md @@ -1,6 +1,6 @@ commit: 54a8389f -Passed: 96/108 +Passed: 97/110 # All Passed: * babel-plugin-transform-class-static-block @@ -15,9 +15,31 @@ Passed: 96/108 * regexp -# babel-plugin-transform-class-properties (3/4) -* private-loose-logical-assignment/input.js -x Output mismatch +# babel-plugin-transform-class-properties (4/6) +* private-loose-tagged-template/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-loose-tagged-template-static/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)) # babel-plugin-transform-async-to-generator (14/15) diff --git a/tasks/transform_conformance/snapshots/oxc_exec.snap.md b/tasks/transform_conformance/snapshots/oxc_exec.snap.md index 5503ec83fc7f7f..23e62427ba1c38 100644 --- a/tasks/transform_conformance/snapshots/oxc_exec.snap.md +++ b/tasks/transform_conformance/snapshots/oxc_exec.snap.md @@ -2,4 +2,4 @@ commit: 54a8389f node: v22.12.0 -Passed: 1 of 1 (100.00%) +Passed: 3 of 3 (100.00%) diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template-static/exec.js b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template-static/exec.js new file mode 100644 index 00000000000000..42e27d5308b2c9 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template-static/exec.js @@ -0,0 +1,9 @@ +class Foo { + static #tag = function() { return this }; + + static getReceiver() { + return this.#tag`tagged template`; + } +} + +expect(Foo.getReceiver()).toBe(Foo); diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template-static/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template-static/input.js new file mode 100644 index 00000000000000..15499cbe2858fc --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template-static/input.js @@ -0,0 +1,7 @@ +class Foo { + static #tag = function() { return this }; + + static getReceiver() { + return this.#tag`tagged template`; + } +} diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template-static/options.json b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template-static/options.json new file mode 100644 index 00000000000000..af5c06805e8025 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template-static/options.json @@ -0,0 +1,10 @@ +{ + "plugins": [ + [ + "transform-class-properties", + { + "loose": true + } + ] + ] +} diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template-static/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template-static/output.js new file mode 100644 index 00000000000000..1bee3b94fa3657 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template-static/output.js @@ -0,0 +1,14 @@ +var _tag = /*#__PURE__*/ babelHelpers.classPrivateFieldLooseKey("tag"); + +class Foo { + static getReceiver() { + return babelHelpers.classPrivateFieldLooseBase(this, _tag)[_tag]`tagged template`; + } +} + +Object.defineProperty(Foo, _tag, { + writable: true, + value: function() { + return this; + } +}); diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template/exec.js b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template/exec.js new file mode 100644 index 00000000000000..2ad4c7769f6e3a --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template/exec.js @@ -0,0 +1,11 @@ +class Foo { + #tag = function() { return this }; + + getReceiver() { + return this.#tag`tagged template`; + } +} + +const foo = new Foo(); +const receiver = foo.getReceiver(); +expect(receiver).toBe(foo); diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template/input.js new file mode 100644 index 00000000000000..7307ea0b694c0a --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template/input.js @@ -0,0 +1,7 @@ +class Foo { + #tag = function() { return this }; + + getReceiver() { + return this.#tag`tagged template`; + } +} diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template/options.json b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template/options.json new file mode 100644 index 00000000000000..af5c06805e8025 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template/options.json @@ -0,0 +1,10 @@ +{ + "plugins": [ + [ + "transform-class-properties", + { + "loose": true + } + ] + ] +} diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template/output.js new file mode 100644 index 00000000000000..c0190638ebdd1f --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/private-loose-tagged-template/output.js @@ -0,0 +1,16 @@ +var _tag = /*#__PURE__*/ babelHelpers.classPrivateFieldLooseKey("tag"); + +class Foo { + constructor() { + Object.defineProperty(this, _tag, { + writable: true, + value: function() { + return this; + } + }); + } + + getReceiver() { + return babelHelpers.classPrivateFieldLooseBase(this, _tag)[_tag]`tagged template`; + } +}