Skip to content

Commit 4d12808

Browse files
authored
improve helper-create-class-features typings (#14530)
1 parent 9156230 commit 4d12808

11 files changed

Lines changed: 160 additions & 72 deletions

File tree

packages/babel-core/src/config/validation/plugins.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import type {
1313
} from "./option-assertions";
1414
import type { ParserOptions } from "@babel/parser";
1515
import type { Visitor } from "@babel/traverse";
16-
import type PluginPass from "../../transformation/plugin-pass";
1716
import type { ValidatedOptions } from "./options";
17+
import type { File, PluginPass } from "../../..";
1818

1919
// Note: The casts here are just meant to be static assertions to make sure
2020
// that the assertion functions actually assert that the value's type matches
@@ -78,14 +78,14 @@ type VisitorHandler =
7878
exit?: Function;
7979
};
8080

81-
export type PluginObject<S = PluginPass> = {
81+
export type PluginObject<S extends PluginPass = PluginPass> = {
8282
name?: string;
8383
manipulateOptions?: (
8484
options: ValidatedOptions,
8585
parserOpts: ParserOptions,
8686
) => void;
87-
pre?: Function;
88-
post?: Function;
87+
pre?: (this: S, file: File) => void;
88+
post?: (this: S, file: File) => void;
8989
inherits?: Function;
9090
visitor?: Visitor<S>;
9191
parserOverride?: Function;

packages/babel-core/src/transformation/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type * as t from "@babel/types";
33
type SourceMap = any;
44
import type { Handler } from "gensync";
55

6-
import type { ResolvedConfig, PluginPasses } from "../config";
6+
import type { ResolvedConfig, Plugin, PluginPasses } from "../config";
77

88
import PluginPass from "./plugin-pass";
99
import loadBlockHoistPlugin from "./block-hoist-plugin";
@@ -79,7 +79,7 @@ export function* run(
7979

8080
function* transformFile(file: File, pluginPasses: PluginPasses): Handler<void> {
8181
for (const pluginPairs of pluginPasses) {
82-
const passPairs = [];
82+
const passPairs: [Plugin, PluginPass][] = [];
8383
const passes = [];
8484
const visitors = [];
8585

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

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,51 +40,70 @@ function takeDecorators(node: Decorable) {
4040
return result;
4141
}
4242

43-
function getKey(node) {
43+
type AcceptedElement = Exclude<ClassElement, t.TSIndexSignature>;
44+
type SupportedElement = Exclude<
45+
AcceptedElement,
46+
| t.ClassPrivateMethod
47+
| t.ClassPrivateProperty
48+
| t.ClassAccessorProperty
49+
| t.StaticBlock
50+
>;
51+
52+
function getKey(node: SupportedElement) {
4453
if (node.computed) {
4554
return node.key;
4655
} else if (t.isIdentifier(node.key)) {
4756
return t.stringLiteral(node.key.name);
4857
} else {
49-
return t.stringLiteral(String(node.key.value));
58+
return t.stringLiteral(
59+
String(
60+
// A non-identifier non-computed key
61+
(node.key as t.StringLiteral | t.NumericLiteral | t.BigIntLiteral)
62+
.value,
63+
),
64+
);
5065
}
5166
}
5267

53-
// NOTE: This function can be easily bound as .bind(file, classRef, superRef)
54-
// to make it easier to use it in a loop.
5568
function extractElementDescriptor(
56-
this: File,
69+
file: File,
5770
classRef: t.Identifier,
5871
superRef: t.Identifier,
59-
path: ClassElementPath,
72+
path: NodePath<AcceptedElement>,
6073
) {
61-
const { node, scope } = path;
6274
const isMethod = path.isClassMethod();
63-
6475
if (path.isPrivate()) {
6576
throw path.buildCodeFrameError(
6677
`Private ${
6778
isMethod ? "methods" : "fields"
6879
} in decorated classes are not supported yet.`,
6980
);
7081
}
82+
if (path.node.type === "ClassAccessorProperty") {
83+
throw path.buildCodeFrameError(
84+
`Accessor properties are not supported in 2018-09 decorator transform, please specify { "version": "2021-12" } instead.`,
85+
);
86+
}
87+
if (path.node.type === "StaticBlock") {
88+
throw path.buildCodeFrameError(
89+
`Static blocks are not supported in 2018-09 decorator transform, please specify { "version": "2021-12" } instead.`,
90+
);
91+
}
92+
93+
const { node, scope } = path as NodePath<SupportedElement>;
7194

7295
new ReplaceSupers({
7396
methodPath: path,
7497
objectRef: classRef,
7598
superRef,
76-
file: this,
99+
file,
77100
refToPreserve: classRef,
78101
}).replace();
79102

80103
const properties: t.ObjectExpression["properties"] = [
81104
prop("kind", t.stringLiteral(t.isClassMethod(node) ? node.kind : "field")),
82105
prop("decorators", takeDecorators(node as Decorable)),
83-
prop(
84-
"static",
85-
// @ts-expect-error: TS doesn't infer that node is not a StaticBlock
86-
!t.isStaticBlock?.(node) && node.static && t.booleanLiteral(true),
87-
),
106+
prop("static", node.static && t.booleanLiteral(true)),
88107
prop("key", getKey(node)),
89108
].filter(Boolean);
90109

@@ -146,9 +165,20 @@ export function buildDecoratedClass(
146165
const classDecorators = takeDecorators(node);
147166
const definitions = t.arrayExpression(
148167
elements
149-
// @ts-expect-error Ignore TypeScript's abstract methods (see #10514)
150-
.filter(element => !element.node.abstract)
151-
.map(extractElementDescriptor.bind(file, node.id, superId)),
168+
.filter(
169+
element =>
170+
// @ts-expect-error Ignore TypeScript's abstract methods (see #10514)
171+
!element.node.abstract && element.node.type !== "TSIndexSignature",
172+
)
173+
.map(path =>
174+
extractElementDescriptor(
175+
file,
176+
node.id,
177+
superId,
178+
// @ts-expect-error TS can not exclude TSIndexSignature
179+
path,
180+
),
181+
),
152182
);
153183

154184
const wrapperCall = template.expression.ast`

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

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -850,17 +850,19 @@ function buildPrivateMethodDeclaration(
850850
);
851851
}
852852

853-
const thisContextVisitor = traverse.visitors.merge<{
853+
type ReplaceThisState = {
854854
classRef: t.Identifier;
855855
needsClassRef: boolean;
856-
innerBinding: t.Identifier;
857-
}>([
856+
innerBinding: t.Identifier | null;
857+
};
858+
859+
const thisContextVisitor = traverse.visitors.merge<ReplaceThisState>([
858860
{
859861
ThisExpression(path, state) {
860862
state.needsClassRef = true;
861863
path.replaceWith(t.cloneNode(state.classRef));
862864
},
863-
MetaProperty(path: NodePath<t.MetaProperty>) {
865+
MetaProperty(path) {
864866
const meta = path.get("meta");
865867
const property = path.get("property");
866868
const { scope } = path;
@@ -877,8 +879,8 @@ const thisContextVisitor = traverse.visitors.merge<{
877879
environmentVisitor,
878880
]);
879881

880-
const innerReferencesVisitor = {
881-
ReferencedIdentifier(path: NodePath<t.Identifier>, state) {
882+
const innerReferencesVisitor: Visitor<ReplaceThisState> = {
883+
ReferencedIdentifier(path, state) {
882884
if (
883885
path.scope.bindingIdentifierEquals(path.node.name, state.innerBinding)
884886
) {
@@ -895,9 +897,9 @@ function replaceThisContext(
895897
file: File,
896898
isStaticBlock: boolean,
897899
constantSuper: boolean,
898-
innerBindingRef: t.Identifier,
900+
innerBindingRef: t.Identifier | null,
899901
) {
900-
const state = {
902+
const state: ReplaceThisState = {
901903
classRef: ref,
902904
needsClassRef: false,
903905
innerBinding: innerBindingRef,
@@ -922,7 +924,12 @@ function replaceThisContext(
922924
path.traverse(thisContextVisitor, state);
923925
}
924926

925-
if (state.classRef?.name && state.classRef.name !== innerBindingRef?.name) {
927+
// todo: use innerBinding.referencePaths to avoid full traversal
928+
if (
929+
innerBindingRef != null &&
930+
state.classRef?.name &&
931+
state.classRef.name !== innerBindingRef?.name
932+
) {
926933
path.traverse(innerReferencesVisitor, state);
927934
}
928935

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

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { types as t } from "@babel/core";
2-
import type { File, PluginAPI, PluginObject } from "@babel/core";
2+
import type { PluginAPI, PluginObject } from "@babel/core";
33
import type { NodePath } from "@babel/traverse";
44
import nameFunction from "@babel/helper-function-name";
55
import splitExportDeclaration from "@babel/helper-split-export-declaration";
@@ -45,7 +45,7 @@ export function createClassFeaturePlugin({
4545
// @ts-ignore TODO(Babel 8): Remove the default value
4646
api = { assumption: () => void 0 },
4747
inherits,
48-
}: Options) {
48+
}: Options): PluginObject {
4949
const setPublicClassFields = api.assumption("setPublicClassFields");
5050
const privateFieldsAsProperties = api.assumption("privateFieldsAsProperties");
5151
const constantSuper = api.assumption("constantSuper");
@@ -81,23 +81,23 @@ export function createClassFeaturePlugin({
8181
manipulateOptions,
8282
inherits,
8383

84-
pre() {
85-
enableFeature(this.file, feature, loose);
84+
pre(file) {
85+
enableFeature(file, feature, loose);
8686

87-
if (!this.file.get(versionKey) || this.file.get(versionKey) < version) {
88-
this.file.set(versionKey, version);
87+
if (!file.get(versionKey) || file.get(versionKey) < version) {
88+
file.set(versionKey, version);
8989
}
9090
},
9191

9292
visitor: {
93-
Class(path: NodePath<t.Class>, state: File) {
94-
if (this.file.get(versionKey) !== version) return;
93+
Class(path, { file }) {
94+
if (file.get(versionKey) !== version) return;
9595

96-
if (!shouldTransform(path, this.file)) return;
96+
if (!shouldTransform(path, file)) return;
9797

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

100-
const loose = isLoose(this.file, feature);
100+
const loose = isLoose(file, feature);
101101

102102
let constructor: NodePath<t.ClassMethod>;
103103
const isDecorated = hasDecorators(path.node);
@@ -186,7 +186,7 @@ export function createClassFeaturePlugin({
186186
const privateNamesNodes = buildPrivateNamesNodes(
187187
privateNamesMap,
188188
(privateFieldsAsProperties ?? loose) as boolean,
189-
state,
189+
file,
190190
);
191191

192192
transformPrivateNamesUsage(
@@ -198,7 +198,7 @@ export function createClassFeaturePlugin({
198198
noDocumentAll,
199199
innerBinding,
200200
},
201-
state,
201+
file,
202202
);
203203

204204
let keysNodes: t.Statement[],
@@ -213,17 +213,17 @@ export function createClassFeaturePlugin({
213213
ref,
214214
path,
215215
elements,
216-
this.file,
216+
file,
217217
));
218218
} else {
219-
keysNodes = extractComputedKeys(ref, path, computedPaths, this.file);
219+
keysNodes = extractComputedKeys(path, computedPaths, file);
220220
({ staticNodes, pureStaticNodes, instanceNodes, wrapClass } =
221221
buildFieldsInitNodes(
222222
ref,
223223
path.node.superClass,
224224
props,
225225
privateNamesMap,
226-
state,
226+
file,
227227
(setPublicClassFields ?? loose) as boolean,
228228
(privateFieldsAsProperties ?? loose) as boolean,
229229
(constantSuper ?? loose) as boolean,
@@ -260,8 +260,8 @@ export function createClassFeaturePlugin({
260260
}
261261
},
262262

263-
ExportDefaultDeclaration(path: NodePath<t.ExportDefaultDeclaration>) {
264-
if (this.file.get(versionKey) !== version) return;
263+
ExportDefaultDeclaration(path, { file }) {
264+
if (file.get(versionKey) !== version) return;
265265

266266
const decl = path.get("declaration");
267267

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

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import environmentVisitor from "@babel/helper-environment-visitor";
55

66
const findBareSupers = traverse.visitors.merge<NodePath<t.CallExpression>[]>([
77
{
8-
Super(path: NodePath<t.Super>) {
8+
Super(path) {
99
const { node, parentPath } = path;
1010
if (parentPath.isCallExpression({ callee: node })) {
1111
this.push(parentPath);
@@ -15,24 +15,29 @@ const findBareSupers = traverse.visitors.merge<NodePath<t.CallExpression>[]>([
1515
environmentVisitor,
1616
]);
1717

18-
const referenceVisitor = {
19-
"TSTypeAnnotation|TypeAnnotation"(path: NodePath) {
18+
const referenceVisitor: Visitor<{ scope: Scope }> = {
19+
"TSTypeAnnotation|TypeAnnotation"(
20+
path: NodePath<t.TSTypeAnnotation | t.TypeAnnotation>,
21+
) {
2022
path.skip();
2123
},
2224

23-
ReferencedIdentifier(path: NodePath<t.Identifier>) {
24-
if (this.scope.hasOwnBinding(path.node.name)) {
25-
this.scope.rename(path.node.name);
25+
ReferencedIdentifier(path: NodePath<t.Identifier>, { scope }) {
26+
if (scope.hasOwnBinding(path.node.name)) {
27+
scope.rename(path.node.name);
2628
path.skip();
2729
}
2830
},
2931
};
32+
33+
type HandleClassTDZState = {
34+
classBinding: Binding;
35+
file: File;
36+
};
37+
3038
function handleClassTDZ(
3139
path: NodePath<t.Identifier>,
32-
state: {
33-
classBinding: Binding;
34-
file: File;
35-
},
40+
state: HandleClassTDZState,
3641
) {
3742
if (
3843
state.classBinding &&
@@ -48,7 +53,7 @@ function handleClassTDZ(
4853
}
4954
}
5055

51-
const classFieldDefinitionEvaluationTDZVisitor = {
56+
const classFieldDefinitionEvaluationTDZVisitor: Visitor<HandleClassTDZState> = {
5257
ReferencedIdentifier: handleClassTDZ,
5358
};
5459

@@ -89,7 +94,7 @@ export function injectInitialization(
8994
}
9095

9196
if (isDerived) {
92-
const bareSupers = [];
97+
const bareSupers: NodePath<t.CallExpression>[] = [];
9398
constructor.traverse(findBareSupers, bareSupers);
9499
let isFirst = true;
95100
for (const bareSuper of bareSupers) {
@@ -106,12 +111,11 @@ export function injectInitialization(
106111
}
107112

108113
export function extractComputedKeys(
109-
ref: t.Identifier,
110114
path: NodePath<t.Class>,
111115
computedPaths: NodePath<t.ClassProperty | t.ClassMethod>[],
112116
file: File,
113117
) {
114-
const declarations: t.Statement[] = [];
118+
const declarations: t.ExpressionStatement[] = [];
115119
const state = {
116120
classBinding: path.node.id && path.scope.getBinding(path.node.id.name),
117121
file,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@f class C {
2+
accessor x;
3+
}

0 commit comments

Comments
 (0)