diff --git a/packages/babel-traverse/src/path/generated/validators.ts b/packages/babel-traverse/src/path/generated/validators.ts index 6f6c457e37..9bc1854f47 100755 --- a/packages/babel-traverse/src/path/generated/validators.ts +++ b/packages/babel-traverse/src/path/generated/validators.ts @@ -418,38 +418,38 @@ export interface NodePathValidators { isWhileStatement(opts?: object): this is NodePath; isWithStatement(opts?: object): this is NodePath; isYieldExpression(opts?: object): this is NodePath; + isBindingIdentifier( + opts?: object, + ): this is NodePath; + isBlockScoped(opts?: object): boolean; + isExistentialTypeParam( + opts?: object, + ): this is NodePath; + isExpression(opts?: object): this is NodePath; + isFlow(opts?: object): this is NodePath; + isForAwaitStatement( + opts?: object, + ): this is NodePath; + isGenerated(opts?: object): boolean; + isNumericLiteralTypeAnnotation( + opts?: object, + ): this is NodePath; + isPure(opts?: object): boolean; + isReferenced(opts?: object): boolean; isReferencedIdentifier( opts?: object, ): this is NodePath; isReferencedMemberExpression( opts?: object, ): this is NodePath; - isBindingIdentifier( - opts?: object, - ): this is NodePath; - isStatement(opts?: object): this is NodePath; - isExpression(opts?: object): this is NodePath; - isScope(opts?: object): this is NodePath; - isReferenced(opts?: object): boolean; - isBlockScoped(opts?: object): boolean; - isVar(opts?: object): this is NodePath; - isUser(opts?: object): boolean; - isGenerated(opts?: object): boolean; - isPure(opts?: object): boolean; - isFlow(opts?: object): this is NodePath; isRestProperty( opts?: object, ): this is NodePath; + isScope(opts?: object): this is NodePath; isSpreadProperty( opts?: object, ): this is NodePath; - isExistentialTypeParam( - opts?: object, - ): this is NodePath; - isNumericLiteralTypeAnnotation( - opts?: object, - ): this is NodePath; - isForAwaitStatement( - opts?: object, - ): this is NodePath; + isStatement(opts?: object): this is NodePath; + isUser(opts?: object): boolean; + isVar(opts?: object): this is NodePath; } diff --git a/packages/babel-traverse/src/path/generated/virtual-types.ts b/packages/babel-traverse/src/path/generated/virtual-types.ts index 9ac07fc424..f741d76d7a 100755 --- a/packages/babel-traverse/src/path/generated/virtual-types.ts +++ b/packages/babel-traverse/src/path/generated/virtual-types.ts @@ -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; } diff --git a/packages/babel-traverse/test/scope.js b/packages/babel-traverse/test/scope.js index 8d3beb93e4..fbe1e15862 100644 --- a/packages/babel-traverse/test/scope.js +++ b/packages/babel-traverse/test/scope.js @@ -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); + }); + }); + }); }); diff --git a/packages/babel-types/scripts/generators/docs.js b/packages/babel-types/scripts/generators/docs.js index f7b82e56d3..2472ed4701 100644 --- a/packages/babel-types/scripts/generators/docs.js +++ b/packages/babel-types/scripts/generators/docs.js @@ -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: diff --git a/packages/babel-types/src/asserts/generated/index.ts b/packages/babel-types/src/asserts/generated/index.ts index c171b27816..6712d59837 100755 --- a/packages/babel-types/src/asserts/generated/index.ts +++ b/packages/babel-types/src/asserts/generated/index.ts @@ -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, diff --git a/packages/babel-types/src/ast-types/generated/index.ts b/packages/babel-types/src/ast-types/generated/index.ts index f4def136c3..bae7faf225 100755 --- a/packages/babel-types/src/ast-types/generated/index.ts +++ b/packages/babel-types/src/ast-types/generated/index.ts @@ -1073,6 +1073,11 @@ export interface PrivateName extends BaseNode { id: Identifier; } +export interface StaticBlock extends BaseNode { + type: "StaticBlock"; + body: Array; +} + 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; -} - 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 diff --git a/packages/babel-types/src/builders/generated/index.ts b/packages/babel-types/src/builders/generated/index.ts index aa5591c88b..7688db8b46 100755 --- a/packages/babel-types/src/builders/generated/index.ts +++ b/packages/babel-types/src/builders/generated/index.ts @@ -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.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.StaticBlock { - return builder("StaticBlock", ...arguments); -} export function moduleExpression(body: t.Program): t.ModuleExpression { return builder("ModuleExpression", ...arguments); } diff --git a/packages/babel-types/src/builders/generated/uppercase.js b/packages/babel-types/src/builders/generated/uppercase.js index 73d60dbb6f..257a1b083d 100755 --- a/packages/babel-types/src/builders/generated/uppercase.js +++ b/packages/babel-types/src/builders/generated/uppercase.js @@ -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, diff --git a/packages/babel-types/src/definitions/core.ts b/packages/babel-types/src/definitions/core.ts index fbf7e18593..abb7ca476e 100644 --- a/packages/babel-types/src/definitions/core.ts +++ b/packages/babel-types/src/definitions/core.ts @@ -2196,3 +2196,16 @@ defineType("PrivateName", { }, }, }); + +defineType("StaticBlock", { + visitor: ["body"], + fields: { + body: { + validate: chain( + assertValueType("array"), + assertEach(assertNodeType("Statement")), + ), + }, + }, + aliases: ["Scopable", "BlockParent", "FunctionParent"], +}); diff --git a/packages/babel-types/src/definitions/experimental.ts b/packages/babel-types/src/definitions/experimental.ts index 064f25e47f..1d369549e1 100644 --- a/packages/babel-types/src/definitions/experimental.ts +++ b/packages/babel-types/src/definitions/experimental.ts @@ -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"], diff --git a/packages/babel-types/src/validators/generated/index.ts b/packages/babel-types/src/validators/generated/index.ts index 7a2509842d..be46308bb5 100755 --- a/packages/babel-types/src/validators/generated/index.ts +++ b/packages/babel-types/src/validators/generated/index.ts @@ -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;