Previously, #8634 visualized each spec result, but this was not enough.
If the implementation has been updated but the spec still fails, there
is no way to know if there is an improvement or not.
This time, finally, all progress is visualized! 🤩

e.g. here `arrow_function_expression` is mostly passing, except for
comment handling.
The new implementation port from [esbuild](df815ac27b/internal/js_parser/js_parser_lower.go (L355-L467)), before from `Babel`.
Babel's transform implementation for the async method is incorrect because the async method should return a rejecting promise when it throws an error. Everything is good if the errors are thrown in the async method body, but the following case will throw an error in the parameters which causes the whole program crushed not a rejecting promise. So we should move the parameters to the inner generator function when the parameters could throw an error.
Input:
```js
class Cls {
// ReferenceError: Cannot access 'b' before initialization
async method(a = b, b = 0) {}
}
```
Before output
```js
class Cls {
method(a = b, b = 0) {
return babelHelpers.asyncToGenerator(function* () {})();
}
}
```
After output:
```js
class Cls {
method() {
// ReferenceError: Cannot access 'b' before initialization
return babelHelpers.asyncToGenerator(function* (a = b, b = 0) {}).apply(this, arguments);
}
}
```
No override tests because Babel doesn't cover this case.
Update babel submodule for conformance tests to latest HEAD.
The test fixtures include a new one for a bug fix which @branchseer
intends to also apply to Oxc - https://github.com/babel/babel/pull/17050
(see #8342).
Follow-on after #8482.
Fix an edge case in arrow functions transform when inserting `_this = this` after `super()`. It is possible (though bizarre) for `super()` to contain another `super()` call. This will throw an error when evaluating the outer `super()`, but it can still be observable in some cases.
```js
let f;
class S {}
class C extends S {
constructor(x) {
super(super(), this.x = x, f = async () => this);
}
}
try { new C(123) } catch {}
const c = await f();
assert(c.x === 123);
```
So, before bailing out from searching for more `super()`s in class constructor, visit the `super()` call's arguments.
Some of Babel's exec tests contain a return statement at top level, in order to return a Promise.
e.g. ad572fd1a1/packages/babel-plugin-transform-private-methods/test/fixtures/private-method-loose/async/exec.js
These test files could not be parsed due to illegal position of `return`, and the tests were silently excluded.
This PR:
1. Includes those tests by passing `allow_return_outside_function: true` option to parser for exec tests.
2. Includes a "transform error" message in snapshot for any exec tests which produce errors in parser/transformer.
I did a few things in this PR,
1. Remove `names` which store the declarations name used for checking re-declaration. (We can use SymbolTable to do it now.)
2. Correct semantic data for namespace transform
3. Simplify code
close: #8385
This PR is to solve the missing `super` transform in the async arrow function, and `_this = this` inserts to an incorrect place. These problems are all about the async arrow function, which is a part of the init of class property. Learn more at #8387.
The output matches Babel's output except for static prop as Babel transforms incorrectly.
This `super.value` belongs to the nested class, we shouldn't transform it.
```js
class Outer {
async method() {
class Inner extends Outer {
normal() {
// `super.value` should not be transformed, because it is not in an async method
super.value
}
}
}
}
```
No temp var is required for class if it contains static private method which is not referenced within class. e.g.:
```js
let C = class {
static #method() {}
};
```
->
```js
let C = class {};
function _method() {}
```
In the following case, async methods can be nested in another async method. The implementation is changing to store `super_methods` on a stack, and then we can store super method information in the correct `super_methods` map.
```js
const outer = {
value: 0,
async method() {
() => super.value;
const inner = {
value: 0,
async method() {
() => super.value;
}
};
() => super.value;
}
};
```
The `class-properties` plugin supports all class-related plugins, so we need to ensure that once any of them are enabled, we also enable all class-related plugins.
This PR supports transforming private `getter` or `setter` methods, and the output is a little different from Babel's output, For example:
Input:
```js
class Cls {
#prop = 0;
get #accessor() {
return this.#prop;
}
set #accessor(v) {
console.log(arguments);
this.#prop = v;
}
constructor() {
console.log(this.#accessor)
[this.#accessor] = [1];
}
}
```
[Babel's output](https://babeljs.io/repl#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&build=&builtIns=false&corejs=3.21&spec=false&loose=false&code_lz=MYGwhgzhAEDCIwN4ChrQMQAcBOB7T0AvNAAwDcyq0A5gKYAuGYwwtUu2AFAJTQpppsDAK7YAdtHoALAJYQAdFjyYKaAL5UIDJizYQOnAG69-A4LjH6QteSFzVOYbNWEBbWmPoRuqgdLmKOPhE0Ia-GlTmlvTYwsD0BiZUaFFWNnYO_grozKzs2NzJ0ADaWYq5ehwAuiHFAIxV4cgaQA&debug=false&forceAllTransforms=false&modules=false&shippedProposals=false&evaluate=false&fileSize=false&timeTravel=false&sourceType=script&lineWrap=true&presets=&prettier=false&targets=&version=7.26.4&externalPlugins=%40babel%2Fplugin-transform-class-properties%407.25.9%2C%40babel%2Fplugin-transform-private-methods%407.25.9%2C%40babel%2Fplugin-external-helpers%407.25.9&assumptions=%7B%7D):
```js
var _prop = new WeakMap();
var _Cls_brand = new WeakSet();
class Cls {
constructor() {
babelHelpers.classPrivateMethodInitSpec(this, _Cls_brand);
babelHelpers.classPrivateFieldInitSpec(this, _prop, 0);
console.log(babelHelpers.classPrivateGetter(_Cls_brand, this, _get_accessor));
[babelHelpers.classPrivateGetter(_Cls_brand, this, _get_accessor)] = [1];
}
}
function _get_accessor(_this) {
return babelHelpers.classPrivateFieldGet(_prop, _this);
}
function _set_accessor(_this2, v) {
var _arguments = [].slice.call(arguments, 1);
console.log(_arguments);
babelHelpers.classPrivateFieldSet(_prop, _this2, v);
}
```
Oxc's output:
```js
var _prop = new WeakMap();
var _Cls_brand = new WeakSet();
class Cls {
constructor() {
babelHelpers.classPrivateMethodInitSpec(this, _Cls_brand);
babelHelpers.classPrivateFieldInitSpec(this, _prop, 0);
console.log(_get_accessor.call(babelHelpers.assertClassBrand(_Cls_brand, this)));
[babelHelpers.toSetter(_set_accessor.bind(babelHelpers.assertClassBrand(_Cls_brand, this)), [])._] = [1];
}
}
function _get_accessor() {
return babelHelpers.classPrivateFieldGet2(_prop, this);
}
function _set_accessor(v) {
console.log(arguments);
babelHelpers.classPrivateFieldSet2(_prop, this, v);
}
```
### Main difference
```diff
// Babel
+ console.log(babelHelpers.classPrivateGetter(_Cls_brand, this, _get_accessor));
+ [babelHelpers.classPrivateGetter(_Cls_brand, this, _get_accessor)] = [1];
+ function _get_accessor(_this) {
+ return babelHelpers.classPrivateFieldGet(_prop, _this);
+ }
+ function _set_accessor(_this2, v) {
+ var _arguments = [].slice.call(arguments, 1);
+ console.log(_arguments);
+ babelHelpers.classPrivateFieldSet(_prop, _this2, v);
+ }
// Oxc
- console.log(_get_accessor.call(babelHelpers.assertClassBrand(_Cls_brand, this)));- -
- [babelHelpers.toSetter(_set_accessor.bind(babelHelpers.assertClassBrand(_Cls_brand, this)), [])._] = [1];
- function _get_accessor() {
- return babelHelpers.classPrivateFieldGet2(_prop, this);
- }
- function _set_accessor(v) {
- console.log(arguments);
- babelHelpers.classPrivateFieldSet2(_prop, this, v);
- }
```
From the main differences, we can see that Babel handles `getter` and `setter` methods using `classPrivateGetter` and `classPrivateSetter` helper functions, which causes all use `this` and `arguments` needs to rewrite to use a temp var instead in `getter` and `setter` methods. This is unnecessary and is not an efficient transformation for us.
Instead, I adapt binding a `this` instead of passing in `this`, this way we don't need to rewrite anything. We can't control the `helper` library for now, so I just transformed the AST to bind `this`, in the future, we can create a helper function to do the same thing.
All private methods will be moved out of class, so we need to transform all `super` expressions in private methods and transform identifiers that refer to class binding.
We don't transform private methods yet, but in class properties transform, still need to record private methods that classes have, so can correctly resolve private fields.
```js
class Outer {
#foo = 123;
method() {
class Inner {
#foo() {}
// Refers to `Inner`'s `#foo` method, not `Outer`'s `#foo` property
prop = this.#foo;
}
}
}
```
Add failing test for nullish coalescing operator. The output is correct, but temp var is created in wrong scope.
My guess is that it needs to use `current_hoist_scope_id`, not `current_scope_id`.
This test from class properties also shows the same problem: ac097e9160/tasks/transform_conformance/snapshots/oxc.snap.md (L20-L35)
Large re-architecting of class properties transform. Split transform into 3 phases:
1. Transform instance properties when entering class.
2. Transform private fields during traversal of class body.
3. Transform static properties and static blocks when exiting class.
This ensures that code which has to be moved outside of the class (static property initializers, static blocks, computed keys) can get transformed by other transforms before they're moved out.
Also fixes a problem where we previously registered private properties too early - on entering the class, rather than the class *body*, so private fields in `extends` clause of a nested class were misinterpretted.
When inserting instance property initializers into class constructor, need to search for and transform `super()`. Exit that visit earlier in some cases, for better performance and smaller output.
Follow-on after #7997.
Generate "base name" for temp var using `temp_var_name_base` and then create the 2 temp bindings from it.
This is a bit more efficient than creating 2nd temp binding from name of the first temp binding, because the first binding's name has `_` added to start, and may have digits on the end, which have to be trimmed off again. Whereas the "base name" is ready to go.
Incidentally, changing the timing of when temp bindings are created also aligns output with Babel.