Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce bugfix-safari-class-field-initializer-scope #16569

Merged

Conversation

davidtaylorhq
Copy link
Contributor

@davidtaylorhq davidtaylorhq commented Jun 10, 2024

Q                       A
Fixed Issues? Fixes #14289
Patch: Bug Fix?
Major: Breaking Change?
Minor: New Feature? Yes
Tests Added + Pass? Yes
Documentation PR Link babel/website#2921
Any Dependency Changes?
License MIT

This is a workaround for https://bugs.webkit.org/show_bug.cgi?id=236843, and aims to resolve #14289.

It adds the new compat-table check to the requirements for transform-class-properties, which means that transform will now apply for Safari < 16 (previously Safari < 14.5).

It also introduces a bugfix transformation which can be used as an alternative to transform-class-properties on Safari 15. This bugfix transformations wraps potentially-affected initializers in an IIFE. A number of shortcuts are added to avoid transforming initializers which are guaranteed to be unaffected by the bug.

Todo:

@babel-bot
Copy link
Collaborator

babel-bot commented Jun 10, 2024

Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/57460

@nicolo-ribaudo nicolo-ribaudo added this to the v7.25.0 milestone Jun 20, 2024
@nicolo-ribaudo
Copy link
Member

@davidtaylorhq I'm starting to prepare the 7.25.0 release -- do you have any idea for when you'll have time to finish this PR? :)

@davidtaylorhq
Copy link
Contributor Author

Thanks for the note @nicolo-ribaudo - I'll see if I can find some time this week. What's the timeline on 7.25.0?

@nicolo-ribaudo
Copy link
Member

I'm targeting mid/end of next week. If needed, I can also help with this PR, but it seems like it's already almost complete.

@nicolo-ribaudo nicolo-ribaudo added PR: New Feature 🚀 A type of pull request used for our changelog categories PR: Needs Docs i: browser bug labels Jul 10, 2024
) {
const { value } = path.node;

if (value && !(t.isLiteral(value) && !t.isTemplateLiteral(value))) {
Copy link
Member

@nicolo-ribaudo nicolo-ribaudo Jul 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition for when wrapping is necessary is if there is anything wrapped in parentheses that is outside of a function, right?

To avoid too many IIFEs, I would expand this logic to, recursively:

  • if NODE is a non-template literal, or a Function, return false
  • if NODE is a CallExpression or a NewExpression, return true if recursing on the callee or on any of the arguments returns true
  • if NODE is a TemplateLiteral, return true if recursing on the callee or on any of the inner expressions returns true
  • if NODE is a TaggedTemplateExpression, return true if recursing on the tag or on the template returns true
  • if NODE is an ArrayExpression, return true if recursing on any of the elements returns true
  • if NODE is an ObjectExpression, return true if recursing on any of the property values or computed keys returns true
  • if NODE is a MemberExpression, return true if the object or the computed property returns true
  • if NODE is an Identifier, return false

This should avoid parens in most cases, except for when they are strictly necessary

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition for when wrapping is necessary is if there is anything wrapped in parentheses that is outside of a function, right?

Not quite. The presence of parenthesis anywhere in the expression is enough to trigger the bug. The check added in compat-table demonstrates this. c = a[0] is fine. c = a[(0)] is not 😬

In this comment of #14289, concern was raised about doing any transformation based on the presence (or not) of parenthesis. It's quite hard to do reliably.

That said, we can certainly cut down on the number of initializers which need to be transformed. Instead of gating it on parenthesis, we can look for any Identifiers which may refer to the parent scope. In fact, the logic we need is almost identical to what you wrote out, except that the last line should be:

  • If NODE is an identifier, return falsetrue

I've also added handling for a few other 'safe' nodes which can be skipped:

  • added: "if NODE is a FunctionExpression or ArrowFunctionExpression, return false"
  • added: "if NODE is a ThisExpression, return false"
  • added: "if NODE is a SequenceExpression, return true if any of the elements returns true"

@davidtaylorhq davidtaylorhq force-pushed the safari-class-field-initializer-scope branch 2 times, most recently from 90e9011 to eef8c82 Compare July 11, 2024 17:22
davidtaylorhq added a commit to davidtaylorhq/website that referenced this pull request Jul 11, 2024
See babel/babel#16569

Doc structure adapted from `plugin-bugfix-firefox-class-in-computed-class-key`
@davidtaylorhq
Copy link
Contributor Author

@nicolo-ribaudo this is now in a much better state - thanks for the help. Please let me know if you have any comments/suggestions.

Is there anything we need to do to prepare for the new @babel/babel-plugin-bugfix-safari-class-field-initializer-scope package which this PR introduces?

Also, is there any good way to test a development version of @babel/preset-env (and its dependencies) against a real app? In my case, I'd like to try it against our https://github.com/discourse/discourse build. I've tested the new transform in isolation, but haven't been able to work out how to link up the development version of preset-env 🤔

@davidtaylorhq davidtaylorhq force-pushed the safari-class-field-initializer-scope branch from eef8c82 to 70abe65 Compare July 11, 2024 17:35
davidtaylorhq added a commit to davidtaylorhq/website that referenced this pull request Jul 11, 2024
See babel/babel#16569

Doc structure adapted from `plugin-bugfix-firefox-class-in-computed-class-key`
@davidtaylorhq davidtaylorhq marked this pull request as ready for review July 11, 2024 17:40
@davidtaylorhq davidtaylorhq force-pushed the safari-class-field-initializer-scope branch from 70abe65 to 5827ad4 Compare July 11, 2024 17:40
return false;
}

if (t.isCallExpression(node) || t.isNewExpression(node)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also cover the OptionalCallExpression.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added 👍

});
}

if (t.isMemberExpression(node)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the OptionalMemberExpression.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added 👍

);
}

if (t.isFunctionExpression(node) || t.isArrowFunctionExpression(node)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also cover ClassExpression here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, added 👍

I have confirmed that the superClass expression does not seem to be affected by the Safari bug, so we don't need to recurse into that.

Copy link
Contributor

@JLHwung JLHwung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. Added some comments on uncovered AST types.

@davidtaylorhq davidtaylorhq force-pushed the safari-class-field-initializer-scope branch from 5827ad4 to 0072adf Compare July 12, 2024 09:58
davidtaylorhq added a commit to discourse/discourse that referenced this pull request Jul 12, 2024
This tightens things up to reduce the number of initializers which need to be wrapped in an IIFE.

Mirrors the changes made in babel/babel#16569
davidtaylorhq added a commit to discourse/discourse that referenced this pull request Jul 12, 2024
This tightens things up to reduce the number of initializers which need to be wrapped in an IIFE.

Mirrors the changes made in babel/babel#16569
@nicolo-ribaudo nicolo-ribaudo added the PR: Ready to be Merged A pull request with already two approvals, but waiting for the next minor release label Jul 12, 2024
Copy link
Member

@nicolo-ribaudo nicolo-ribaudo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@nicolo-ribaudo
Copy link
Member

Is there anything we need to do to prepare for the new @babel/babel-plugin-bugfix-safari-class-field-initializer-scope package which this PR introduces?

Ideally we'd need a basic docs PR, to add a file like https://github.com/babel/website/blob/main/docs/plugin-bugfix-firefox-class-in-computed-class-key.md

@davidtaylorhq
Copy link
Contributor Author

Here's the docs PR: babel/website#2921

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
i: browser bug PR: New Feature 🚀 A type of pull request used for our changelog categories PR: Ready to be Merged A pull request with already two approvals, but waiting for the next minor release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Bug]: Safari class fields implementation is buggy - ReferenceError: Can't find variable
6 participants