Skip to content

Commit b2cfc5b

Browse files
Skip class fields transform when not necessary for priv methods (#14169)
Co-authored-by: Huáng Jùnliàng <[email protected]>
1 parent 478a970 commit b2cfc5b

180 files changed

Lines changed: 387 additions & 237 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/babel-helper-create-class-features-plugin/src/features.ts

Lines changed: 82 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { File } from "@babel/core";
1+
import type { File, types as t } from "@babel/core";
22
import type { NodePath } from "@babel/traverse";
33
import { hasOwnDecorators } from "./decorators";
44

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

129-
export function verifyUsedFeatures(path: NodePath, file: File) {
129+
export function shouldTransform(path: NodePath<t.Class>, file: File): boolean {
130+
let decoratorPath: NodePath<t.Decorator> | null = null;
131+
let publicFieldPath: NodePath<t.ClassProperty> | null = null;
132+
let privateFieldPath: NodePath<t.ClassPrivateProperty> | null = null;
133+
let privateMethodPath: NodePath<t.ClassPrivateMethod> | null = null;
134+
let staticBlockPath: NodePath<t.StaticBlock> | null = null;
135+
130136
if (hasOwnDecorators(path.node)) {
131-
if (!hasFeature(file, FEATURES.decorators)) {
132-
throw path.buildCodeFrameError(
133-
"Decorators are not enabled." +
134-
"\nIf you are using " +
135-
'["@babel/plugin-proposal-decorators", { "legacy": true }], ' +
136-
'make sure it comes *before* "@babel/plugin-proposal-class-properties" ' +
137-
"and enable loose mode, like so:\n" +
138-
'\t["@babel/plugin-proposal-decorators", { "legacy": true }]\n' +
139-
'\t["@babel/plugin-proposal-class-properties", { "loose": true }]',
140-
);
137+
decoratorPath = path.get("decorators.0");
138+
}
139+
for (const el of path.get("body.body")) {
140+
if (!decoratorPath && hasOwnDecorators(el.node)) {
141+
decoratorPath = el.get("decorators.0");
141142
}
142-
143-
if (path.isPrivate()) {
144-
throw path.buildCodeFrameError(
145-
`Private ${
146-
path.isClassMethod() ? "methods" : "fields"
147-
} in decorated classes are not supported yet.`,
148-
);
143+
if (!publicFieldPath && el.isClassProperty()) {
144+
publicFieldPath = el;
145+
}
146+
if (!privateFieldPath && el.isClassPrivateProperty()) {
147+
privateFieldPath = el;
148+
}
149+
// NOTE: path.isClassPrivateMethod() it isn't supported in <7.2.0
150+
if (!privateMethodPath && el.isClassPrivateMethod?.()) {
151+
privateMethodPath = el;
152+
}
153+
if (!staticBlockPath && el.isStaticBlock?.()) {
154+
staticBlockPath = el;
149155
}
150156
}
151157

152-
// NOTE: path.isClassPrivateMethod() it isn't supported in <7.2.0
153-
if (path.isClassPrivateMethod?.()) {
154-
if (!hasFeature(file, FEATURES.privateMethods)) {
155-
throw path.buildCodeFrameError("Class private methods are not enabled.");
156-
}
158+
if (decoratorPath && privateFieldPath) {
159+
throw privateFieldPath.buildCodeFrameError(
160+
"Private fields in decorated classes are not supported yet.",
161+
);
162+
}
163+
if (decoratorPath && privateMethodPath) {
164+
throw privateMethodPath.buildCodeFrameError(
165+
"Private methods in decorated classes are not supported yet.",
166+
);
167+
}
168+
169+
if (decoratorPath && !hasFeature(file, FEATURES.decorators)) {
170+
throw path.buildCodeFrameError(
171+
"Decorators are not enabled." +
172+
"\nIf you are using " +
173+
'["@babel/plugin-proposal-decorators", { "legacy": true }], ' +
174+
'make sure it comes *before* "@babel/plugin-proposal-class-properties" ' +
175+
"and enable loose mode, like so:\n" +
176+
'\t["@babel/plugin-proposal-decorators", { "legacy": true }]\n' +
177+
'\t["@babel/plugin-proposal-class-properties", { "loose": true }]',
178+
);
179+
}
180+
181+
if (privateMethodPath && !hasFeature(file, FEATURES.privateMethods)) {
182+
throw privateMethodPath.buildCodeFrameError(
183+
"Class private methods are not enabled. " +
184+
"Please add `@babel/plugin-proposal-private-method` to your configuration.",
185+
);
157186
}
158187

159188
if (
160-
path.isPrivateName() &&
161-
path.parentPath.isBinaryExpression({
162-
operator: "in",
163-
left: path.node,
164-
})
189+
(publicFieldPath || privateFieldPath) &&
190+
!hasFeature(file, FEATURES.fields) &&
191+
// We want to allow enabling the private-methods plugin even without enabling
192+
// the class-properties plugin. Class fields will still be compiled in classes
193+
// that contain private methods.
194+
// This is already allowed with the other various class features plugins, but
195+
// it's because they can fallback to a transform separated from this helper.
196+
!hasFeature(file, FEATURES.privateMethods)
165197
) {
166-
if (!hasFeature(file, FEATURES.privateIn)) {
167-
throw path.buildCodeFrameError(
168-
"Private property in checks are not enabled.",
169-
);
170-
}
198+
throw path.buildCodeFrameError(
199+
"Class fields are not enabled. " +
200+
"Please add `@babel/plugin-proposal-class-properties` to your configuration.",
201+
);
171202
}
172203

173-
if (path.isProperty()) {
174-
if (!hasFeature(file, FEATURES.fields)) {
175-
throw path.buildCodeFrameError("Class fields are not enabled.");
176-
}
204+
if (staticBlockPath && !hasFeature(file, FEATURES.staticBlocks)) {
205+
throw path.buildCodeFrameError(
206+
"Static class blocks are not enabled. " +
207+
"Please add `@babel/plugin-proposal-class-static-block` to your configuration.",
208+
);
177209
}
178210

179-
if (path.isStaticBlock?.()) {
180-
if (!hasFeature(file, FEATURES.staticBlocks)) {
181-
throw path.buildCodeFrameError(
182-
"Static class blocks are not enabled. " +
183-
"Please add `@babel/plugin-proposal-class-static-block` to your configuration.",
184-
);
185-
}
211+
if (decoratorPath || privateMethodPath || staticBlockPath) {
212+
// If one of those feature is used we know that its transform is
213+
// enabled, otherwise the previous checks throw.
214+
return true;
186215
}
216+
if (
217+
(publicFieldPath || privateFieldPath) &&
218+
hasFeature(file, FEATURES.fields)
219+
) {
220+
return true;
221+
}
222+
223+
return false;
187224
}

packages/babel-helper-create-class-features-plugin/src/index.ts

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,7 @@ import {
1212
import type { PropPath } from "./fields";
1313
import { buildDecoratedClass, hasDecorators } from "./decorators";
1414
import { injectInitialization, extractComputedKeys } from "./misc";
15-
import {
16-
enableFeature,
17-
verifyUsedFeatures,
18-
FEATURES,
19-
isLoose,
20-
} from "./features";
15+
import { enableFeature, FEATURES, isLoose, shouldTransform } from "./features";
2116
import { assertFieldTransformed } from "./typescript";
2217
import type { ParserOptions } from "@babel/parser";
2318

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

101-
verifyUsedFeatures(path, this.file);
96+
if (!shouldTransform(path, this.file)) return;
10297

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

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

115110
for (const path of body.get("body")) {
116-
verifyUsedFeatures(path, this.file);
117-
118111
if (
119112
// check path.node.computed is enough, but ts will complain
120113
(path.isClassProperty() || path.isClassMethod()) &&
@@ -266,17 +259,6 @@ export function createClassFeaturePlugin({
266259
}
267260
},
268261

269-
PrivateName(path: NodePath<t.PrivateName>) {
270-
if (
271-
this.file.get(versionKey) !== version ||
272-
path.parentPath.isPrivate({ key: path.node })
273-
) {
274-
return;
275-
}
276-
277-
throw path.buildCodeFrameError(`Unknown PrivateName "${path}"`);
278-
},
279-
280262
ExportDefaultDeclaration(path: NodePath<t.ExportDefaultDeclaration>) {
281263
if (this.file.get(versionKey) !== version) return;
282264

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"presets": [["env", { "shippedProposals": true }]],
33
"targets": {
4-
"chrome": "75"
4+
"chrome": "70"
55
}
66
}

packages/babel-preset-env/src/plugins-compat-data.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,4 @@ for (const plugin of Object.keys(bugfixPlugins)) {
1717
}
1818
}
1919

20-
pluginsFiltered["proposal-class-properties"] =
21-
pluginsFiltered["proposal-private-methods"];
22-
2320
export { pluginsFiltered as plugins, bugfixPluginsFiltered as pluginsBugfixes };

packages/babel-preset-env/test/fixtures/bugfixes/_esmodules-no-bugfixes/stdout.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Using modules transform: auto
1818
Using plugins:
1919
proposal-class-static-block { android, chrome < 94, edge < 94, firefox < 93, ios, node < 16.11, opera < 80, safari, samsung }
2020
proposal-private-property-in-object { android, chrome < 91, edge < 91, firefox < 90, ios < 15, node < 16.9, opera < 77, safari < 15, samsung }
21-
proposal-class-properties { android, chrome < 84, edge < 84, firefox < 90, ios < 15, node < 14.6, opera < 70, safari < 15, samsung < 14 }
21+
proposal-class-properties { android, chrome < 74, edge < 79, firefox < 90, ios < 15, opera < 62, safari < 14.1, samsung < 11 }
2222
proposal-private-methods { android, chrome < 84, edge < 84, firefox < 90, ios < 15, node < 14.6, opera < 70, safari < 15, samsung < 14 }
2323
proposal-numeric-separator { android, chrome < 75, edge < 79, firefox < 70, ios < 13, opera < 62, safari < 13, samsung < 11 }
2424
proposal-logical-assignment-operators { android, chrome < 85, edge < 85, firefox < 79, ios < 14, node < 15, opera < 71, safari < 14, samsung < 14 }

packages/babel-preset-env/test/fixtures/bugfixes/_esmodules/stdout.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Using modules transform: auto
1818
Using plugins:
1919
proposal-class-static-block { android, chrome < 94, edge < 94, firefox < 93, ios, node < 16.11, opera < 80, safari, samsung }
2020
proposal-private-property-in-object { android, chrome < 91, edge < 91, firefox < 90, ios < 15, node < 16.9, opera < 77, safari < 15, samsung }
21-
proposal-class-properties { android, chrome < 84, edge < 84, firefox < 90, ios < 15, node < 14.6, opera < 70, safari < 15, samsung < 14 }
21+
proposal-class-properties { android, chrome < 74, edge < 79, firefox < 90, ios < 15, opera < 62, safari < 14.1, samsung < 11 }
2222
proposal-private-methods { android, chrome < 84, edge < 84, firefox < 90, ios < 15, node < 14.6, opera < 70, safari < 15, samsung < 14 }
2323
proposal-numeric-separator { android, chrome < 75, edge < 79, firefox < 70, ios < 13, opera < 62, safari < 13, samsung < 11 }
2424
proposal-logical-assignment-operators { android, chrome < 85, edge < 85, firefox < 79, ios < 14, node < 15, opera < 71, safari < 14, samsung < 14 }

packages/babel-preset-env/test/fixtures/bugfixes/edge-default-params-chrome-40/stdout.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Using modules transform: auto
1010
Using plugins:
1111
proposal-class-static-block { chrome < 94 }
1212
proposal-private-property-in-object { chrome < 91 }
13-
proposal-class-properties { chrome < 84 }
13+
proposal-class-properties { chrome < 74 }
1414
proposal-private-methods { chrome < 84 }
1515
proposal-numeric-separator { chrome < 75 }
1616
proposal-logical-assignment-operators { chrome < 85 }

packages/babel-preset-env/test/fixtures/bugfixes/edge-default-params-chrome-70/stdout.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Using modules transform: auto
1010
Using plugins:
1111
proposal-class-static-block { chrome < 94 }
1212
proposal-private-property-in-object { chrome < 91 }
13-
proposal-class-properties { chrome < 84 }
13+
proposal-class-properties { chrome < 74 }
1414
proposal-private-methods { chrome < 84 }
1515
proposal-numeric-separator { chrome < 75 }
1616
proposal-logical-assignment-operators { chrome < 85 }

packages/babel-preset-env/test/fixtures/bugfixes/edge-default-params-edge-14/stdout.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Using modules transform: auto
1010
Using plugins:
1111
proposal-class-static-block { edge < 94 }
1212
proposal-private-property-in-object { edge < 91 }
13-
proposal-class-properties { edge < 84 }
13+
proposal-class-properties { edge < 79 }
1414
proposal-private-methods { edge < 84 }
1515
proposal-numeric-separator { edge < 79 }
1616
proposal-logical-assignment-operators { edge < 85 }

packages/babel-preset-env/test/fixtures/bugfixes/edge-default-params-edge-15/stdout.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Using modules transform: auto
1010
Using plugins:
1111
proposal-class-static-block { edge < 94 }
1212
proposal-private-property-in-object { edge < 91 }
13-
proposal-class-properties { edge < 84 }
13+
proposal-class-properties { edge < 79 }
1414
proposal-private-methods { edge < 84 }
1515
proposal-numeric-separator { edge < 79 }
1616
proposal-logical-assignment-operators { edge < 85 }

0 commit comments

Comments
 (0)