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

Skip class fields transform when not necessary for private methods #14169

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
127 changes: 82 additions & 45 deletions packages/babel-helper-create-class-features-plugin/src/features.ts
@@ -1,4 +1,4 @@
import type { File } from "@babel/core";
import type { File, types as t } from "@babel/core";
import type { NodePath } from "@babel/traverse";
import { hasOwnDecorators } from "./decorators";

Expand Down Expand Up @@ -126,62 +126,99 @@ function canIgnoreLoose(file: File, feature: number) {
return !!(file.get(looseLowPriorityKey) & feature);
}

export function verifyUsedFeatures(path: NodePath, file: File) {
export function shouldTransform(path: NodePath<t.Class>, file: File): boolean {
let decoratorPath: NodePath<t.Decorator> | null = null;
let publicFieldPath: NodePath<t.ClassProperty> | null = null;
let privateFieldPath: NodePath<t.ClassPrivateProperty> | null = null;
let privateMethodPath: NodePath<t.ClassPrivateMethod> | null = null;
let staticBlockPath: NodePath<t.StaticBlock> | null = null;

if (hasOwnDecorators(path.node)) {
if (!hasFeature(file, FEATURES.decorators)) {
throw path.buildCodeFrameError(
"Decorators are not enabled." +
"\nIf you are using " +
'["@babel/plugin-proposal-decorators", { "legacy": true }], ' +
'make sure it comes *before* "@babel/plugin-proposal-class-properties" ' +
"and enable loose mode, like so:\n" +
'\t["@babel/plugin-proposal-decorators", { "legacy": true }]\n' +
'\t["@babel/plugin-proposal-class-properties", { "loose": true }]',
);
decoratorPath = path.get("decorators.0");
}
for (const el of path.get("body.body")) {
if (!decoratorPath && hasOwnDecorators(el.node)) {
decoratorPath = el.get("decorators.0");
}

if (path.isPrivate()) {
throw path.buildCodeFrameError(
`Private ${
path.isClassMethod() ? "methods" : "fields"
} in decorated classes are not supported yet.`,
);
if (!publicFieldPath && el.isClassProperty()) {
publicFieldPath = el;
}
if (!privateFieldPath && el.isClassPrivateProperty()) {
privateFieldPath = el;
}
// NOTE: path.isClassPrivateMethod() it isn't supported in <7.2.0
if (!privateMethodPath && el.isClassPrivateMethod?.()) {
privateMethodPath = el;
}
if (!staticBlockPath && el.isStaticBlock?.()) {
staticBlockPath = el;
}
}

// NOTE: path.isClassPrivateMethod() it isn't supported in <7.2.0
if (path.isClassPrivateMethod?.()) {
if (!hasFeature(file, FEATURES.privateMethods)) {
throw path.buildCodeFrameError("Class private methods are not enabled.");
}
if (decoratorPath && privateFieldPath) {
throw privateFieldPath.buildCodeFrameError(
"Private fields in decorated classes are not supported yet.",
);
}
if (decoratorPath && privateMethodPath) {
throw privateMethodPath.buildCodeFrameError(
"Private methods in decorated classes are not supported yet.",
);
}

if (decoratorPath && !hasFeature(file, FEATURES.decorators)) {
throw path.buildCodeFrameError(
"Decorators are not enabled." +
"\nIf you are using " +
'["@babel/plugin-proposal-decorators", { "legacy": true }], ' +
'make sure it comes *before* "@babel/plugin-proposal-class-properties" ' +
"and enable loose mode, like so:\n" +
'\t["@babel/plugin-proposal-decorators", { "legacy": true }]\n' +
'\t["@babel/plugin-proposal-class-properties", { "loose": true }]',
);
}

if (privateMethodPath && !hasFeature(file, FEATURES.privateMethods)) {
throw privateMethodPath.buildCodeFrameError(
"Class private methods are not enabled. " +
"Please add `@babel/plugin-proposal-private-method` to your configuration.",
);
}

if (
path.isPrivateName() &&
path.parentPath.isBinaryExpression({
operator: "in",
left: path.node,
})
(publicFieldPath || privateFieldPath) &&
!hasFeature(file, FEATURES.fields) &&
// We want to allow enabling the private-methods plugin even without enabling
// the class-properties plugin. Class fields will still be compiled in classes
// that contain private methods.
// This is already allowed with the other various class features plugins, but
// it's because they can fallback to a transform separated from this helper.
!hasFeature(file, FEATURES.privateMethods)
) {
if (!hasFeature(file, FEATURES.privateIn)) {
throw path.buildCodeFrameError(
"Private property in checks are not enabled.",
);
}
throw path.buildCodeFrameError(
"Class fields are not enabled. " +
"Please add `@babel/plugin-proposal-class-properties` to your configuration.",
);
}

if (path.isProperty()) {
if (!hasFeature(file, FEATURES.fields)) {
throw path.buildCodeFrameError("Class fields are not enabled.");
}
if (staticBlockPath && !hasFeature(file, FEATURES.staticBlocks)) {
throw path.buildCodeFrameError(
"Static class blocks are not enabled. " +
"Please add `@babel/plugin-proposal-class-static-block` to your configuration.",
);
}

if (path.isStaticBlock?.()) {
if (!hasFeature(file, FEATURES.staticBlocks)) {
throw path.buildCodeFrameError(
"Static class blocks are not enabled. " +
"Please add `@babel/plugin-proposal-class-static-block` to your configuration.",
);
}
if (decoratorPath || privateMethodPath || staticBlockPath) {
// If one of those feature is used we know that its transform is
// enabled, otherwise the previous checks throw.
return true;
}
if (
(publicFieldPath || privateFieldPath) &&
hasFeature(file, FEATURES.fields)
) {
return true;
}

return false;
}
22 changes: 2 additions & 20 deletions packages/babel-helper-create-class-features-plugin/src/index.ts
Expand Up @@ -12,12 +12,7 @@ import {
import type { PropPath } from "./fields";
import { buildDecoratedClass, hasDecorators } from "./decorators";
import { injectInitialization, extractComputedKeys } from "./misc";
import {
enableFeature,
verifyUsedFeatures,
FEATURES,
isLoose,
} from "./features";
import { enableFeature, FEATURES, isLoose, shouldTransform } from "./features";
import { assertFieldTransformed } from "./typescript";
import type { ParserOptions } from "@babel/parser";

Expand Down Expand Up @@ -98,7 +93,7 @@ export function createClassFeaturePlugin({
Class(path: NodePath<t.Class>, state: File) {
if (this.file.get(versionKey) !== version) return;

verifyUsedFeatures(path, this.file);
if (!shouldTransform(path, this.file)) return;

if (path.isClassDeclaration()) assertFieldTransformed(path);

Expand All @@ -113,8 +108,6 @@ export function createClassFeaturePlugin({
const body = path.get("body");

for (const path of body.get("body")) {
verifyUsedFeatures(path, this.file);

if (
// check path.node.computed is enough, but ts will complain
(path.isClassProperty() || path.isClassMethod()) &&
Expand Down Expand Up @@ -266,17 +259,6 @@ export function createClassFeaturePlugin({
}
},

PrivateName(path: NodePath<t.PrivateName>) {
if (
this.file.get(versionKey) !== version ||
path.parentPath.isPrivate({ key: path.node })
) {
return;
}

throw path.buildCodeFrameError(`Unknown PrivateName "${path}"`);
Copy link
Member Author

Choose a reason for hiding this comment

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

This check was causing problems because if we only enable the private methods plugin, some private names might not be transformed (i.e. if they refer to a private field).
We now also validate private names usage in the parser, so it's safe to remove it from here.

},

ExportDefaultDeclaration(path: NodePath<t.ExportDefaultDeclaration>) {
if (this.file.get(versionKey) !== version) return;

Expand Down
@@ -1,6 +1,6 @@
{
"presets": [["env", { "shippedProposals": true }]],
"targets": {
"chrome": "75"
"chrome": "70"
}
}
3 changes: 0 additions & 3 deletions packages/babel-preset-env/src/plugins-compat-data.ts
Expand Up @@ -17,7 +17,4 @@ for (const plugin of Object.keys(bugfixPlugins)) {
}
}

pluginsFiltered["proposal-class-properties"] =
pluginsFiltered["proposal-private-methods"];

export { pluginsFiltered as plugins, bugfixPluginsFiltered as pluginsBugfixes };
Expand Up @@ -18,7 +18,7 @@ Using modules transform: auto
Using plugins:
proposal-class-static-block { android, chrome < 94, edge < 94, firefox < 93, ios, node < 16.11, opera < 80, safari, samsung }
proposal-private-property-in-object { android, chrome < 91, edge < 91, firefox < 90, ios < 15, node < 16.9, opera < 77, safari < 15, samsung }
proposal-class-properties { android, chrome < 84, edge < 84, firefox < 90, ios < 15, node < 14.6, opera < 70, safari < 15, samsung < 14 }
proposal-class-properties { android, chrome < 74, edge < 79, firefox < 90, ios < 15, opera < 62, safari < 14.1, samsung < 11 }
proposal-private-methods { android, chrome < 84, edge < 84, firefox < 90, ios < 15, node < 14.6, opera < 70, safari < 15, samsung < 14 }
proposal-numeric-separator { android, chrome < 75, edge < 79, firefox < 70, ios < 13, opera < 62, safari < 13, samsung < 11 }
proposal-logical-assignment-operators { android, chrome < 85, edge < 85, firefox < 79, ios < 14, node < 15, opera < 71, safari < 14, samsung < 14 }
Expand Down
Expand Up @@ -18,7 +18,7 @@ Using modules transform: auto
Using plugins:
proposal-class-static-block { android, chrome < 94, edge < 94, firefox < 93, ios, node < 16.11, opera < 80, safari, samsung }
proposal-private-property-in-object { android, chrome < 91, edge < 91, firefox < 90, ios < 15, node < 16.9, opera < 77, safari < 15, samsung }
proposal-class-properties { android, chrome < 84, edge < 84, firefox < 90, ios < 15, node < 14.6, opera < 70, safari < 15, samsung < 14 }
proposal-class-properties { android, chrome < 74, edge < 79, firefox < 90, ios < 15, opera < 62, safari < 14.1, samsung < 11 }
proposal-private-methods { android, chrome < 84, edge < 84, firefox < 90, ios < 15, node < 14.6, opera < 70, safari < 15, samsung < 14 }
proposal-numeric-separator { android, chrome < 75, edge < 79, firefox < 70, ios < 13, opera < 62, safari < 13, samsung < 11 }
proposal-logical-assignment-operators { android, chrome < 85, edge < 85, firefox < 79, ios < 14, node < 15, opera < 71, safari < 14, samsung < 14 }
Expand Down
Expand Up @@ -10,7 +10,7 @@ Using modules transform: auto
Using plugins:
proposal-class-static-block { chrome < 94 }
proposal-private-property-in-object { chrome < 91 }
proposal-class-properties { chrome < 84 }
proposal-class-properties { chrome < 74 }
proposal-private-methods { chrome < 84 }
proposal-numeric-separator { chrome < 75 }
proposal-logical-assignment-operators { chrome < 85 }
Expand Down
Expand Up @@ -10,7 +10,7 @@ Using modules transform: auto
Using plugins:
proposal-class-static-block { chrome < 94 }
proposal-private-property-in-object { chrome < 91 }
proposal-class-properties { chrome < 84 }
proposal-class-properties { chrome < 74 }
proposal-private-methods { chrome < 84 }
proposal-numeric-separator { chrome < 75 }
proposal-logical-assignment-operators { chrome < 85 }
Expand Down
Expand Up @@ -10,7 +10,7 @@ Using modules transform: auto
Using plugins:
proposal-class-static-block { edge < 94 }
proposal-private-property-in-object { edge < 91 }
proposal-class-properties { edge < 84 }
proposal-class-properties { edge < 79 }
proposal-private-methods { edge < 84 }
proposal-numeric-separator { edge < 79 }
proposal-logical-assignment-operators { edge < 85 }
Expand Down
Expand Up @@ -10,7 +10,7 @@ Using modules transform: auto
Using plugins:
proposal-class-static-block { edge < 94 }
proposal-private-property-in-object { edge < 91 }
proposal-class-properties { edge < 84 }
proposal-class-properties { edge < 79 }
proposal-private-methods { edge < 84 }
proposal-numeric-separator { edge < 79 }
proposal-logical-assignment-operators { edge < 85 }
Expand Down
Expand Up @@ -10,7 +10,7 @@ Using modules transform: auto
Using plugins:
proposal-class-static-block { edge < 94 }
proposal-private-property-in-object { edge < 91 }
proposal-class-properties { edge < 84 }
proposal-class-properties { edge < 79 }
proposal-private-methods { edge < 84 }
proposal-numeric-separator { edge < 79 }
proposal-logical-assignment-operators { edge < 85 }
Expand Down
Expand Up @@ -10,7 +10,7 @@ Using modules transform: auto
Using plugins:
proposal-class-static-block { edge < 94 }
proposal-private-property-in-object { edge < 91 }
proposal-class-properties { edge < 84 }
proposal-class-properties { edge < 79 }
proposal-private-methods { edge < 84 }
proposal-numeric-separator { edge < 79 }
proposal-logical-assignment-operators { edge < 85 }
Expand Down
Expand Up @@ -10,7 +10,7 @@ Using modules transform: auto
Using plugins:
proposal-class-static-block { edge < 94 }
proposal-private-property-in-object { edge < 91 }
proposal-class-properties { edge < 84 }
proposal-class-properties { edge < 79 }
proposal-private-methods { edge < 84 }
proposal-numeric-separator { edge < 79 }
proposal-logical-assignment-operators { edge < 85 }
Expand Down
Expand Up @@ -10,7 +10,7 @@ Using modules transform: auto
Using plugins:
proposal-class-static-block { edge < 94 }
proposal-private-property-in-object { edge < 91 }
proposal-class-properties { edge < 84 }
proposal-class-properties { edge < 79 }
proposal-private-methods { edge < 84 }
proposal-numeric-separator { edge < 79 }
proposal-logical-assignment-operators { edge < 85 }
Expand Down
Expand Up @@ -10,7 +10,7 @@ Using modules transform: auto
Using plugins:
proposal-class-static-block { edge < 94 }
proposal-private-property-in-object { edge < 91 }
proposal-class-properties { edge < 84 }
proposal-class-properties { edge < 79 }
proposal-private-methods { edge < 84 }
proposal-numeric-separator { edge < 79 }
proposal-logical-assignment-operators { edge < 85 }
Expand Down
Expand Up @@ -10,7 +10,7 @@ Using modules transform: auto
Using plugins:
proposal-class-static-block { edge < 94 }
proposal-private-property-in-object { edge < 91 }
proposal-class-properties { edge < 84 }
proposal-class-properties { edge < 79 }
proposal-private-methods { edge < 84 }
proposal-numeric-separator { edge < 79 }
proposal-logical-assignment-operators { edge < 85 }
Expand Down
Expand Up @@ -10,7 +10,7 @@ Using modules transform: auto
Using plugins:
proposal-class-static-block { safari }
proposal-private-property-in-object { safari < 15 }
proposal-class-properties { safari < 15 }
proposal-class-properties { safari < 14.1 }
proposal-private-methods { safari < 15 }
proposal-numeric-separator { safari < 13 }
proposal-logical-assignment-operators { safari < 14 }
Expand Down
Expand Up @@ -10,7 +10,7 @@ Using modules transform: auto
Using plugins:
proposal-class-static-block { safari }
proposal-private-property-in-object { safari < 15 }
proposal-class-properties { safari < 15 }
proposal-class-properties { safari < 14.1 }
proposal-private-methods { safari < 15 }
proposal-numeric-separator { safari < 13 }
proposal-logical-assignment-operators { safari < 14 }
Expand Down
Expand Up @@ -10,7 +10,7 @@ Using modules transform: auto
Using plugins:
proposal-class-static-block { safari }
proposal-private-property-in-object { safari < 15 }
proposal-class-properties { safari < 15 }
proposal-class-properties { safari < 14.1 }
proposal-private-methods { safari < 15 }
proposal-numeric-separator { safari < 13 }
proposal-logical-assignment-operators { safari < 14 }
Expand Down
Expand Up @@ -10,7 +10,7 @@ Using modules transform: auto
Using plugins:
proposal-class-static-block { safari }
proposal-private-property-in-object { safari < 15 }
proposal-class-properties { safari < 15 }
proposal-class-properties { safari < 14.1 }
proposal-private-methods { safari < 15 }
proposal-numeric-separator { safari < 13 }
proposal-logical-assignment-operators { safari < 14 }
Expand Down
Expand Up @@ -18,7 +18,7 @@ Using modules transform: false
Using plugins:
proposal-class-static-block { android, chrome < 94, edge < 94, firefox < 93, ios, node < 16.11, opera < 80, safari, samsung }
proposal-private-property-in-object { android, chrome < 91, edge < 91, firefox < 90, ios < 15, node < 16.9, opera < 77, safari < 15, samsung }
proposal-class-properties { android, chrome < 84, edge < 84, firefox < 90, ios < 15, node < 14.6, opera < 70, safari < 15, samsung < 14 }
proposal-class-properties { android, chrome < 74, edge < 79, firefox < 90, ios < 15, opera < 62, safari < 14.1, samsung < 11 }
proposal-private-methods { android, chrome < 84, edge < 84, firefox < 90, ios < 15, node < 14.6, opera < 70, safari < 15, samsung < 14 }
proposal-numeric-separator { android, chrome < 75, edge < 79, firefox < 70, ios < 13, opera < 62, safari < 13, samsung < 11 }
proposal-logical-assignment-operators { android, chrome < 85, edge < 85, firefox < 79, ios < 14, node < 15, opera < 71, safari < 14, samsung < 14 }
Expand Down
Expand Up @@ -18,7 +18,7 @@ Using modules transform: false
Using plugins:
proposal-class-static-block { android, chrome < 94, edge < 94, firefox < 93, ios, node < 16.11, opera < 80, safari, samsung }
proposal-private-property-in-object { android, chrome < 91, edge < 91, firefox < 90, ios < 15, node < 16.9, opera < 77, safari < 15, samsung }
proposal-class-properties { android, chrome < 84, edge < 84, firefox < 90, ios < 15, node < 14.6, opera < 70, safari < 15, samsung < 14 }
proposal-class-properties { android, chrome < 74, edge < 79, firefox < 90, ios < 15, opera < 62, safari < 14.1, samsung < 11 }
proposal-private-methods { android, chrome < 84, edge < 84, firefox < 90, ios < 15, node < 14.6, opera < 70, safari < 15, samsung < 14 }
proposal-numeric-separator { android, chrome < 75, edge < 79, firefox < 70, ios < 13, opera < 62, safari < 13, samsung < 11 }
proposal-logical-assignment-operators { android, chrome < 85, edge < 85, firefox < 79, ios < 14, node < 15, opera < 71, safari < 14, samsung < 14 }
Expand Down
Expand Up @@ -17,7 +17,7 @@ Using modules transform: auto
Using plugins:
proposal-class-static-block { chrome < 94, edge < 94, firefox < 93, ios, opera < 80, safari, samsung }
proposal-private-property-in-object { firefox < 90, ios < 15, safari < 15, samsung }
proposal-class-properties { firefox < 90, ios < 15, safari < 15 }
proposal-class-properties { firefox < 90, ios < 15, safari < 14.1 }
proposal-private-methods { firefox < 90, ios < 15, safari < 15 }
proposal-numeric-separator { ios < 13 }
proposal-logical-assignment-operators { firefox < 79, ios < 14, safari < 14 }
Expand Down
Expand Up @@ -18,7 +18,7 @@ Using modules transform: auto
Using plugins:
proposal-class-static-block { chrome < 94, edge < 94, firefox < 93, ie, ios, opera < 80, safari, samsung }
proposal-private-property-in-object { firefox < 90, ie, ios < 15, safari < 15, samsung }
proposal-class-properties { firefox < 90, ie, ios < 15, safari < 15 }
proposal-class-properties { firefox < 90, ie, ios < 15, safari < 14.1 }
proposal-private-methods { firefox < 90, ie, ios < 15, safari < 15 }
proposal-numeric-separator { ie, ios < 13 }
proposal-logical-assignment-operators { firefox < 79, ie, ios < 14, safari < 14 }
Expand Down