Mark static block as FunctionParent (#13832)
* refactor: move StaticBlock definition to core * update generated types * fix: mark StaticBlock as FunctionParent * revise testcase * update babel-types docs
This commit is contained in:
parent
00f8ee3484
commit
49a0d65be1
@ -418,38 +418,38 @@ export interface NodePathValidators {
|
||||
isWhileStatement(opts?: object): this is NodePath<t.WhileStatement>;
|
||||
isWithStatement(opts?: object): this is NodePath<t.WithStatement>;
|
||||
isYieldExpression(opts?: object): this is NodePath<t.YieldExpression>;
|
||||
isBindingIdentifier(
|
||||
opts?: object,
|
||||
): this is NodePath<VirtualTypeAliases["BindingIdentifier"]>;
|
||||
isBlockScoped(opts?: object): boolean;
|
||||
isExistentialTypeParam(
|
||||
opts?: object,
|
||||
): this is NodePath<VirtualTypeAliases["ExistentialTypeParam"]>;
|
||||
isExpression(opts?: object): this is NodePath<t.Expression>;
|
||||
isFlow(opts?: object): this is NodePath<t.Flow>;
|
||||
isForAwaitStatement(
|
||||
opts?: object,
|
||||
): this is NodePath<VirtualTypeAliases["ForAwaitStatement"]>;
|
||||
isGenerated(opts?: object): boolean;
|
||||
isNumericLiteralTypeAnnotation(
|
||||
opts?: object,
|
||||
): this is NodePath<VirtualTypeAliases["NumericLiteralTypeAnnotation"]>;
|
||||
isPure(opts?: object): boolean;
|
||||
isReferenced(opts?: object): boolean;
|
||||
isReferencedIdentifier(
|
||||
opts?: object,
|
||||
): this is NodePath<VirtualTypeAliases["ReferencedIdentifier"]>;
|
||||
isReferencedMemberExpression(
|
||||
opts?: object,
|
||||
): this is NodePath<VirtualTypeAliases["ReferencedMemberExpression"]>;
|
||||
isBindingIdentifier(
|
||||
opts?: object,
|
||||
): this is NodePath<VirtualTypeAliases["BindingIdentifier"]>;
|
||||
isStatement(opts?: object): this is NodePath<t.Statement>;
|
||||
isExpression(opts?: object): this is NodePath<t.Expression>;
|
||||
isScope(opts?: object): this is NodePath<VirtualTypeAliases["Scope"]>;
|
||||
isReferenced(opts?: object): boolean;
|
||||
isBlockScoped(opts?: object): boolean;
|
||||
isVar(opts?: object): this is NodePath<VirtualTypeAliases["Var"]>;
|
||||
isUser(opts?: object): boolean;
|
||||
isGenerated(opts?: object): boolean;
|
||||
isPure(opts?: object): boolean;
|
||||
isFlow(opts?: object): this is NodePath<t.Flow>;
|
||||
isRestProperty(
|
||||
opts?: object,
|
||||
): this is NodePath<VirtualTypeAliases["RestProperty"]>;
|
||||
isScope(opts?: object): this is NodePath<VirtualTypeAliases["Scope"]>;
|
||||
isSpreadProperty(
|
||||
opts?: object,
|
||||
): this is NodePath<VirtualTypeAliases["SpreadProperty"]>;
|
||||
isExistentialTypeParam(
|
||||
opts?: object,
|
||||
): this is NodePath<VirtualTypeAliases["ExistentialTypeParam"]>;
|
||||
isNumericLiteralTypeAnnotation(
|
||||
opts?: object,
|
||||
): this is NodePath<VirtualTypeAliases["NumericLiteralTypeAnnotation"]>;
|
||||
isForAwaitStatement(
|
||||
opts?: object,
|
||||
): this is NodePath<VirtualTypeAliases["ForAwaitStatement"]>;
|
||||
isStatement(opts?: object): this is NodePath<t.Statement>;
|
||||
isUser(opts?: object): boolean;
|
||||
isVar(opts?: object): this is NodePath<VirtualTypeAliases["Var"]>;
|
||||
}
|
||||
|
||||
@ -5,22 +5,22 @@
|
||||
import * as t from "@babel/types";
|
||||
|
||||
export interface VirtualTypeAliases {
|
||||
BindingIdentifier: t.Identifier;
|
||||
BlockScoped: t.Node;
|
||||
ExistentialTypeParam: t.ExistsTypeAnnotation;
|
||||
Expression: t.Expression;
|
||||
Flow: t.Flow | t.ImportDeclaration | t.ExportDeclaration | t.ImportSpecifier;
|
||||
ForAwaitStatement: t.ForOfStatement;
|
||||
Generated: t.Node;
|
||||
NumericLiteralTypeAnnotation: t.NumberLiteralTypeAnnotation;
|
||||
Pure: t.Node;
|
||||
Referenced: t.Node;
|
||||
ReferencedIdentifier: t.Identifier | t.JSXIdentifier;
|
||||
ReferencedMemberExpression: t.MemberExpression;
|
||||
BindingIdentifier: t.Identifier;
|
||||
Statement: t.Statement;
|
||||
Expression: t.Expression;
|
||||
Scope: t.Scopable | t.Pattern;
|
||||
Referenced: t.Node;
|
||||
BlockScoped: t.Node;
|
||||
Var: t.VariableDeclaration;
|
||||
User: t.Node;
|
||||
Generated: t.Node;
|
||||
Pure: t.Node;
|
||||
Flow: t.Flow | t.ImportDeclaration | t.ExportDeclaration | t.ImportSpecifier;
|
||||
RestProperty: t.RestElement;
|
||||
Scope: t.Scopable | t.Pattern;
|
||||
SpreadProperty: t.RestElement;
|
||||
ExistentialTypeParam: t.ExistsTypeAnnotation;
|
||||
NumericLiteralTypeAnnotation: t.NumberLiteralTypeAnnotation;
|
||||
ForAwaitStatement: t.ForOfStatement;
|
||||
Statement: t.Statement;
|
||||
User: t.Node;
|
||||
Var: t.VariableDeclaration;
|
||||
}
|
||||
|
||||
@ -702,4 +702,171 @@ describe("scope", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("own bindings", () => {
|
||||
// Var declarations should be declared in the nearest FunctionParent ancestry
|
||||
describe("var declarations should be registered", () => {
|
||||
it("in program", () => {
|
||||
const program = getPath("var foo;");
|
||||
expect(program.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in function declaration", () => {
|
||||
const functionDeclaration = getPath("function f() { var foo; }").get(
|
||||
"body.0.expression",
|
||||
);
|
||||
expect(functionDeclaration.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in function expression", () => {
|
||||
const functionExpression = getPath("(function () { var foo; })").get(
|
||||
"body.0.expression",
|
||||
);
|
||||
expect(functionExpression.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in arrow expression", () => {
|
||||
const arrowExpression =
|
||||
getPath("() => { var foo; }").get("body.0.expression");
|
||||
expect(arrowExpression.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in object method", () => {
|
||||
const objectMethod = getPath("({ method() { var foo; } })").get(
|
||||
"body.0.expression.properties.0",
|
||||
);
|
||||
expect(objectMethod.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in class method", () => {
|
||||
const classMethod = getPath("(class { method() { var foo; } })").get(
|
||||
"body.0.expression.body.body.0",
|
||||
);
|
||||
expect(classMethod.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in class private method", () => {
|
||||
const classMethod = getPath("(class { #method() { var foo; } })").get(
|
||||
"body.0.expression.body.body.0",
|
||||
);
|
||||
expect(classMethod.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in static block", () => {
|
||||
const staticBlock = getPath("(class { static { var foo; } })", {
|
||||
plugins: ["classStaticBlock"],
|
||||
}).get("body.0.expression.body.body.0");
|
||||
expect(staticBlock.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
});
|
||||
describe("var declarations should not be registered", () => {
|
||||
it("in block statement", () => {
|
||||
const blockStatement = getPath("{ var foo; }").get("body.0");
|
||||
expect(blockStatement.scope.hasOwnBinding("foo")).toBe(false);
|
||||
});
|
||||
it("in catch clause", () => {
|
||||
const catchClause = getPath("try {} catch { var foo; }").get(
|
||||
"body.0.handler",
|
||||
);
|
||||
expect(catchClause.scope.hasOwnBinding("foo")).toBe(false);
|
||||
});
|
||||
it("in for-init statement", () => {
|
||||
const forStatement = getPath("for (var foo;;);").get("body.0");
|
||||
expect(forStatement.scope.hasOwnBinding("foo")).toBe(false);
|
||||
});
|
||||
it("in for-in statement", () => {
|
||||
const forStatement = getPath("for (var foo in x);").get("body.0");
|
||||
expect(forStatement.scope.hasOwnBinding("foo")).toBe(false);
|
||||
});
|
||||
it("in for-of statement", () => {
|
||||
const forStatement = getPath("for (var foo of x);").get("body.0");
|
||||
expect(forStatement.scope.hasOwnBinding("foo")).toBe(false);
|
||||
});
|
||||
it("in switch statement", () => {
|
||||
const switchStatement = getPath("switch (0) { case 0: var foo; }").get(
|
||||
"body.0",
|
||||
);
|
||||
expect(switchStatement.scope.hasOwnBinding("foo")).toBe(false);
|
||||
});
|
||||
it("in while statement", () => {
|
||||
const whileStatement = getPath("while (0) \n var foo;").get("body.0");
|
||||
expect(whileStatement.scope.hasOwnBinding("foo")).toBe(false);
|
||||
});
|
||||
it("in do-while statement", () => {
|
||||
const doWhileStatement = getPath("do \n var foo \n while(0);").get(
|
||||
"body.0",
|
||||
);
|
||||
expect(doWhileStatement.scope.hasOwnBinding("foo")).toBe(false);
|
||||
});
|
||||
});
|
||||
// Lexical declarations should be registered in the nearest BlockParent ancestry
|
||||
describe("let declarations should be registered", () => {
|
||||
it("in program", () => {
|
||||
const program = getPath("let foo;");
|
||||
expect(program.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in function declaration", () => {
|
||||
const functionDeclaration = getPath("function f() { let foo; }").get(
|
||||
"body.0.expression",
|
||||
);
|
||||
expect(functionDeclaration.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in function expression", () => {
|
||||
const functionExpression = getPath("(function () { let foo; })").get(
|
||||
"body.0.expression",
|
||||
);
|
||||
expect(functionExpression.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in arrow expression", () => {
|
||||
const arrowExpression =
|
||||
getPath("() => { let foo; }").get("body.0.expression");
|
||||
expect(arrowExpression.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in object method", () => {
|
||||
const objectMethod = getPath("({ method() { let foo; } })").get(
|
||||
"body.0.expression.properties.0",
|
||||
);
|
||||
expect(objectMethod.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in class method", () => {
|
||||
const classMethod = getPath("(class { method() { let foo; } })").get(
|
||||
"body.0.expression.body.body.0",
|
||||
);
|
||||
expect(classMethod.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in class private method", () => {
|
||||
const classMethod = getPath("(class { #method() { let foo; } })").get(
|
||||
"body.0.expression.body.body.0",
|
||||
);
|
||||
expect(classMethod.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in static block", () => {
|
||||
const staticBlock = getPath("(class { static { let foo; } })", {
|
||||
plugins: ["classStaticBlock"],
|
||||
}).get("body.0.expression.body.body.0");
|
||||
expect(staticBlock.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in block statement", () => {
|
||||
const blockStatement = getPath("{ let foo; }").get("body.0");
|
||||
expect(blockStatement.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in catch clause", () => {
|
||||
const catchClause = getPath("try {} catch { let foo; }").get(
|
||||
"body.0.handler",
|
||||
);
|
||||
expect(catchClause.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in for-init statement", () => {
|
||||
const forStatement = getPath("for (let foo;;);").get("body.0");
|
||||
expect(forStatement.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in for-in statement", () => {
|
||||
const forStatement = getPath("for (let foo in x);").get("body.0");
|
||||
expect(forStatement.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in for-of statement", () => {
|
||||
const forStatement = getPath("for (let foo of x);").get("body.0");
|
||||
expect(forStatement.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
it("in switch statement", () => {
|
||||
const switchStatement = getPath("switch (0) { case 0: let foo; }").get(
|
||||
"body.0",
|
||||
);
|
||||
expect(switchStatement.scope.hasOwnBinding("foo")).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -206,7 +206,7 @@ const aliasDescriptions = {
|
||||
ForXStatement:
|
||||
"A cover of [ForInStatements and ForOfStatements](https://tc39.es/ecma262/#sec-for-in-and-for-of-statements).",
|
||||
Function:
|
||||
"A cover of functions and [method](#method)s, the must have `body` and `params`. Note: `Function` is different to `FunctionParent`.",
|
||||
"A cover of functions and [method](#method)s, the must have `body` and `params`. Note: `Function` is different to `FunctionParent`. For example, a `StaticBlock` is a `FunctionParent` but not `Function`.",
|
||||
FunctionParent:
|
||||
"A cover of AST nodes that start an execution context with new [VariableEnvironment](https://tc39.es/ecma262/#table-additional-state-components-for-ecmascript-code-execution-contexts). In other words, they define the scope of `var` declarations. FunctionParent did not include `Program` since Babel 7.",
|
||||
Immutable:
|
||||
|
||||
@ -524,6 +524,12 @@ export function assertPrivateName(
|
||||
): asserts node is t.PrivateName {
|
||||
assert("PrivateName", node, opts);
|
||||
}
|
||||
export function assertStaticBlock(
|
||||
node: object | null | undefined,
|
||||
opts?: object | null,
|
||||
): asserts node is t.StaticBlock {
|
||||
assert("StaticBlock", node, opts);
|
||||
}
|
||||
export function assertAnyTypeAnnotation(
|
||||
node: object | null | undefined,
|
||||
opts?: object | null,
|
||||
@ -1076,12 +1082,6 @@ export function assertDecimalLiteral(
|
||||
): asserts node is t.DecimalLiteral {
|
||||
assert("DecimalLiteral", node, opts);
|
||||
}
|
||||
export function assertStaticBlock(
|
||||
node: object | null | undefined,
|
||||
opts?: object | null,
|
||||
): asserts node is t.StaticBlock {
|
||||
assert("StaticBlock", node, opts);
|
||||
}
|
||||
export function assertModuleExpression(
|
||||
node: object | null | undefined,
|
||||
opts?: object | null,
|
||||
|
||||
@ -1073,6 +1073,11 @@ export interface PrivateName extends BaseNode {
|
||||
id: Identifier;
|
||||
}
|
||||
|
||||
export interface StaticBlock extends BaseNode {
|
||||
type: "StaticBlock";
|
||||
body: Array<Statement>;
|
||||
}
|
||||
|
||||
export interface AnyTypeAnnotation extends BaseNode {
|
||||
type: "AnyTypeAnnotation";
|
||||
}
|
||||
@ -1633,11 +1638,6 @@ export interface DecimalLiteral extends BaseNode {
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface StaticBlock extends BaseNode {
|
||||
type: "StaticBlock";
|
||||
body: Array<Statement>;
|
||||
}
|
||||
|
||||
export interface ModuleExpression extends BaseNode {
|
||||
type: "ModuleExpression";
|
||||
body: Program;
|
||||
@ -2232,7 +2232,8 @@ export type FunctionParent =
|
||||
| ObjectMethod
|
||||
| ArrowFunctionExpression
|
||||
| ClassMethod
|
||||
| ClassPrivateMethod;
|
||||
| ClassPrivateMethod
|
||||
| StaticBlock;
|
||||
export type Pureish =
|
||||
| FunctionDeclaration
|
||||
| FunctionExpression
|
||||
|
||||
@ -547,6 +547,9 @@ export function classPrivateMethod(
|
||||
export function privateName(id: t.Identifier): t.PrivateName {
|
||||
return builder("PrivateName", ...arguments);
|
||||
}
|
||||
export function staticBlock(body: Array<t.Statement>): t.StaticBlock {
|
||||
return builder("StaticBlock", ...arguments);
|
||||
}
|
||||
export function anyTypeAnnotation(): t.AnyTypeAnnotation {
|
||||
return builder("AnyTypeAnnotation", ...arguments);
|
||||
}
|
||||
@ -1057,9 +1060,6 @@ export function tupleExpression(
|
||||
export function decimalLiteral(value: string): t.DecimalLiteral {
|
||||
return builder("DecimalLiteral", ...arguments);
|
||||
}
|
||||
export function staticBlock(body: Array<t.Statement>): t.StaticBlock {
|
||||
return builder("StaticBlock", ...arguments);
|
||||
}
|
||||
export function moduleExpression(body: t.Program): t.ModuleExpression {
|
||||
return builder("ModuleExpression", ...arguments);
|
||||
}
|
||||
|
||||
@ -94,6 +94,7 @@ export {
|
||||
classPrivateProperty as ClassPrivateProperty,
|
||||
classPrivateMethod as ClassPrivateMethod,
|
||||
privateName as PrivateName,
|
||||
staticBlock as StaticBlock,
|
||||
anyTypeAnnotation as AnyTypeAnnotation,
|
||||
arrayTypeAnnotation as ArrayTypeAnnotation,
|
||||
booleanTypeAnnotation as BooleanTypeAnnotation,
|
||||
@ -186,7 +187,6 @@ export {
|
||||
recordExpression as RecordExpression,
|
||||
tupleExpression as TupleExpression,
|
||||
decimalLiteral as DecimalLiteral,
|
||||
staticBlock as StaticBlock,
|
||||
moduleExpression as ModuleExpression,
|
||||
topicReference as TopicReference,
|
||||
pipelineTopicExpression as PipelineTopicExpression,
|
||||
|
||||
@ -2196,3 +2196,16 @@ defineType("PrivateName", {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
defineType("StaticBlock", {
|
||||
visitor: ["body"],
|
||||
fields: {
|
||||
body: {
|
||||
validate: chain(
|
||||
assertValueType("array"),
|
||||
assertEach(assertNodeType("Statement")),
|
||||
),
|
||||
},
|
||||
},
|
||||
aliases: ["Scopable", "BlockParent", "FunctionParent"],
|
||||
});
|
||||
|
||||
@ -116,20 +116,6 @@ defineType("DecimalLiteral", {
|
||||
aliases: ["Expression", "Pureish", "Literal", "Immutable"],
|
||||
});
|
||||
|
||||
// https://github.com/tc39/proposal-class-static-block
|
||||
defineType("StaticBlock", {
|
||||
visitor: ["body"],
|
||||
fields: {
|
||||
body: {
|
||||
validate: chain(
|
||||
assertValueType("array"),
|
||||
assertEach(assertNodeType("Statement")),
|
||||
),
|
||||
},
|
||||
},
|
||||
aliases: ["Scopable", "BlockParent"],
|
||||
});
|
||||
|
||||
// https://github.com/tc39/proposal-js-module-blocks
|
||||
defineType("ModuleExpression", {
|
||||
visitor: ["body"],
|
||||
|
||||
@ -1450,6 +1450,23 @@ export function isPrivateName(
|
||||
|
||||
return false;
|
||||
}
|
||||
export function isStaticBlock(
|
||||
node: object | null | undefined,
|
||||
opts?: object | null,
|
||||
): node is t.StaticBlock {
|
||||
if (!node) return false;
|
||||
|
||||
const nodeType = (node as t.Node).type;
|
||||
if (nodeType === "StaticBlock") {
|
||||
if (typeof opts === "undefined") {
|
||||
return true;
|
||||
} else {
|
||||
return shallowEqual(node, opts);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
export function isAnyTypeAnnotation(
|
||||
node: object | null | undefined,
|
||||
opts?: object | null,
|
||||
@ -3014,23 +3031,6 @@ export function isDecimalLiteral(
|
||||
|
||||
return false;
|
||||
}
|
||||
export function isStaticBlock(
|
||||
node: object | null | undefined,
|
||||
opts?: object | null,
|
||||
): node is t.StaticBlock {
|
||||
if (!node) return false;
|
||||
|
||||
const nodeType = (node as t.Node).type;
|
||||
if (nodeType === "StaticBlock") {
|
||||
if (typeof opts === "undefined") {
|
||||
return true;
|
||||
} else {
|
||||
return shallowEqual(node, opts);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
export function isModuleExpression(
|
||||
node: object | null | undefined,
|
||||
opts?: object | null,
|
||||
@ -4642,7 +4642,8 @@ export function isFunctionParent(
|
||||
"ObjectMethod" === nodeType ||
|
||||
"ArrowFunctionExpression" === nodeType ||
|
||||
"ClassMethod" === nodeType ||
|
||||
"ClassPrivateMethod" === nodeType
|
||||
"ClassPrivateMethod" === nodeType ||
|
||||
"StaticBlock" === nodeType
|
||||
) {
|
||||
if (typeof opts === "undefined") {
|
||||
return true;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user