From 5c19a65a09ee3909412ce33fb1e284fb8cf72ca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Wed, 19 Jan 2022 17:58:54 -0500 Subject: [PATCH 1/7] refactor: split virtual types validators --- Gulpfile.mjs | 1 - .../scripts/generators/validators.js | 28 +--- .../scripts/generators/virtual-types.js | 27 ---- .../src/path/generated/validators.ts | 62 +------ .../src/path/generated/virtual-types.ts | 26 --- packages/babel-traverse/src/path/index.ts | 14 +- .../src/path/lib/virtual-types-validator.ts | 151 ++++++++++++++++++ .../src/path/lib/virtual-types.ts | 144 +++++------------ packages/babel-traverse/src/types.ts | 2 +- 9 files changed, 197 insertions(+), 258 deletions(-) delete mode 100644 packages/babel-traverse/scripts/generators/virtual-types.js delete mode 100644 packages/babel-traverse/src/path/generated/virtual-types.ts create mode 100644 packages/babel-traverse/src/path/lib/virtual-types-validator.ts diff --git a/Gulpfile.mjs b/Gulpfile.mjs index b803878e6194..a7b38789c6dc 100644 --- a/Gulpfile.mjs +++ b/Gulpfile.mjs @@ -525,7 +525,6 @@ gulp.task("generate-type-helpers", () => { generateTypeHelpers("ast-types"), generateTraverseHelpers("asserts"), generateTraverseHelpers("validators"), - generateTraverseHelpers("virtual-types"), ]); }); diff --git a/packages/babel-traverse/scripts/generators/validators.js b/packages/babel-traverse/scripts/generators/validators.js index 1dca02cd48f8..334324180aad 100644 --- a/packages/babel-traverse/scripts/generators/validators.js +++ b/packages/babel-traverse/scripts/generators/validators.js @@ -1,14 +1,12 @@ import * as t from "@babel/types"; -import * as virtualTypes from "../../lib/path/lib/virtual-types.js"; export default function generateValidators() { let output = `/* * This file is auto-generated! Do not modify it directly. * To re-generate run 'make build' */ -import * as t from "@babel/types"; -import NodePath from "../index"; -import type { VirtualTypeAliases } from "./virtual-types"; +import type * as t from "@babel/types"; +import type NodePath from "../index"; export interface NodePathValidators { `; @@ -17,28 +15,6 @@ export interface NodePathValidators { output += `is${type}(this: NodePath, opts?: object): this is NodePath;`; } - for (const type of Object.keys(virtualTypes)) { - // TODO: Remove this check once we stop compiling to CJS - if (type === "default" || type === "__esModule") continue; - - const { types } = virtualTypes[type]; - if (type[0] === "_") continue; - if (t.NODE_FIELDS[type] || t.FLIPPED_ALIAS_KEYS[type]) { - output += `is${type}(this: NodePath, opts?: object): this is NodePath;`; - } else if (types /* in VirtualTypeAliases */) { - output += `is${type}(this: NodePath, opts?: object): this is NodePath;`; - } else if (type === "Pure") { - output += `isPure(constantsOnly?: boolean): boolean;`; - } else { - // if it don't have types, then VirtualTypeAliases[type] is t.Node - // which TS marked as always true - // eg. if (path.isBlockScope()) return; - // path resolved to `never` here - // so we have to return boolean instead of this is NodePath here - output += `is${type}(opts?: object): boolean;`; - } - } - output += ` } `; diff --git a/packages/babel-traverse/scripts/generators/virtual-types.js b/packages/babel-traverse/scripts/generators/virtual-types.js deleted file mode 100644 index ff873f0203c5..000000000000 --- a/packages/babel-traverse/scripts/generators/virtual-types.js +++ /dev/null @@ -1,27 +0,0 @@ -import * as virtualTypes from "../../lib/path/lib/virtual-types.js"; - -export default function generateValidators() { - let output = `/* - * This file is auto-generated! Do not modify it directly. - * To re-generate run 'make build' - */ -import * as t from "@babel/types"; - -export interface VirtualTypeAliases { -`; - - for (const type of Object.keys(virtualTypes)) { - // TODO: Remove this check once we stop compiling to CJS - if (type === "default" || type === "__esModule") continue; - - output += ` ${type}: ${(virtualTypes[type].types || ["Node"]) - .map(t => `t.${t}`) - .join(" | ")};`; - } - - output += ` -} -`; - - return output; -} diff --git a/packages/babel-traverse/src/path/generated/validators.ts b/packages/babel-traverse/src/path/generated/validators.ts index 7271088faca8..3967fb8c3365 100644 --- a/packages/babel-traverse/src/path/generated/validators.ts +++ b/packages/babel-traverse/src/path/generated/validators.ts @@ -2,9 +2,8 @@ * This file is auto-generated! Do not modify it directly. * To re-generate run 'make build' */ -import * as t from "@babel/types"; -import NodePath from "../index"; -import type { VirtualTypeAliases } from "./virtual-types"; +import type * as t from "@babel/types"; +import type NodePath from "../index"; export interface NodePathValidators { isAccessor( @@ -1211,61 +1210,4 @@ export interface NodePathValidators { this: NodePath, opts?: object, ): this is NodePath; - isBindingIdentifier( - this: NodePath, - opts?: object, - ): this is NodePath; - isBlockScoped(opts?: object): boolean; - isExistentialTypeParam( - this: NodePath, - opts?: object, - ): this is NodePath; - isExpression( - this: NodePath, - opts?: object, - ): this is NodePath; - isFlow( - this: NodePath, - opts?: object, - ): this is NodePath; - isForAwaitStatement( - this: NodePath, - opts?: object, - ): this is NodePath; - isGenerated(opts?: object): boolean; - isNumericLiteralTypeAnnotation( - this: NodePath, - opts?: object, - ): this is NodePath; - isPure(constantsOnly?: boolean): boolean; - isReferenced(opts?: object): boolean; - isReferencedIdentifier( - this: NodePath, - opts?: object, - ): this is NodePath; - isReferencedMemberExpression( - this: NodePath, - opts?: object, - ): this is NodePath; - isRestProperty( - this: NodePath, - opts?: object, - ): this is NodePath; - isScope( - this: NodePath, - opts?: object, - ): this is NodePath; - isSpreadProperty( - this: NodePath, - opts?: object, - ): this is NodePath; - isStatement( - this: NodePath, - opts?: object, - ): this is NodePath; - isUser(opts?: object): boolean; - isVar( - this: NodePath, - 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 deleted file mode 100644 index f741d76d7a71..000000000000 --- a/packages/babel-traverse/src/path/generated/virtual-types.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * This file is auto-generated! Do not modify it directly. - * To re-generate run 'make build' - */ -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; - RestProperty: t.RestElement; - Scope: t.Scopable | t.Pattern; - SpreadProperty: t.RestElement; - Statement: t.Statement; - User: t.Node; - Var: t.VariableDeclaration; -} diff --git a/packages/babel-traverse/src/path/index.ts b/packages/babel-traverse/src/path/index.ts index faa5af2c55bc..19930c1fc79c 100644 --- a/packages/babel-traverse/src/path/index.ts +++ b/packages/babel-traverse/src/path/index.ts @@ -22,6 +22,7 @@ import * as NodePath_removal from "./removal"; import * as NodePath_modification from "./modification"; import * as NodePath_family from "./family"; import * as NodePath_comments from "./comments"; +import * as NodePath_virtual_types_validator from "./lib/virtual-types-validator"; import type { NodePathAssetions } from "./generated/asserts"; import type { NodePathValidators } from "./generated/validators"; @@ -234,6 +235,7 @@ Object.assign( NodePath_modification, NodePath_family, NodePath_comments, + NodePath_virtual_types_validator, ); if (!process.env.BABEL_8_BREAKING) { @@ -268,14 +270,7 @@ for (const type of t.TYPES) { for (const type of Object.keys(virtualTypes) as (keyof typeof virtualTypes)[]) { if (type[0] === "_") continue; - if (t.TYPES.indexOf(type) < 0) t.TYPES.push(type); - - const virtualType = virtualTypes[type]; - - NodePath.prototype[`is${type}`] = function (opts?: any) { - // @ts-expect-error checkPath will throw when type is ExistentialTypeParam/NumericLiteralTypeAnnotation - return virtualType.checkPath(this, opts); - }; + if (!t.TYPES.includes(type)) t.TYPES.push(type); } type NodePathMixins = typeof NodePath_ancestry & @@ -288,7 +283,8 @@ type NodePathMixins = typeof NodePath_ancestry & typeof NodePath_removal & typeof NodePath_modification & typeof NodePath_family & - typeof NodePath_comments; + typeof NodePath_comments & + typeof NodePath_virtual_types_validator; // @ts-expect-error TS throws because ensureBlock returns the body node path // however, we don't use the return value and treat it as a transform and diff --git a/packages/babel-traverse/src/path/lib/virtual-types-validator.ts b/packages/babel-traverse/src/path/lib/virtual-types-validator.ts new file mode 100644 index 000000000000..94b043b3a6e7 --- /dev/null +++ b/packages/babel-traverse/src/path/lib/virtual-types-validator.ts @@ -0,0 +1,151 @@ +import type NodePath from "../index"; +import { + isBinding, + isBlockScoped as nodeIsBlockScoped, + isExportDeclaration, + isExpression as nodeIsExpression, + isFlow as nodeIsFlow, + isForStatement, + isForXStatement, + isIdentifier, + isImportDeclaration, + isImportSpecifier, + isJSXIdentifier, + isJSXMemberExpression, + isMemberExpression, + isReferenced as nodeIsReferenced, + isScope as nodeIsScope, + isStatement as nodeIsStatement, + isVar as nodeIsVar, + isVariableDeclaration, + react, +} from "@babel/types"; +import type * as t from "@babel/types"; +const { isCompatTag } = react; +import type { VirtualTypeAliases } from "./virtual-types"; + +export function isReferencedIdentifier( + this: NodePath, + opts?: any, +): this is NodePath { + const { node, parent } = this; + if (!isIdentifier(node, opts) && !isJSXMemberExpression(parent, opts)) { + if (isJSXIdentifier(node, opts)) { + if (isCompatTag(node.name)) return false; + } else { + // not a JSXIdentifier or an Identifier + return false; + } + } + + // check if node is referenced + return nodeIsReferenced(node, parent, this.parentPath.parent); +} + +export function isReferencedMemberExpression( + this: NodePath, +): this is NodePath { + const { node, parent } = this; + return isMemberExpression(node) && nodeIsReferenced(node, parent); +} + +export function isBindingIdentifier( + this: NodePath, +): this is NodePath { + const { node, parent } = this; + const grandparent = this.parentPath.parent; + return isIdentifier(node) && isBinding(node, parent, grandparent); +} + +export function isStatement(this: NodePath): this is NodePath { + const { node, parent } = this; + if (nodeIsStatement(node)) { + if (isVariableDeclaration(node)) { + if (isForXStatement(parent, { left: node })) return false; + if (isForStatement(parent, { init: node })) return false; + } + + return true; + } else { + return false; + } +} + +export function isExpression(this: NodePath): this is NodePath { + if (this.isIdentifier()) { + return this.isReferencedIdentifier(); + } else { + return nodeIsExpression(this.node); + } +} + +export function isScope( + this: NodePath, +): this is NodePath { + return nodeIsScope(this.node, this.parent); +} + +export function isReferenced(this: NodePath): boolean { + return nodeIsReferenced(this.node, this.parent); +} + +export function isBlockScoped(this: NodePath): boolean { + return nodeIsBlockScoped(this.node, this.parent); +} + +export function isVar( + this: NodePath, +): this is NodePath { + return nodeIsVar(this.node); +} + +export function isUser(this: NodePath): boolean { + return this.node && !!this.node.loc; +} + +export function isGenerated(this: NodePath): boolean { + return !this.isUser(); +} + +export function isPure(this: NodePath, opts?): boolean { + return this.scope.isPure(this.node, opts); +} + +export function isFlow( + this: NodePath, +): this is NodePath { + const { node } = this; + if (nodeIsFlow(node)) { + return true; + } else if (isImportDeclaration(node)) { + return node.importKind === "type" || node.importKind === "typeof"; + } else if (isExportDeclaration(node)) { + return node.exportKind === "type"; + } else if (isImportSpecifier(node)) { + return node.importKind === "type" || node.importKind === "typeof"; + } else { + return false; + } +} + +// TODO: 7.0 Backwards Compat +// todo: we should check RestProperty first +export function isRestProperty( + this: NodePath, +): this is NodePath { + return this.parentPath && this.parentPath.isObjectPattern(); +} + +// todo: we should check RestProperty first +export function isSpreadProperty( + this: NodePath, +): this is NodePath { + return this.parentPath && this.parentPath.isObjectExpression(); +} + +// todo: we should check isForOfStatement first +export function isForAwaitStatement( + this: NodePath, +): this is NodePath { + return this.node.await === true; +} diff --git a/packages/babel-traverse/src/path/lib/virtual-types.ts b/packages/babel-traverse/src/path/lib/virtual-types.ts index 3acf1ba5ebba..70e997a24fd5 100644 --- a/packages/babel-traverse/src/path/lib/virtual-types.ts +++ b/packages/babel-traverse/src/path/lib/virtual-types.ts @@ -1,27 +1,23 @@ -import type NodePath from "../index"; -import { - isBinding, - isBlockScoped, - isExportDeclaration, - isExpression, - isFlow, - isForStatement, - isForXStatement, - isIdentifier, - isImportDeclaration, - isImportSpecifier, - isJSXIdentifier, - isJSXMemberExpression, - isMemberExpression, - isReferenced, - isScope, - isStatement, - isVar, - isVariableDeclaration, - react, -} from "@babel/types"; -import type * as t from "@babel/types"; -const { isCompatTag } = react; +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; + RestProperty: t.RestElement; + Scope: t.Scopable | t.Pattern; + SpreadProperty: t.RestElement; + Statement: t.Statement; + User: t.Node; + Var: t.VariableDeclaration; +} type NodeTypes = t.Node["type"] | t.Comment["type"] | keyof t.Aliases; @@ -32,140 +28,74 @@ export type Wrapper = { export const ReferencedIdentifier: Wrapper = { types: ["Identifier", "JSXIdentifier"], - checkPath(path: NodePath, opts?: any): boolean { - const { node, parent } = path; - if (!isIdentifier(node, opts) && !isJSXMemberExpression(parent, opts)) { - if (isJSXIdentifier(node, opts)) { - if (isCompatTag(node.name)) return false; - } else { - // not a JSXIdentifier or an Identifier - return false; - } - } - - // check if node is referenced - return isReferenced(node, parent, path.parentPath.parent); - }, + checkPath: path => path.isReferencedIdentifier(), }; export const ReferencedMemberExpression: Wrapper = { types: ["MemberExpression"], - checkPath({ node, parent }: NodePath) { - return isMemberExpression(node) && isReferenced(node, parent); - }, + checkPath: path => path.isReferencedMemberExpression(), }; export const BindingIdentifier: Wrapper = { types: ["Identifier"], - checkPath(path: NodePath): boolean { - const { node, parent } = path; - const grandparent = path.parentPath.parent; - return isIdentifier(node) && isBinding(node, parent, grandparent); - }, + checkPath: path => path.isBindingIdentifier(), }; export const Statement: Wrapper = { types: ["Statement"], - checkPath({ node, parent }: NodePath): boolean { - if (isStatement(node)) { - if (isVariableDeclaration(node)) { - if (isForXStatement(parent, { left: node })) return false; - if (isForStatement(parent, { init: node })) return false; - } - - return true; - } else { - return false; - } - }, + checkPath: path => path.isStatement(), }; export const Expression: Wrapper = { types: ["Expression"], - checkPath(path: NodePath): boolean { - if (path.isIdentifier()) { - return path.isReferencedIdentifier(); - } else { - return isExpression(path.node); - } - }, + checkPath: path => path.isExpression(), }; export const Scope: Wrapper = { // When pattern is inside the function params, it is a scope types: ["Scopable", "Pattern"], - checkPath(path: NodePath) { - return isScope(path.node, path.parent); - }, + checkPath: path => path.isScope(), }; export const Referenced: Wrapper = { - checkPath(path: NodePath): boolean { - return isReferenced(path.node, path.parent); - }, + checkPath: path => path.isReferenced(), }; export const BlockScoped: Wrapper = { - checkPath(path: NodePath): boolean { - return isBlockScoped(path.node); - }, + checkPath: path => path.isBlockScoped(), }; export const Var: Wrapper = { types: ["VariableDeclaration"], - checkPath(path: NodePath): boolean { - return isVar(path.node); - }, + checkPath: path => path.isVar(), }; export const User: Wrapper = { - checkPath(path: NodePath): boolean { - return path.node && !!path.node.loc; - }, + checkPath: path => path.isUser(), }; export const Generated: Wrapper = { - checkPath(path: NodePath): boolean { - return !path.isUser(); - }, + checkPath: path => path.isGenerated(), }; export const Pure: Wrapper = { - checkPath(path: NodePath, constantsOnly?: boolean): boolean { - return path.scope.isPure(path.node, constantsOnly); - }, + checkPath: path => path.isPure(), }; export const Flow: Wrapper = { types: ["Flow", "ImportDeclaration", "ExportDeclaration", "ImportSpecifier"], - checkPath({ node }: NodePath): boolean { - if (isFlow(node)) { - return true; - } else if (isImportDeclaration(node)) { - return node.importKind === "type" || node.importKind === "typeof"; - } else if (isExportDeclaration(node)) { - return node.exportKind === "type"; - } else if (isImportSpecifier(node)) { - return node.importKind === "type" || node.importKind === "typeof"; - } else { - return false; - } - }, + checkPath: path => path.isFlow(), }; // TODO: 7.0 Backwards Compat export const RestProperty: Wrapper = { types: ["RestElement"], - checkPath(path: NodePath): boolean { - return path.parentPath && path.parentPath.isObjectPattern(); - }, + checkPath: path => path.isRestProperty(), }; export const SpreadProperty: Wrapper = { types: ["RestElement"], - checkPath(path: NodePath): boolean { - return path.parentPath && path.parentPath.isObjectExpression(); - }, + checkPath: path => path.isSpreadProperty(), }; export const ExistentialTypeParam: Wrapper = { @@ -178,7 +108,5 @@ export const NumericLiteralTypeAnnotation: Wrapper = { export const ForAwaitStatement: Wrapper = { types: ["ForOfStatement"], - checkPath({ node }: NodePath): boolean { - return node.await === true; - }, + checkPath: path => path.isForAwaitStatement(), }; diff --git a/packages/babel-traverse/src/types.ts b/packages/babel-traverse/src/types.ts index c0fadb16f8d4..a608defe7ca9 100644 --- a/packages/babel-traverse/src/types.ts +++ b/packages/babel-traverse/src/types.ts @@ -1,6 +1,6 @@ import type * as t from "@babel/types"; import { NodePath } from "./index"; -import { VirtualTypeAliases } from "./path/generated/virtual-types"; +import type { VirtualTypeAliases } from "./path/lib/virtual-types"; export type Visitor = VisitNodeObject & { [Type in t.Node["type"]]?: VisitNode>; From a470b9f45ec1305532a6170e9b2a931315ae4d8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Tue, 26 Jul 2022 10:34:41 -0400 Subject: [PATCH 2/7] move type predicates to interface --- .../scripts/generators/validators.js | 6 +- .../src/path/generated/validators.ts | 7 +- packages/babel-traverse/src/path/index.ts | 3 +- .../src/path/lib/virtual-types-validator.ts | 74 +++++++++++-------- .../src/path/lib/virtual-types.ts | 2 + 5 files changed, 58 insertions(+), 34 deletions(-) diff --git a/packages/babel-traverse/scripts/generators/validators.js b/packages/babel-traverse/scripts/generators/validators.js index 334324180aad..50340de31f33 100644 --- a/packages/babel-traverse/scripts/generators/validators.js +++ b/packages/babel-traverse/scripts/generators/validators.js @@ -7,8 +7,9 @@ export default function generateValidators() { */ import type * as t from "@babel/types"; import type NodePath from "../index"; +import type { VirtualTypeNodePathValidators } from "../lib/virtual-types-validator"; -export interface NodePathValidators { +interface BaseNodePathValidators { `; for (const type of [...t.TYPES].sort()) { @@ -17,6 +18,9 @@ export interface NodePathValidators { output += ` } + +export interface NodePathValidators + extends BaseNodePathValidators, VirtualTypeNodePathValidators {} `; return output; diff --git a/packages/babel-traverse/src/path/generated/validators.ts b/packages/babel-traverse/src/path/generated/validators.ts index 3967fb8c3365..908e5db8f355 100644 --- a/packages/babel-traverse/src/path/generated/validators.ts +++ b/packages/babel-traverse/src/path/generated/validators.ts @@ -4,8 +4,9 @@ */ import type * as t from "@babel/types"; import type NodePath from "../index"; +import type { VirtualTypeNodePathValidators } from "../lib/virtual-types-validator"; -export interface NodePathValidators { +interface BaseNodePathValidators { isAccessor( this: NodePath, opts?: object, @@ -1211,3 +1212,7 @@ export interface NodePathValidators { opts?: object, ): this is NodePath; } + +export interface NodePathValidators + extends BaseNodePathValidators, + VirtualTypeNodePathValidators {} diff --git a/packages/babel-traverse/src/path/index.ts b/packages/babel-traverse/src/path/index.ts index 19930c1fc79c..dde654acf7f6 100644 --- a/packages/babel-traverse/src/path/index.ts +++ b/packages/babel-traverse/src/path/index.ts @@ -283,8 +283,7 @@ type NodePathMixins = typeof NodePath_ancestry & typeof NodePath_removal & typeof NodePath_modification & typeof NodePath_family & - typeof NodePath_comments & - typeof NodePath_virtual_types_validator; + typeof NodePath_comments; // @ts-expect-error TS throws because ensureBlock returns the body node path // however, we don't use the return value and treat it as a transform and diff --git a/packages/babel-traverse/src/path/lib/virtual-types-validator.ts b/packages/babel-traverse/src/path/lib/virtual-types-validator.ts index 94b043b3a6e7..068005cf56dc 100644 --- a/packages/babel-traverse/src/path/lib/virtual-types-validator.ts +++ b/packages/babel-traverse/src/path/lib/virtual-types-validator.ts @@ -24,10 +24,40 @@ import type * as t from "@babel/types"; const { isCompatTag } = react; import type { VirtualTypeAliases } from "./virtual-types"; -export function isReferencedIdentifier( - this: NodePath, - opts?: any, -): this is NodePath { +export interface VirtualTypeNodePathValidators { + 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; + isRestProperty(opts?: object): this is NodePath; + isScope(opts?: object): this is NodePath; + isSpreadProperty(opts?: object): this is NodePath; + isStatement(opts?: object): this is NodePath; + isUser(opts?: object): boolean; + isVar(opts?: object): this is NodePath; +} + +export function isReferencedIdentifier(this: NodePath, opts?: any): boolean { const { node, parent } = this; if (!isIdentifier(node, opts) && !isJSXMemberExpression(parent, opts)) { if (isJSXIdentifier(node, opts)) { @@ -42,22 +72,18 @@ export function isReferencedIdentifier( return nodeIsReferenced(node, parent, this.parentPath.parent); } -export function isReferencedMemberExpression( - this: NodePath, -): this is NodePath { +export function isReferencedMemberExpression(this: NodePath): boolean { const { node, parent } = this; return isMemberExpression(node) && nodeIsReferenced(node, parent); } -export function isBindingIdentifier( - this: NodePath, -): this is NodePath { +export function isBindingIdentifier(this: NodePath): boolean { const { node, parent } = this; const grandparent = this.parentPath.parent; return isIdentifier(node) && isBinding(node, parent, grandparent); } -export function isStatement(this: NodePath): this is NodePath { +export function isStatement(this: NodePath): boolean { const { node, parent } = this; if (nodeIsStatement(node)) { if (isVariableDeclaration(node)) { @@ -71,7 +97,7 @@ export function isStatement(this: NodePath): this is NodePath { } } -export function isExpression(this: NodePath): this is NodePath { +export function isExpression(this: NodePath): boolean { if (this.isIdentifier()) { return this.isReferencedIdentifier(); } else { @@ -79,9 +105,7 @@ export function isExpression(this: NodePath): this is NodePath { } } -export function isScope( - this: NodePath, -): this is NodePath { +export function isScope(this: NodePath): boolean { return nodeIsScope(this.node, this.parent); } @@ -93,9 +117,7 @@ export function isBlockScoped(this: NodePath): boolean { return nodeIsBlockScoped(this.node, this.parent); } -export function isVar( - this: NodePath, -): this is NodePath { +export function isVar(this: NodePath): boolean { return nodeIsVar(this.node); } @@ -111,9 +133,7 @@ export function isPure(this: NodePath, opts?): boolean { return this.scope.isPure(this.node, opts); } -export function isFlow( - this: NodePath, -): this is NodePath { +export function isFlow(this: NodePath): boolean { const { node } = this; if (nodeIsFlow(node)) { return true; @@ -130,22 +150,16 @@ export function isFlow( // TODO: 7.0 Backwards Compat // todo: we should check RestProperty first -export function isRestProperty( - this: NodePath, -): this is NodePath { +export function isRestProperty(this: NodePath): boolean { return this.parentPath && this.parentPath.isObjectPattern(); } // todo: we should check RestProperty first -export function isSpreadProperty( - this: NodePath, -): this is NodePath { +export function isSpreadProperty(this: NodePath): boolean { return this.parentPath && this.parentPath.isObjectExpression(); } // todo: we should check isForOfStatement first -export function isForAwaitStatement( - this: NodePath, -): this is NodePath { +export function isForAwaitStatement(this: NodePath): boolean { return this.node.await === true; } diff --git a/packages/babel-traverse/src/path/lib/virtual-types.ts b/packages/babel-traverse/src/path/lib/virtual-types.ts index 70e997a24fd5..e7051c6e7915 100644 --- a/packages/babel-traverse/src/path/lib/virtual-types.ts +++ b/packages/babel-traverse/src/path/lib/virtual-types.ts @@ -1,3 +1,5 @@ +import type * as t from "@babel/types"; + export interface VirtualTypeAliases { BindingIdentifier: t.Identifier; BlockScoped: t.Node; From f5968cdb5728773e61613acc1ccfd2f2d209f100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 20 Jan 2022 14:28:08 -0500 Subject: [PATCH 3/7] fix: nodeIsBlockScoped only accept one argument --- packages/babel-traverse/src/path/lib/virtual-types-validator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-traverse/src/path/lib/virtual-types-validator.ts b/packages/babel-traverse/src/path/lib/virtual-types-validator.ts index 068005cf56dc..d2bdff0e4ba9 100644 --- a/packages/babel-traverse/src/path/lib/virtual-types-validator.ts +++ b/packages/babel-traverse/src/path/lib/virtual-types-validator.ts @@ -114,7 +114,7 @@ export function isReferenced(this: NodePath): boolean { } export function isBlockScoped(this: NodePath): boolean { - return nodeIsBlockScoped(this.node, this.parent); + return nodeIsBlockScoped(this.node); } export function isVar(this: NodePath): boolean { From faee054cdc0346b28be0a56729525c5d86d534a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 20 Jan 2022 14:47:52 -0500 Subject: [PATCH 4/7] fix: virtual types validators should validate types first --- .../src/path/lib/virtual-types-validator.ts | 106 +++++++++++++----- .../src/path/lib/virtual-types.ts | 1 + 2 files changed, 78 insertions(+), 29 deletions(-) diff --git a/packages/babel-traverse/src/path/lib/virtual-types-validator.ts b/packages/babel-traverse/src/path/lib/virtual-types-validator.ts index d2bdff0e4ba9..2ebb77765ab5 100644 --- a/packages/babel-traverse/src/path/lib/virtual-types-validator.ts +++ b/packages/babel-traverse/src/path/lib/virtual-types-validator.ts @@ -13,6 +13,7 @@ import { isJSXIdentifier, isJSXMemberExpression, isMemberExpression, + isRestElement as nodeIsRestElement, isReferenced as nodeIsReferenced, isScope as nodeIsScope, isStatement as nodeIsStatement, @@ -25,36 +26,66 @@ const { isCompatTag } = react; import type { VirtualTypeAliases } from "./virtual-types"; export interface VirtualTypeNodePathValidators { - isBindingIdentifier( + isBindingIdentifier( + this: NodePath, opts?: object, - ): this is NodePath; + ): this is NodePath; isBlockScoped(opts?: object): boolean; - isExistentialTypeParam( + /** + * @deprecated + */ + isExistentialTypeParam( + this: NodePath, opts?: object, - ): this is NodePath; - isExpression(opts?: object): this is NodePath; - isFlow(opts?: object): this is NodePath; - isForAwaitStatement( + ): this is NodePath; + isExpression( + this: NodePath, opts?: object, - ): this is NodePath; - isGenerated(opts?: object): boolean; - isNumericLiteralTypeAnnotation( + ): this is NodePath; + isFlow( + this: NodePath, + opts?: object, + ): this is NodePath; + isForAwaitStatement( + this: NodePath, opts?: object, - ): this is NodePath; + ): this is NodePath; + isGenerated(opts?: object): boolean; + /** + * @deprecated + */ + isNumericLiteralTypeAnnotation(opts?: object): void; isPure(opts?: object): boolean; isReferenced(opts?: object): boolean; - isReferencedIdentifier( + isReferencedIdentifier( + this: NodePath, + opts?: object, + ): this is NodePath; + isReferencedMemberExpression( + this: NodePath, + opts?: object, + ): this is NodePath; + isRestProperty( + this: NodePath, + opts?: object, + ): this is NodePath; + isScope( + this: NodePath, opts?: object, - ): this is NodePath; - isReferencedMemberExpression( + ): this is NodePath; + isSpreadProperty( + this: NodePath, opts?: object, - ): this is NodePath; - isRestProperty(opts?: object): this is NodePath; - isScope(opts?: object): this is NodePath; - isSpreadProperty(opts?: object): this is NodePath; - isStatement(opts?: object): this is NodePath; + ): this is NodePath; + isStatement( + this: NodePath, + opts?: object, + ): this is NodePath; isUser(opts?: object): boolean; - isVar(opts?: object): this is NodePath; + isVar( + this: NodePath, + opts?: object, + ): this is NodePath; } export function isReferencedIdentifier(this: NodePath, opts?: any): boolean { @@ -129,8 +160,8 @@ export function isGenerated(this: NodePath): boolean { return !this.isUser(); } -export function isPure(this: NodePath, opts?): boolean { - return this.scope.isPure(this.node, opts); +export function isPure(this: NodePath, constantsOnly?: boolean): boolean { + return this.scope.isPure(this.node, constantsOnly); } export function isFlow(this: NodePath): boolean { @@ -149,17 +180,34 @@ export function isFlow(this: NodePath): boolean { } // TODO: 7.0 Backwards Compat -// todo: we should check RestProperty first export function isRestProperty(this: NodePath): boolean { - return this.parentPath && this.parentPath.isObjectPattern(); + return ( + nodeIsRestElement(this.node) && + this.parentPath && + this.parentPath.isObjectPattern() + ); } -// todo: we should check RestProperty first export function isSpreadProperty(this: NodePath): boolean { - return this.parentPath && this.parentPath.isObjectExpression(); + return ( + nodeIsRestElement(this.node) && + this.parentPath && + this.parentPath.isObjectExpression() + ); +} + +export function isForAwaitStatement(this: NodePath): boolean { + return isForStatement(this.node, { await: true }); +} + +export function isExistentialTypeParam(this: NodePath): void { + throw new Error( + "`path.isExistentialTypeParam` has been renamed to `path.isExistsTypeAnnotation()` in Babel 7.", + ); } -// todo: we should check isForOfStatement first -export function isForAwaitStatement(this: NodePath): boolean { - return this.node.await === true; +export function isNumericLiteralTypeAnnotation(this: NodePath): void { + throw new Error( + "`path.isNumericLiteralTypeAnnotation()` has been renamed to `path.isNumberLiteralTypeAnnotation()` in Babel 7.", + ); } diff --git a/packages/babel-traverse/src/path/lib/virtual-types.ts b/packages/babel-traverse/src/path/lib/virtual-types.ts index e7051c6e7915..1b3f99197daa 100644 --- a/packages/babel-traverse/src/path/lib/virtual-types.ts +++ b/packages/babel-traverse/src/path/lib/virtual-types.ts @@ -1,3 +1,4 @@ +import type NodePath from "../index"; import type * as t from "@babel/types"; export interface VirtualTypeAliases { From 9505228bf0abf87d56de09e5ec964fd594757bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Tue, 26 Jul 2022 10:35:04 -0400 Subject: [PATCH 5/7] fix: register virtual types validators after base types --- packages/babel-traverse/src/path/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/babel-traverse/src/path/index.ts b/packages/babel-traverse/src/path/index.ts index dde654acf7f6..98cb5d75aa21 100644 --- a/packages/babel-traverse/src/path/index.ts +++ b/packages/babel-traverse/src/path/index.ts @@ -235,7 +235,6 @@ Object.assign( NodePath_modification, NodePath_family, NodePath_comments, - NodePath_virtual_types_validator, ); if (!process.env.BABEL_8_BREAKING) { @@ -268,6 +267,9 @@ for (const type of t.TYPES) { }; } +// Register virtual types validators after base types validators +Object.assign(NodePath.prototype, NodePath_virtual_types_validator); + for (const type of Object.keys(virtualTypes) as (keyof typeof virtualTypes)[]) { if (type[0] === "_") continue; if (!t.TYPES.includes(type)) t.TYPES.push(type); From aa82de633dc6e93ad79f9cccbb0d5c86c05d8bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Tue, 26 Jul 2022 15:24:31 -0400 Subject: [PATCH 6/7] remove virtual-types from gulpfile jsdoc comment --- Gulpfile.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gulpfile.mjs b/Gulpfile.mjs index a7b38789c6dc..b7c52913a288 100644 --- a/Gulpfile.mjs +++ b/Gulpfile.mjs @@ -142,7 +142,7 @@ async function generateTypeHelpers(helperKind, filename = "index.ts") { /** * - * @typedef {("asserts" | "validators" | "virtual-types")} TraverseHelperKind + * @typedef {("asserts" | "validators")} TraverseHelperKind * @param {TraverseHelperKind} helperKind */ function generateTraverseHelpers(helperKind) { From ac6b50bf8e03c3f30602967a470d02a08c54d504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Wed, 27 Jul 2022 11:18:08 -0400 Subject: [PATCH 7/7] simplify virtual types --- .../src/path/lib/virtual-types.ts | 140 +++++++----------- packages/babel-traverse/src/visitors.ts | 22 +-- 2 files changed, 65 insertions(+), 97 deletions(-) diff --git a/packages/babel-traverse/src/path/lib/virtual-types.ts b/packages/babel-traverse/src/path/lib/virtual-types.ts index 1b3f99197daa..8ae52e2bab36 100644 --- a/packages/babel-traverse/src/path/lib/virtual-types.ts +++ b/packages/babel-traverse/src/path/lib/virtual-types.ts @@ -1,4 +1,3 @@ -import type NodePath from "../index"; import type * as t from "@babel/types"; export interface VirtualTypeAliases { @@ -24,92 +23,57 @@ export interface VirtualTypeAliases { type NodeTypes = t.Node["type"] | t.Comment["type"] | keyof t.Aliases; -export type Wrapper = { - types?: NodeTypes[]; - checkPath?(path: NodePath): boolean; -}; - -export const ReferencedIdentifier: Wrapper = { - types: ["Identifier", "JSXIdentifier"], - checkPath: path => path.isReferencedIdentifier(), -}; - -export const ReferencedMemberExpression: Wrapper = { - types: ["MemberExpression"], - checkPath: path => path.isReferencedMemberExpression(), -}; - -export const BindingIdentifier: Wrapper = { - types: ["Identifier"], - checkPath: path => path.isBindingIdentifier(), -}; - -export const Statement: Wrapper = { - types: ["Statement"], - checkPath: path => path.isStatement(), -}; - -export const Expression: Wrapper = { - types: ["Expression"], - checkPath: path => path.isExpression(), -}; - -export const Scope: Wrapper = { - // When pattern is inside the function params, it is a scope - types: ["Scopable", "Pattern"], - checkPath: path => path.isScope(), -}; - -export const Referenced: Wrapper = { - checkPath: path => path.isReferenced(), -}; - -export const BlockScoped: Wrapper = { - checkPath: path => path.isBlockScoped(), -}; - -export const Var: Wrapper = { - types: ["VariableDeclaration"], - checkPath: path => path.isVar(), -}; - -export const User: Wrapper = { - checkPath: path => path.isUser(), -}; - -export const Generated: Wrapper = { - checkPath: path => path.isGenerated(), -}; - -export const Pure: Wrapper = { - checkPath: path => path.isPure(), -}; - -export const Flow: Wrapper = { - types: ["Flow", "ImportDeclaration", "ExportDeclaration", "ImportSpecifier"], - checkPath: path => path.isFlow(), -}; +type VirtualTypeMapping = readonly NodeTypes[] | null; + +export const ReferencedIdentifier: VirtualTypeMapping = [ + "Identifier", + "JSXIdentifier", +] as const; + +export const ReferencedMemberExpression: VirtualTypeMapping = [ + "MemberExpression", +] as const; + +export const BindingIdentifier: VirtualTypeMapping = ["Identifier"] as const; + +export const Statement: VirtualTypeMapping = ["Statement"] as const; + +export const Expression: VirtualTypeMapping = ["Expression"] as const; + +export const Scope: VirtualTypeMapping = ["Scopable", "Pattern"] as const; + +export const Referenced: VirtualTypeMapping = null as null; + +export const BlockScoped: VirtualTypeMapping = null as null; + +export const Var: VirtualTypeMapping = ["VariableDeclaration"]; + +export const User: VirtualTypeMapping = null as null; + +export const Generated: VirtualTypeMapping = null as null; + +export const Pure: VirtualTypeMapping = null as null; + +export const Flow: VirtualTypeMapping = [ + "Flow", + "ImportDeclaration", + "ExportDeclaration", + "ImportSpecifier", +] as const; // TODO: 7.0 Backwards Compat -export const RestProperty: Wrapper = { - types: ["RestElement"], - checkPath: path => path.isRestProperty(), -}; - -export const SpreadProperty: Wrapper = { - types: ["RestElement"], - checkPath: path => path.isSpreadProperty(), -}; - -export const ExistentialTypeParam: Wrapper = { - types: ["ExistsTypeAnnotation"], -}; - -export const NumericLiteralTypeAnnotation: Wrapper = { - types: ["NumberLiteralTypeAnnotation"], -}; - -export const ForAwaitStatement: Wrapper = { - types: ["ForOfStatement"], - checkPath: path => path.isForAwaitStatement(), -}; +export const RestProperty: VirtualTypeMapping = ["RestElement"] as const; + +export const SpreadProperty: VirtualTypeMapping = ["RestElement"] as const; + +export const ExistentialTypeParam: VirtualTypeMapping = [ + "ExistsTypeAnnotation", +] as const; + +export const NumericLiteralTypeAnnotation: VirtualTypeMapping = [ + "NumberLiteralTypeAnnotation", +] as const; + +export const ForAwaitStatement: VirtualTypeMapping = [ + "ForOfStatement", +] as const; diff --git a/packages/babel-traverse/src/visitors.ts b/packages/babel-traverse/src/visitors.ts index 5b6e5b672fc5..21301ccdba76 100644 --- a/packages/babel-traverse/src/visitors.ts +++ b/packages/babel-traverse/src/visitors.ts @@ -1,8 +1,12 @@ import * as virtualTypes from "./path/lib/virtual-types"; -import type { Wrapper } from "./path/lib/virtual-types"; import { DEPRECATED_KEYS, FLIPPED_ALIAS_KEYS, TYPES } from "@babel/types"; import type { NodePath, Visitor } from "./index"; +type VIRTUAL_TYPES = keyof typeof virtualTypes; +function isVirtualType(type: string): type is VIRTUAL_TYPES { + return type in virtualTypes; +} + /** * explode() will take a visitor object with all of the various shorthands * that we support, and validates & normalizes it into a common format, ready @@ -55,26 +59,26 @@ export function explode(visitor: Visitor) { for (const nodeType of Object.keys(visitor)) { if (shouldIgnoreKey(nodeType)) continue; - // @ts-expect-error Fixme: nodeType could index virtualTypes - const wrapper = virtualTypes[nodeType]; - if (!wrapper) continue; + if (!isVirtualType(nodeType)) continue; // wrap all the functions const fns = visitor[nodeType]; for (const type of Object.keys(fns)) { // @ts-expect-error manipulating visitors - fns[type] = wrapCheck(wrapper, fns[type]); + fns[type] = wrapCheck(nodeType, fns[type]); } // clear it from the visitor delete visitor[nodeType]; - if (wrapper.types) { - for (const type of wrapper.types) { + const types = virtualTypes[nodeType]; + if (types !== null) { + for (const type of types) { // merge the visitor if necessary or just put it back in if (visitor[type]) { mergePair(visitor[type], fns); } else { + // @ts-expect-error Expression produces too complex union visitor[type] = fns; } } @@ -280,9 +284,9 @@ function ensureCallbackArrays(obj: Visitor) { if (obj.exit && !Array.isArray(obj.exit)) obj.exit = [obj.exit]; } -function wrapCheck(wrapper: Wrapper, fn: Function) { +function wrapCheck(nodeType: VIRTUAL_TYPES, fn: Function) { const newFn = function (this: unknown, path: NodePath) { - if (wrapper.checkPath(path)) { + if (path[`is${nodeType}`]()) { return fn.apply(this, arguments); } };