From 5585c054b347061f0677af17fca1b6cc5c93ca76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Fri, 8 Oct 2021 14:31:17 -0400 Subject: [PATCH 1/5] refactor: move StaticBlock definition to core --- packages/babel-types/src/definitions/core.ts | 13 +++++++++++++ .../babel-types/src/definitions/experimental.ts | 14 -------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/babel-types/src/definitions/core.ts b/packages/babel-types/src/definitions/core.ts index fbf7e1859342..a69181eb57d7 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"], +}); diff --git a/packages/babel-types/src/definitions/experimental.ts b/packages/babel-types/src/definitions/experimental.ts index 064f25e47f26..1d369549e134 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"], From 6e600116e7e6524beae1bcbad2e41e46584c457e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Fri, 8 Oct 2021 17:05:15 -0400 Subject: [PATCH 2/5] update generated types --- .../src/path/generated/validators.ts | 44 +++++++++---------- .../src/path/generated/virtual-types.ts | 24 +++++----- .../src/asserts/generated/index.ts | 12 ++--- .../src/ast-types/generated/index.ts | 13 +++--- .../src/builders/generated/index.ts | 6 +-- .../src/builders/generated/uppercase.js | 2 +- .../src/validators/generated/index.ts | 37 ++++++++-------- 7 files changed, 70 insertions(+), 68 deletions(-) diff --git a/packages/babel-traverse/src/path/generated/validators.ts b/packages/babel-traverse/src/path/generated/validators.ts index 6f6c457e37e5..9bc1854f47a1 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; - 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; + 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; - isFlow(opts?: object): this is NodePath; + isReferenced(opts?: object): boolean; + isReferencedIdentifier( + opts?: object, + ): this is NodePath; + isReferencedMemberExpression( + 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 9ac07fc424fe..f741d76d7a71 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 { - 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; + 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; - Flow: t.Flow | t.ImportDeclaration | t.ExportDeclaration | t.ImportSpecifier; + Referenced: t.Node; + ReferencedIdentifier: t.Identifier | t.JSXIdentifier; + ReferencedMemberExpression: t.MemberExpression; 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-types/src/asserts/generated/index.ts b/packages/babel-types/src/asserts/generated/index.ts index c171b27816cf..6712d5983751 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 f4def136c3c2..bae7faf225aa 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 aa5591c88bbd..7688db8b46be 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 73d60dbb6ff8..257a1b083d8b 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/validators/generated/index.ts b/packages/babel-types/src/validators/generated/index.ts index 7a2509842d27..be46308bb595 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; From 67ce9fb802dff41f963a78635922e37f8e38628d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Fri, 8 Oct 2021 15:53:55 -0400 Subject: [PATCH 3/5] fix: mark StaticBlock as FunctionParent --- packages/babel-traverse/test/scope.js | 182 +++++++++++++++++++ packages/babel-types/src/definitions/core.ts | 2 +- 2 files changed, 183 insertions(+), 1 deletion(-) diff --git a/packages/babel-traverse/test/scope.js b/packages/babel-traverse/test/scope.js index 8d3beb93e401..085a92c81b07 100644 --- a/packages/babel-traverse/test/scope.js +++ b/packages/babel-traverse/test/scope.js @@ -702,4 +702,186 @@ 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) { var foo; }").get("body.0"); + + expect(whileStatement.scope.hasOwnBinding("foo")).toBe(false); + }); + it("in do-while statement", () => { + const doWhileStatement = getPath("do { var foo; } 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); + }); + }); + //todo: decide whether these statements should be scopeable and blockParent + describe("let declarations should not be registered", () => { + it("in while statement", () => { + const whileStatement = getPath("while (0) { let foo; }").get("body.0"); + + expect(whileStatement.scope.hasOwnBinding("foo")).toBe(false); + }); + it("in do-while statement", () => { + const doWhileStatement = getPath("do { let foo; } while (0)").get( + "body.0", + ); + expect(doWhileStatement.scope.hasOwnBinding("foo")).toBe(false); + }); + }); + }); }); diff --git a/packages/babel-types/src/definitions/core.ts b/packages/babel-types/src/definitions/core.ts index a69181eb57d7..abb7ca476e43 100644 --- a/packages/babel-types/src/definitions/core.ts +++ b/packages/babel-types/src/definitions/core.ts @@ -2207,5 +2207,5 @@ defineType("StaticBlock", { ), }, }, - aliases: ["Scopable", "BlockParent"], + aliases: ["Scopable", "BlockParent", "FunctionParent"], }); From f6b5fae46252d6b687e341f98acee755dbd12ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Fri, 8 Oct 2021 16:39:40 -0400 Subject: [PATCH 4/5] revise testcase --- packages/babel-traverse/test/scope.js | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/packages/babel-traverse/test/scope.js b/packages/babel-traverse/test/scope.js index 085a92c81b07..fbe1e1586272 100644 --- a/packages/babel-traverse/test/scope.js +++ b/packages/babel-traverse/test/scope.js @@ -782,12 +782,11 @@ describe("scope", () => { expect(switchStatement.scope.hasOwnBinding("foo")).toBe(false); }); it("in while statement", () => { - const whileStatement = getPath("while (0) { var foo; }").get("body.0"); - + 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 { var foo; } while (0)").get( + const doWhileStatement = getPath("do \n var foo \n while(0);").get( "body.0", ); expect(doWhileStatement.scope.hasOwnBinding("foo")).toBe(false); @@ -869,19 +868,5 @@ describe("scope", () => { expect(switchStatement.scope.hasOwnBinding("foo")).toBe(true); }); }); - //todo: decide whether these statements should be scopeable and blockParent - describe("let declarations should not be registered", () => { - it("in while statement", () => { - const whileStatement = getPath("while (0) { let foo; }").get("body.0"); - - expect(whileStatement.scope.hasOwnBinding("foo")).toBe(false); - }); - it("in do-while statement", () => { - const doWhileStatement = getPath("do { let foo; } while (0)").get( - "body.0", - ); - expect(doWhileStatement.scope.hasOwnBinding("foo")).toBe(false); - }); - }); }); }); From c3a7fd9fe745d85a951795819d9698d71232aa1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Fri, 8 Oct 2021 17:08:22 -0400 Subject: [PATCH 5/5] update babel-types docs --- packages/babel-types/scripts/generators/docs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-types/scripts/generators/docs.js b/packages/babel-types/scripts/generators/docs.js index f7b82e56d394..2472ed470111 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: