babel: [Bug]: Code generated by Babel for decorated static accessors throws SyntaxError in Firefox

💻

  • Would you like to work on a fix?

How are you using Babel?

@rollup/plugin-babel

Input code

https://babeljs.io/repl#?browsers=last 3 versions%2C not dead&build=&builtIns=false&corejs=3.21&spec=false&loose=false&code_lz=GYVwdgxgLglg9mABAEwKYTgJwIZVQYQBtsBnEgCim0wHNUoAaRDMPADygEpEBvAKAC-fPqEiwEKdFlyoAghAioyWStTqNmCdl16DhAATQYceIqRJ8IxMogAqSqLsSJDUk3IVKSWRCSqwIRGxPZUxEYDg4RABeRAAWACYAbj0-AHo0xAA-RABlAE9WbDYAUUxMLAAuRExUYFRayFREKCjwI2Ja5EQAB0wYADcZcJhUQm6fAFt6AAs4boBiWSA&debug=false&forceAllTransforms=false&modules=false&shippedProposals=false&circleciRepo=&evaluate=true&fileSize=false&timeTravel=false&sourceType=module&lineWrap=true&presets=env&prettier=false&targets=&version=7.24.3&externalPlugins=%40babel%2Fplugin-proposal-decorators%407.24.0&assumptions={}

function decorateClass(target, context) {
}

function decorateAccessor(target, context) {
}

@decorateClass
class Test {
  @decorateAccessor static accessor foo = 42;
}

// > SyntaxError: reference to undeclared private field or method #A

Configuration file name

babel.config.mjs

Configuration

/* eslint-env node */

const config = {
  presets: [
    ["@babel/preset-env", {}],
    ["@babel/preset-typescript", { allowDeclareFields: true }],
    // See https://github.com/babel/babel/issues/16373#issuecomment-2013163733
  ],
};

if (process.env.NODE_ENV === "test") {
  config.plugins = [
    [
      "@babel/plugin-proposal-decorators",
      {
        version: "2023-11",
      },
    ],
  ];
}

export default config;

Current and expected behavior

Current behavior: with targets set to last 3 versions, not dead, the generated code throws a syntax error in Firefox. This may be a Firefox problem rather than a Babel problem. Tested with Firefox 124.0.1 (64-bit) and Nightly 126.0a1 (2024-03-24) (64-bit).

Expected behavior: no syntax error in any browser

Environment

Possible solution

No response

Additional context

No response

About this issue

  • Original URL
  • State: closed
  • Created 3 months ago
  • Comments: 16 (15 by maintainers)

Most upvoted comments

I think that is the same bug. Chrome doesn’t throw because the function is never called.

An even minimal example is

let capturedPrivateAccess;
class A {
  static #x = 42;
  static [(class {}, capturedPrivateAccess = () => A.#x)];
}
console.log(capturedPrivateAccess())

the class in the computed key is needed, but the private access doesn’t need to be inside the class

SpiderMonkey just merged a fix for this, but let’s still implement a workaround plugin for all the existing Firefox version. @JLHwung do you want to work on it since you are fixing other decorator bugs, or should I do it?

Thank you. The bugfix is your idea, go ahead, meanwhile I will be kept busy by a few other decorators bugs.

This code on the other hand works:

class A {
  static [class A {}];
  static #x = 42;
}

Maybe this hints at a better fix than wrapping in an IIFE 🤔

Well the decorator transform already output the class in the first static property.

This code on the other hand works:

class A {
  static [class A {}];
  static #x = 42;
}

Maybe this hints at a better fix than wrapping in an IIFE 🤔

Even smaller reproduction, from Firefox folks 😃

class A {
  static #x = 42;
  static [class {}];
} 

This throws in Firefox.

It seems like wrapping the computed key in an IIFE works:

let capturedPrivateAccess;
class A {
  static #x = 42;
  static [(() => (class {}, capturedPrivateAccess = () => A.#x))()];
}
console.log(capturedPrivateAccess())