diff --git a/Gulpfile.js b/Gulpfile.js index 0887d1ece5..b9beb8b8ec 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -86,13 +86,12 @@ function rename(fn) { } /** - * - * @typedef {("asserts" | "builders" | "constants" | "validators")} HelperKind - * @param {HelperKind} helperKind + * @param {string} generator + * @param {string} pkg * @param {string} filename + * @param {string} message */ -function generateTypeHelpers(helperKind, filename = "index.ts") { - const dest = `./packages/babel-types/src/${helperKind}/generated/`; +function generateHelpers(generator, dest, filename, message) { const formatCode = require("./scripts/utils/formatCode"); const stream = gulp .src(".", { base: __dirname }) @@ -101,14 +100,9 @@ function generateTypeHelpers(helperKind, filename = "index.ts") { through.obj(function (file, enc, callback) { file.path = filename; file.contents = Buffer.from( - formatCode( - require(`./packages/babel-types/scripts/generators/${helperKind}`)( - filename - ), - dest + file.path - ) + formatCode(require(generator)(filename), dest + file.path) ); - fancyLog(`${chalk.green("✔")} Generated ${helperKind}`); + fancyLog(`${chalk.green("✔")} Generated ${message}`); callback(null, file); }) ) @@ -117,6 +111,35 @@ function generateTypeHelpers(helperKind, filename = "index.ts") { return finish(stream); } +/** + * + * @typedef {("asserts" | "builders" | "constants" | "validators")} TypesHelperKind + * @param {TypesHelperKind} helperKind + * @param {string} filename + */ +async function generateTypeHelpers(helperKind, filename = "index.ts") { + return generateHelpers( + `./packages/babel-types/scripts/generators/${helperKind}`, + `./packages/babel-types/src/${helperKind}/generated/`, + filename, + `@babel/types -> ${helperKind}` + ); +} + +/** + * + * @typedef {("asserts" | "validators" | "virtual-types")} TraverseHelperKind + * @param {TraverseHelperKind} helperKind + */ +async function generateTraverseHelpers(helperKind) { + return generateHelpers( + `./packages/babel-traverse/scripts/generators/${helperKind}`, + `./packages/babel-traverse/src/path/generated/`, + `${helperKind}.ts`, + `@babel/traverse -> ${helperKind}` + ); +} + function generateStandalone() { const dest = "./packages/babel-standalone/src/generated/"; const formatCode = require("./scripts/utils/formatCode"); @@ -383,7 +406,7 @@ const standaloneBundle = [ ]; gulp.task("generate-type-helpers", () => { - fancyLog("Generating @babel/types dynamic functions"); + fancyLog("Generating @babel/types and @babel/traverse dynamic functions"); return Promise.all([ generateTypeHelpers("asserts"), @@ -392,6 +415,9 @@ gulp.task("generate-type-helpers", () => { generateTypeHelpers("constants"), generateTypeHelpers("validators"), generateTypeHelpers("ast-types"), + generateTraverseHelpers("asserts"), + generateTraverseHelpers("validators"), + generateTraverseHelpers("virtual-types"), ]); }); diff --git a/lib/babel-packages.js.flow b/lib/babel-packages.js.flow index c2f9b713ea..22d1926abb 100644 --- a/lib/babel-packages.js.flow +++ b/lib/babel-packages.js.flow @@ -168,3 +168,7 @@ declare module "@babel/helper-function-name" { declare module "@babel/helper-split-export-declaration" { declare export default function splitExportDeclaration(exportDeclaration: any): any; } + +declare module "@babel/traverse" { + declare module.exports: any; +} diff --git a/packages/babel-traverse/scripts/generators/asserts.js b/packages/babel-traverse/scripts/generators/asserts.js new file mode 100644 index 0000000000..7f666c2307 --- /dev/null +++ b/packages/babel-traverse/scripts/generators/asserts.js @@ -0,0 +1,26 @@ +"use strict"; +const t = require("@babel/types"); + +module.exports = function generateAsserts() { + 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"; + + +export interface NodePathAssetions {`; + + for (const type of t.TYPES) { + output += ` + assert${type}( + opts?: object, + ): asserts this is NodePath;`; + } + + output += ` +}`; + + return output; +}; diff --git a/packages/babel-traverse/scripts/generators/validators.js b/packages/babel-traverse/scripts/generators/validators.js new file mode 100644 index 0000000000..b8a956057e --- /dev/null +++ b/packages/babel-traverse/scripts/generators/validators.js @@ -0,0 +1,37 @@ +"use strict"; + +const t = require("@babel/types"); +const virtualTypes = require("../../lib/path/lib/virtual-types"); + +const definitions = require("@babel/types/lib/definitions"); + +module.exports = 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"; + +export interface NodePathValidators { +`; + + for (const type of t.TYPES) { + output += `is${type}(opts?: object): this is NodePath;`; + } + + for (const type of Object.keys(virtualTypes)) { + if (type[0] === "_") continue; + if (definitions.NODE_FIELDS[type] || definitions.FLIPPED_ALIAS_KEYS[type]) { + output += `is${type}(opts?: object): this is NodePath;`; + } else { + output += `is${type}(opts?: object): boolean;`; + } + } + + output += ` +} +`; + + return output; +}; diff --git a/packages/babel-traverse/scripts/generators/virtual-types.js b/packages/babel-traverse/scripts/generators/virtual-types.js new file mode 100644 index 0000000000..504e32c953 --- /dev/null +++ b/packages/babel-traverse/scripts/generators/virtual-types.js @@ -0,0 +1,26 @@ +"use strict"; + +const virtualTypes = require("../../lib/path/lib/virtual-types"); + +module.exports = 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)) { + output += ` ${type}: ${(virtualTypes[type].types || ["Node"]) + .map(t => `t.${t}`) + .join(" | ")};`; + } + + output += ` +} +`; + + return output; +}; diff --git a/packages/babel-traverse/src/cache.js b/packages/babel-traverse/src/cache.ts similarity index 100% rename from packages/babel-traverse/src/cache.js rename to packages/babel-traverse/src/cache.ts diff --git a/packages/babel-traverse/src/context.js b/packages/babel-traverse/src/context.ts similarity index 92% rename from packages/babel-traverse/src/context.js rename to packages/babel-traverse/src/context.ts index be41ecc90c..874df29580 100644 --- a/packages/babel-traverse/src/context.js +++ b/packages/babel-traverse/src/context.ts @@ -1,5 +1,6 @@ import NodePath from "./path"; import * as t from "@babel/types"; +import type Scope from "./scope"; const testing = process.env.NODE_ENV === "test"; @@ -12,10 +13,12 @@ export default class TraversalContext { } declare parentPath: NodePath; - declare scope; + declare scope: Scope; declare state; declare opts; - queue: ?Array = null; + queue: Array | null = null; + priorityQueue: Array | null = null; + declare trap?: boolean; /** * This method does a simple check to determine whether or not we really need to attempt @@ -30,7 +33,7 @@ export default class TraversalContext { if (opts[node.type]) return true; // check if we're going to traverse into this node - const keys: ?Array = t.VISITOR_KEYS[node.type]; + const keys: Array | undefined = t.VISITOR_KEYS[node.type]; if (!keys?.length) return false; // we need to traverse into this node so ensure that it has children to traverse into! @@ -41,7 +44,7 @@ export default class TraversalContext { return false; } - create(node, obj, key, listKey): NodePath { + create(node, obj, key, listKey?): NodePath { // We don't need to `.setContext()` here, since `.visitQueue()` already // calls `.pushContext`. return NodePath.get({ diff --git a/packages/babel-traverse/src/hub.js b/packages/babel-traverse/src/hub.ts similarity index 68% rename from packages/babel-traverse/src/hub.js rename to packages/babel-traverse/src/hub.ts index 3cd5c645f0..1894d9930a 100644 --- a/packages/babel-traverse/src/hub.js +++ b/packages/babel-traverse/src/hub.ts @@ -1,10 +1,10 @@ import type Scope from "./scope"; export interface HubInterface { - getCode(): ?string; - getScope(): ?Scope; - addHelper(name: string): Object; - buildError(node: Object, msg: string, Error: Class): Error; + getCode(): string | void; + getScope(): Scope | void; + addHelper(name: string): any; + buildError(node: any, msg: string, Error: new () => Error): Error; } export default class Hub implements HubInterface { diff --git a/packages/babel-traverse/src/index.js b/packages/babel-traverse/src/index.ts similarity index 66% rename from packages/babel-traverse/src/index.js rename to packages/babel-traverse/src/index.ts index 103d4c7554..0d9b5ddf00 100644 --- a/packages/babel-traverse/src/index.js +++ b/packages/babel-traverse/src/index.ts @@ -2,7 +2,11 @@ import TraversalContext from "./context"; import * as visitors from "./visitors"; import * as t from "@babel/types"; import * as cache from "./cache"; +import type NodePath from "./path"; +import type Scope from "./scope"; +import type { Visitor } from "./types"; +export type { Visitor }; export { default as NodePath } from "./path"; export { default as Scope } from "./scope"; export { default as Hub } from "./hub"; @@ -10,15 +14,38 @@ export type { HubInterface } from "./hub"; export { visitors }; -export default function traverse( - parent: Object | Array, - opts?: Object, - scope?: Object, - state: Object, - parentPath: Object, +export type TraverseOptions = + | { + scope?: Scope; + noScope?: boolean; + denylist?: string[]; + } + | Visitor; + +function traverse( + parent: t.Node, + opts: TraverseOptions, + scope: Scope | undefined, + state: S, + parentPath?: NodePath, +): void; + +function traverse( + parent: t.Node, + opts: TraverseOptions, + scope?: Scope, + state?: any, + parentPath?: NodePath, +): void; + +function traverse( + parent: t.Node, + opts: TraverseOptions = {}, + scope?: Scope, + state?: any, + parentPath?: NodePath, ) { if (!parent) return; - if (!opts) opts = {}; if (!opts.noScope && !scope) { if (parent.type !== "Program" && parent.type !== "File") { @@ -39,6 +66,8 @@ export default function traverse( traverse.node(parent, opts, scope, state, parentPath); } +export default traverse; + traverse.visitors = visitors; traverse.verify = visitors.verify; traverse.explode = visitors.explode; @@ -48,14 +77,14 @@ traverse.cheap = function (node, enter) { }; traverse.node = function ( - node: Object, - opts: Object, - scope: Object, - state: Object, - parentPath: Object, + node: t.Node, + opts: TraverseOptions, + scope?: Scope, + state?: any, + parentPath?: NodePath, skipKeys?, ) { - const keys: Array = t.VISITOR_KEYS[node.type]; + const keys = t.VISITOR_KEYS[node.type]; if (!keys) return; const context = new TraversalContext(scope, opts, state, parentPath); @@ -65,18 +94,18 @@ traverse.node = function ( } }; -traverse.clearNode = function (node, opts) { +traverse.clearNode = function (node: t.Node, opts?) { t.removeProperties(node, opts); cache.path.delete(node); }; -traverse.removeProperties = function (tree, opts) { +traverse.removeProperties = function (tree, opts?) { t.traverseFast(tree, traverse.clearNode, opts); return tree; }; -function hasDenylistedType(path, state) { +function hasDenylistedType(path: NodePath, state) { if (path.node.type === state.type) { state.has = true; path.stop(); @@ -84,8 +113,8 @@ function hasDenylistedType(path, state) { } traverse.hasType = function ( - tree: Object, - type: Object, + tree: any, + type: any, denylistTypes?: Array, ): boolean { // the node we're searching in is denylisted diff --git a/packages/babel-traverse/src/path/ancestry.js b/packages/babel-traverse/src/path/ancestry.ts similarity index 80% rename from packages/babel-traverse/src/path/ancestry.js rename to packages/babel-traverse/src/path/ancestry.ts index 73ab074b9f..b594e2d29e 100644 --- a/packages/babel-traverse/src/path/ancestry.js +++ b/packages/babel-traverse/src/path/ancestry.ts @@ -10,7 +10,9 @@ import NodePath from "./index"; * truthy value. */ -export function findParent(callback): ?NodePath { +export function findParent( + callback: (path: NodePath) => boolean, +): NodePath | null { let path = this; while ((path = path.parentPath)) { if (callback(path)) return path; @@ -24,7 +26,10 @@ export function findParent(callback): ?NodePath { * or `null` if the `callback` never returns a truthy value. */ -export function find(callback): ?NodePath { +export function find( + this: NodePath, + callback: (path: NodePath) => boolean, +): NodePath | null { let path = this; do { if (callback(path)) return path; @@ -36,15 +41,15 @@ export function find(callback): ?NodePath { * Get the parent function of the current path. */ -export function getFunctionParent(): ?NodePath { - return this.findParent(p => p.isFunction()); +export function getFunctionParent(this: NodePath): NodePath | null { + return this.findParent(p => p.isFunction()) as NodePath | null; } /** * Walk up the tree until we hit a parent node path in a list. */ -export function getStatementParent(): NodePath { +export function getStatementParent(this: NodePath): NodePath { let path = this; do { @@ -64,7 +69,7 @@ export function getStatementParent(): NodePath { ); } - return path; + return path as NodePath; } /** @@ -76,6 +81,7 @@ export function getStatementParent(): NodePath { */ export function getEarliestCommonAncestorFrom( + this: NodePath, paths: Array, ): NodePath { return this.getDeepestCommonAncestorFrom( @@ -84,7 +90,7 @@ export function getEarliestCommonAncestorFrom( let earliest; const keys = t.VISITOR_KEYS[deepest.type]; - for (const ancestry of (ancestries: Array)) { + for (const ancestry of ancestries) { const path = ancestry[i + 1]; // first path @@ -104,7 +110,7 @@ export function getEarliestCommonAncestorFrom( // handle keys const earliestKeyIndex = keys.indexOf(earliest.parentKey); - const currentKeyIndex = keys.indexOf(path.parentKey); + const currentKeyIndex = keys.indexOf(path.parentKey as string); if (earliestKeyIndex > currentKeyIndex) { // key appears before so it's earlier earliest = path; @@ -123,8 +129,9 @@ export function getEarliestCommonAncestorFrom( */ export function getDeepestCommonAncestorFrom( + this: NodePath, paths: Array, - filter?: Function, + filter?: (deepest: t.Node, i: number, ancestries: NodePath[][]) => NodePath, ): NodePath { if (!paths.length) { return this; @@ -142,7 +149,7 @@ export function getDeepestCommonAncestorFrom( // get the ancestors of the path, breaking when the parent exceeds ourselves const ancestries = paths.map(path => { - const ancestry = []; + const ancestry: NodePath[] = []; do { ancestry.unshift(path); @@ -163,7 +170,7 @@ export function getDeepestCommonAncestorFrom( depthLoop: for (let i = 0; i < minDepth; i++) { const shouldMatch = first[i]; - for (const ancestry of (ancestries: Array)) { + for (const ancestry of ancestries) { if (ancestry[i] !== shouldMatch) { // we've hit a snag break depthLoop; @@ -192,7 +199,7 @@ export function getDeepestCommonAncestorFrom( * NOTE: The current node path is included in this. */ -export function getAncestry(): Array { +export function getAncestry(this: NodePath): Array { let path = this; const paths = []; do { @@ -204,21 +211,21 @@ export function getAncestry(): Array { /** * A helper to find if `this` path is an ancestor of @param maybeDescendant */ -export function isAncestor(maybeDescendant: NodePath): boolean { +export function isAncestor(this: NodePath, maybeDescendant: NodePath): boolean { return maybeDescendant.isDescendant(this); } /** * A helper to find if `this` path is a descendant of @param maybeAncestor */ -export function isDescendant(maybeAncestor: NodePath): boolean { +export function isDescendant(this: NodePath, maybeAncestor: NodePath): boolean { return !!this.findParent(parent => parent === maybeAncestor); } -export function inType(): boolean { +export function inType(this: NodePath, ...candidateTypes: string[]): boolean { let path = this; while (path) { - for (const type of (arguments: Array)) { + for (const type of candidateTypes) { if (path.node.type === type) return true; } path = path.parentPath; diff --git a/packages/babel-traverse/src/path/comments.js b/packages/babel-traverse/src/path/comments.ts similarity index 72% rename from packages/babel-traverse/src/path/comments.js rename to packages/babel-traverse/src/path/comments.ts index 3a967aa1c8..76b16b6867 100644 --- a/packages/babel-traverse/src/path/comments.js +++ b/packages/babel-traverse/src/path/comments.ts @@ -1,11 +1,12 @@ // This file contains methods responsible for dealing with comments. import * as t from "@babel/types"; +import type NodePath from "./index"; /** * Share comments amongst siblings. */ -export function shareCommentsWithSiblings() { +export function shareCommentsWithSiblings(this: NodePath) { // NOTE: this assumes numbered keys if (typeof this.key === "string") return; @@ -27,7 +28,12 @@ export function shareCommentsWithSiblings() { } } -export function addComment(type: string, content: string, line?: boolean) { +export function addComment( + this: NodePath, + type: t.CommentTypeShorthand, + content: string, + line?: boolean, +) { t.addComment(this.node, type, content, line); } @@ -35,6 +41,10 @@ export function addComment(type: string, content: string, line?: boolean) { * Give node `comments` of the specified `type`. */ -export function addComments(type: string, comments: Array) { +export function addComments( + this: NodePath, + type: t.CommentTypeShorthand, + comments: readonly t.Comment[], +) { t.addComments(this.node, type, comments); } diff --git a/packages/babel-traverse/src/path/context.js b/packages/babel-traverse/src/path/context.ts similarity index 83% rename from packages/babel-traverse/src/path/context.js rename to packages/babel-traverse/src/path/context.ts index 03afe02054..3a9aa5620c 100644 --- a/packages/babel-traverse/src/path/context.js +++ b/packages/babel-traverse/src/path/context.ts @@ -2,8 +2,10 @@ import traverse from "../index"; import { SHOULD_SKIP, SHOULD_STOP } from "./index"; +import type TraversalContext from "../context"; +import type NodePath from "./index"; -export function call(key): boolean { +export function call(this: NodePath, key: string): boolean { const opts = this.opts; this.debug(key); @@ -19,7 +21,7 @@ export function call(key): boolean { return false; } -export function _call(fns?: Array): boolean { +export function _call(this: NodePath, fns?: Array): boolean { if (!fns) return false; for (const fn of fns) { @@ -51,7 +53,7 @@ export function _call(fns?: Array): boolean { return false; } -export function isDenylisted(): boolean { +export function isDenylisted(this: NodePath): boolean { const denylist = this.opts.denylist ?? this.opts.blacklist; return denylist && denylist.indexOf(this.node.type) > -1; } @@ -59,7 +61,7 @@ export function isDenylisted(): boolean { // TODO: Remove in Babel 8 export { isDenylisted as isBlacklisted }; -export function visit(): boolean { +export function visit(this: NodePath): boolean { if (!this.node) { return false; } @@ -97,23 +99,23 @@ export function visit(): boolean { return this.shouldStop; } -export function skip() { +export function skip(this: NodePath) { this.shouldSkip = true; } -export function skipKey(key) { +export function skipKey(this: NodePath, key: string) { if (this.skipKeys == null) { this.skipKeys = {}; } this.skipKeys[key] = true; } -export function stop() { +export function stop(this: NodePath) { // this.shouldSkip = true; this.shouldStop = true; this._traverseFlags |= SHOULD_SKIP | SHOULD_STOP; } -export function setScope() { +export function setScope(this: NodePath) { if (this.opts && this.opts.noScope) return; let path = this.parentPath; @@ -129,7 +131,7 @@ export function setScope() { if (this.scope) this.scope.init(); } -export function setContext(context) { +export function setContext(this: NodePath, context?: TraversalContext) { if (this.skipKeys != null) { this.skipKeys = {}; } @@ -153,7 +155,7 @@ export function setContext(context) { * for the new values. */ -export function resync() { +export function resync(this: NodePath) { if (this.removed) return; this._resyncParent(); @@ -162,13 +164,13 @@ export function resync() { //this._resyncRemoved(); } -export function _resyncParent() { +export function _resyncParent(this: NodePath) { if (this.parentPath) { this.parent = this.parentPath.node; } } -export function _resyncKey() { +export function _resyncKey(this: NodePath) { if (!this.container) return; if (this.node === this.container[this.key]) return; @@ -194,7 +196,7 @@ export function _resyncKey() { this.key = null; } -export function _resyncList() { +export function _resyncList(this: NodePath) { if (!this.parent || !this.inList) return; const newContainer = this.parent[this.listKey]; @@ -204,7 +206,7 @@ export function _resyncList() { this.container = newContainer || null; } -export function _resyncRemoved() { +export function _resyncRemoved(this: NodePath) { if ( this.key == null || !this.container || @@ -214,7 +216,7 @@ export function _resyncRemoved() { } } -export function popContext() { +export function popContext(this: NodePath) { this.contexts.pop(); if (this.contexts.length > 0) { this.setContext(this.contexts[this.contexts.length - 1]); @@ -223,12 +225,12 @@ export function popContext() { } } -export function pushContext(context) { +export function pushContext(this: NodePath, context: TraversalContext) { this.contexts.push(context); this.setContext(context); } -export function setup(parentPath, container, listKey, key) { +export function setup(this: NodePath, parentPath, container, listKey, key) { this.listKey = listKey; this.container = container; @@ -236,13 +238,13 @@ export function setup(parentPath, container, listKey, key) { this.setKey(key); } -export function setKey(key) { +export function setKey(this: NodePath, key) { this.key = key; this.node = this.container[this.key]; this.type = this.node?.type; } -export function requeue(pathToQueue = this) { +export function requeue(this: NodePath, pathToQueue = this) { if (pathToQueue.removed) return; // TODO: Uncomment in Babel 8. If a path is skipped, and then replaced with a @@ -263,7 +265,7 @@ export function requeue(pathToQueue = this) { } } -export function _getQueueContexts() { +export function _getQueueContexts(this: NodePath) { let path = this; let contexts = this.contexts; while (!contexts.length) { diff --git a/packages/babel-traverse/src/path/conversion.js b/packages/babel-traverse/src/path/conversion.ts similarity index 94% rename from packages/babel-traverse/src/path/conversion.js rename to packages/babel-traverse/src/path/conversion.ts index 2ca47297c9..0286959612 100644 --- a/packages/babel-traverse/src/path/conversion.js +++ b/packages/babel-traverse/src/path/conversion.ts @@ -2,27 +2,31 @@ import * as t from "@babel/types"; import nameFunction from "@babel/helper-function-name"; +import type NodePath from "./index"; -export function toComputedKey(): Object { - const node = this.node; - +export function toComputedKey(this: NodePath) { let key; if (this.isMemberExpression()) { - key = node.property; + key = this.node.property; } else if (this.isProperty() || this.isMethod()) { - key = node.key; + key = this.node.key; } else { throw new ReferenceError("todo"); } - if (!node.computed) { + // @ts-expect-error todo(flow->ts) computed does not exist in ClassPrivateProperty + if (!this.node.computed) { if (t.isIdentifier(key)) key = t.stringLiteral(key.name); } return key; } -export function ensureBlock() { +export function ensureBlock( + this: NodePath< + t.Loop | t.WithStatement | t.Function | t.LabeledStatement | t.CatchClause + >, +) { const body = this.get("body"); const bodyNode = body.node; @@ -37,7 +41,7 @@ export function ensureBlock() { return bodyNode; } - const statements = []; + const statements: Array = []; let stringPath = "body"; let key; @@ -50,15 +54,15 @@ export function ensureBlock() { stringPath += ".body.0"; if (this.isFunction()) { key = "argument"; - statements.push(t.returnStatement(body.node)); + statements.push(t.returnStatement(body.node as t.Expression)); } else { key = "expression"; - statements.push(t.expressionStatement(body.node)); + statements.push(t.expressionStatement(body.node as t.Expression)); } } this.node.body = t.blockStatement(statements); - const parentPath = this.get(stringPath); + const parentPath = this.get(stringPath) as NodePath; body.setup( parentPath, listKey ? parentPath.node[listKey] : parentPath.node, @@ -72,7 +76,7 @@ export function ensureBlock() { /** * Keeping this for backward-compatibility. You should use arrowFunctionToExpression() for >=7.x. */ -export function arrowFunctionToShadowed() { +export function arrowFunctionToShadowed(this: NodePath) { if (!this.isArrowFunctionExpression()) return; this.arrowFunctionToExpression(); @@ -84,7 +88,7 @@ export function arrowFunctionToShadowed() { * you have wrapped some set of items in an IIFE or other function, but want "this", "arguments", and super" * to continue behaving as expected. */ -export function unwrapFunctionEnvironment() { +export function unwrapFunctionEnvironment(this: NodePath) { if ( !this.isArrowFunctionExpression() && !this.isFunctionExpression() && @@ -101,10 +105,10 @@ export function unwrapFunctionEnvironment() { /** * Convert a given arrow function into a normal ES5 function expression. */ -export function arrowFunctionToExpression({ - allowInsertArrow = true, - specCompliant = false, -} = {}) { +export function arrowFunctionToExpression( + this: NodePath, + { allowInsertArrow = true, specCompliant = false } = {}, +) { if (!this.isArrowFunctionExpression()) { throw this.buildCodeFrameError( "Cannot convert non-arrow function to a function expression.", @@ -118,6 +122,7 @@ export function arrowFunctionToExpression({ ); this.ensureBlock(); + // @ts-expect-error todo(flow->ts): avoid mutating nodes this.node.type = "FunctionExpression"; if (specCompliant) { const checkBinding = thisBinding @@ -392,7 +397,7 @@ function standardizeSuperProperty(superProp) { ? superProp.scope.generateDeclaredUidIdentifier("prop") : null; - const parts = [ + const parts: t.Expression[] = [ t.assignmentExpression( "=", tmp, diff --git a/packages/babel-traverse/src/path/evaluation.js b/packages/babel-traverse/src/path/evaluation.ts similarity index 77% rename from packages/babel-traverse/src/path/evaluation.js rename to packages/babel-traverse/src/path/evaluation.ts index ca6d6dc50d..163e11d993 100644 --- a/packages/babel-traverse/src/path/evaluation.js +++ b/packages/babel-traverse/src/path/evaluation.ts @@ -23,7 +23,7 @@ const INVALID_METHODS = ["random"]; * */ -export function evaluateTruthy(): boolean { +export function evaluateTruthy(this: NodePath): boolean { const res = this.evaluate(); if (res.confident) return !!res.value; } @@ -45,7 +45,7 @@ function deopt(path, state) { * var g = a ? 1 : 2, * a = g * this.foo */ -function evaluateCached(path, state) { +function evaluateCached(path: NodePath, state) { const { node } = path; const { seen } = state; @@ -58,7 +58,8 @@ function evaluateCached(path, state) { return; } } else { - const item = { resolved: false }; + // todo: create type annotation for state instead + const item: { resolved: boolean; value?: any } = { resolved: false }; seen.set(node, item); const val = _evaluate(path, state); @@ -70,11 +71,9 @@ function evaluateCached(path, state) { } } -function _evaluate(path, state) { +function _evaluate(path: NodePath, state) { if (!state.confident) return; - const { node } = path; - if (path.isSequenceExpression()) { const exprs = path.get("expressions"); return evaluateCached(exprs[exprs.length - 1], state); @@ -85,7 +84,7 @@ function _evaluate(path, state) { path.isNumericLiteral() || path.isBooleanLiteral() ) { - return node.value; + return path.node.value; } if (path.isNullLiteral()) { @@ -93,27 +92,30 @@ function _evaluate(path, state) { } if (path.isTemplateLiteral()) { - return evaluateQuasis(path, node.quasis, state); + return evaluateQuasis(path, path.node.quasis, state); } if ( path.isTaggedTemplateExpression() && path.get("tag").isMemberExpression() ) { - const object = path.get("tag.object"); + const object = path.get("tag.object") as NodePath; const { + // @ts-expect-error todo(flow->ts): possible bug, object is can be any expression and so name might be undefined node: { name }, } = object; - const property = path.get("tag.property"); + const property = path.get("tag.property") as NodePath; if ( object.isIdentifier() && name === "String" && - !path.scope.getBinding(name, true) && - property.isIdentifier && + // todo(flow->ts): was changed from getBinding(name, true) + // should this be hasBinding(name, true) as the binding is never used later? + !path.scope.getBinding(name) && + property.isIdentifier() && property.node.name === "raw" ) { - return evaluateQuasis(path, node.quasi.quasis, state, true); + return evaluateQuasis(path, path.node.quasi.quasis, state, true); } } @@ -135,12 +137,13 @@ function _evaluate(path, state) { // "foo".length if ( path.isMemberExpression() && - !path.parentPath.isCallExpression({ callee: node }) + !path.parentPath.isCallExpression({ callee: path.node }) ) { - const property = path.get("property"); - const object = path.get("object"); + const property = path.get("property") as NodePath; + const object = path.get("object") as NodePath; if (object.isLiteral() && property.isIdentifier()) { + // @ts-expect-error todo(flow->ts): instead of typeof - would it be better to check type of ast node? const value = object.node.value; const type = typeof value; if (type === "number" || type === "string") { @@ -150,7 +153,8 @@ function _evaluate(path, state) { } if (path.isReferencedIdentifier()) { - const binding = path.scope.getBinding(node.name); + // @ts-expect-error todo(flow->ts): consider separating type refinement and check for reference + const binding = path.scope.getBinding(path.node.name); if (binding && binding.constantViolations.length > 0) { return deopt(binding.path, state); @@ -163,11 +167,14 @@ function _evaluate(path, state) { if (binding?.hasValue) { return binding.value; } else { - if (node.name === "undefined") { + // @ts-expect-error todo(flow->ts): consider separating type refinement and check for reference + if (path.node.name === "undefined") { return binding ? deopt(binding.path, state) : undefined; - } else if (node.name === "Infinity") { + // @ts-expect-error todo(flow->ts): consider separating type refinement and check for reference + } else if (path.node.name === "Infinity") { return binding ? deopt(binding.path, state) : Infinity; - } else if (node.name === "NaN") { + // @ts-expect-error todo(flow->ts): consider separating type refinement and check for reference + } else if (path.node.name === "NaN") { return binding ? deopt(binding.path, state) : NaN; } @@ -181,14 +188,14 @@ function _evaluate(path, state) { } if (path.isUnaryExpression({ prefix: true })) { - if (node.operator === "void") { + if (path.node.operator === "void") { // we don't need to evaluate the argument to know what this will return return undefined; } const argument = path.get("argument"); if ( - node.operator === "typeof" && + path.node.operator === "typeof" && (argument.isFunction() || argument.isClass()) ) { return "function"; @@ -196,7 +203,7 @@ function _evaluate(path, state) { const arg = evaluateCached(argument, state); if (!state.confident) return; - switch (node.operator) { + switch (path.node.operator) { case "!": return !arg; case "+": @@ -227,13 +234,14 @@ function _evaluate(path, state) { if (path.isObjectExpression()) { const obj = {}; - const props: Array = path.get("properties"); + const props = path.get("properties"); for (const prop of props) { if (prop.isObjectMethod() || prop.isSpreadElement()) { return deopt(prop, state); } - const keyPath = prop.get("key"); + const keyPath: any = prop.get("key"); let key = keyPath; + // @ts-expect-error todo(flow->ts): type refinement issues ObjectMethod and SpreadElement somehow not excluded if (prop.node.computed) { key = key.evaluate(); if (!key.confident) { @@ -245,7 +253,8 @@ function _evaluate(path, state) { } else { key = key.node.value; } - const valuePath = prop.get("value"); + // todo(flow->ts): remove typecast + const valuePath = prop.get("value") as NodePath; let value = valuePath.evaluate(); if (!value.confident) { return deopt(value.deopt, state); @@ -266,7 +275,7 @@ function _evaluate(path, state) { const right = evaluateCached(path.get("right"), state); const rightConfident = state.confident; - switch (node.operator) { + switch (path.node.operator) { case "||": // TODO consider having a "truthy type" that doesn't bail on // left uncertainty but can still evaluate to truthy. @@ -288,7 +297,7 @@ function _evaluate(path, state) { const right = evaluateCached(path.get("right"), state); if (!state.confident) return; - switch (node.operator) { + switch (path.node.operator) { case "-": return left - right; case "+": @@ -340,15 +349,16 @@ function _evaluate(path, state) { // Number(1); if ( callee.isIdentifier() && - !path.scope.getBinding(callee.node.name, true) && + !path.scope.getBinding(callee.node.name) && VALID_CALLEES.indexOf(callee.node.name) >= 0 ) { - func = global[node.callee.name]; + func = global[callee.node.name]; } if (callee.isMemberExpression()) { const object = callee.get("object"); - const property = callee.get("property"); + // todo: improve babel-types + const property = callee.get("property") as NodePath; // Math.min(1, 2) if ( @@ -363,8 +373,10 @@ function _evaluate(path, state) { // "abc".charCodeAt(4) if (object.isLiteral() && property.isIdentifier()) { + // @ts-expect-error todo(flow->ts): consider checking ast node type instead of value type (StringLiteral and NumberLiteral) const type = typeof object.node.value; if (type === "string" || type === "number") { + // @ts-expect-error todo(flow->ts): consider checking ast node type instead of value type context = object.node.value; func = context[property.node.name]; } @@ -382,7 +394,7 @@ function _evaluate(path, state) { deopt(path, state); } -function evaluateQuasis(path, quasis: Array, state, raw = false) { +function evaluateQuasis(path, quasis: Array, state, raw = false) { let str = ""; let i = 0; @@ -420,10 +432,12 @@ function evaluateQuasis(path, quasis: Array, state, raw = false) { * */ -export function evaluate(): { - confident: boolean, - value: any, - deopt?: NodePath, +export function evaluate( + this: NodePath, +): { + confident: boolean; + value: any; + deopt?: NodePath; } { const state = { confident: true, diff --git a/packages/babel-traverse/src/path/family.js b/packages/babel-traverse/src/path/family.ts similarity index 71% rename from packages/babel-traverse/src/path/family.js rename to packages/babel-traverse/src/path/family.ts index 19479009e9..13a47e4d5a 100644 --- a/packages/babel-traverse/src/path/family.js +++ b/packages/babel-traverse/src/path/family.ts @@ -1,24 +1,27 @@ -// @flow // This file contains methods responsible for dealing with/retrieving children or siblings. -import type TraversalContext from "../index"; +import type TraversalContext from "../context"; import NodePath from "./index"; import * as t from "@babel/types"; -export function getOpposite(): ?NodePath { +export function getOpposite(this: NodePath): NodePath | null { if (this.key === "left") { return this.getSibling("right"); } else if (this.key === "right") { return this.getSibling("left"); } + return null; } -function addCompletionRecords(path, paths) { +function addCompletionRecords( + path: NodePath | null | undefined, + paths: NodePath[], +): NodePath[] { if (path) return paths.concat(path.getCompletionRecords()); return paths; } -function findBreak(statements): ?NodePath { +function findBreak(statements): NodePath | null { let breakStatement; if (!Array.isArray(statements)) { statements = [statements]; @@ -94,15 +97,17 @@ function completionRecordForSwitch(cases, paths) { return paths; } -export function getCompletionRecords(): NodePath[] { +export function getCompletionRecords(this: NodePath): NodePath[] { let paths = []; if (this.isIfStatement()) { paths = addCompletionRecords(this.get("consequent"), paths); paths = addCompletionRecords(this.get("alternate"), paths); } else if (this.isDoExpression() || this.isFor() || this.isWhile()) { + // @ts-expect-error(flow->ts): todo paths = addCompletionRecords(this.get("body"), paths); } else if (this.isProgram() || this.isBlockStatement()) { + // @ts-expect-error(flow->ts): todo paths = addCompletionRecords(this.get("body").pop(), paths); } else if (this.isFunction()) { return this.get("body").getCompletionRecords(); @@ -120,7 +125,7 @@ export function getCompletionRecords(): NodePath[] { return paths; } -export function getSibling(key: string): NodePath { +export function getSibling(this: NodePath, key: string | number): NodePath { return NodePath.get({ parentPath: this.parentPath, parent: this.parent, @@ -130,16 +135,19 @@ export function getSibling(key: string): NodePath { }).setContext(this.context); } -export function getPrevSibling(): NodePath { +export function getPrevSibling(this: NodePath): NodePath { + // @ts-expect-error todo(flow->ts) this.key could be a string return this.getSibling(this.key - 1); } -export function getNextSibling(): NodePath { +export function getNextSibling(this: NodePath): NodePath { + // @ts-expect-error todo(flow->ts) this.key could be a string return this.getSibling(this.key + 1); } -export function getAllNextSiblings(): NodePath[] { - let _key = this.key; +export function getAllNextSiblings(this: NodePath): NodePath[] { + // @ts-expect-error todo(flow->ts) this.key could be a string + let _key: number = this.key; let sibling = this.getSibling(++_key); const siblings = []; while (sibling.node) { @@ -149,8 +157,9 @@ export function getAllNextSiblings(): NodePath[] { return siblings; } -export function getAllPrevSiblings(): NodePath[] { - let _key = this.key; +export function getAllPrevSiblings(this: NodePath): NodePath[] { + // @ts-expect-error todo(flow->ts) this.key could be a string + let _key: number = this.key; let sibling = this.getSibling(--_key); const siblings = []; while (sibling.node) { @@ -160,9 +169,26 @@ export function getAllPrevSiblings(): NodePath[] { return siblings; } -export function get( +function get( + this: NodePath, + key: K, + context?: boolean | TraversalContext, +): T[K] extends Array + ? Array> + : T[K] extends t.Node | null | undefined + ? NodePath + : never; + +function get( + this: NodePath, key: string, - context?: boolean | TraversalContext = true, + context?: true | TraversalContext, +): NodePath | NodePath[]; + +function get( + this: NodePath, + key: string, + context: true | TraversalContext = true, ): NodePath | NodePath[] { if (context === true) context = this.context; const parts = key.split("."); @@ -175,7 +201,10 @@ export function get( } } -export function _getKey( +export { get }; + +export function _getKey( + this: NodePath, key: string, context?: TraversalContext, ): NodePath | NodePath[] { @@ -204,12 +233,14 @@ export function _getKey( } export function _getPattern( + this: NodePath, parts: string[], context?: TraversalContext, ): NodePath | NodePath[] { - let path = this; + let path: NodePath | NodePath[] = this; for (const part of parts) { if (part === ".") { + // @ts-expect-error todo(flow-ts): Can path be an array here? path = path.parentPath; } else { if (Array.isArray(path)) { @@ -222,21 +253,52 @@ export function _getPattern( return path; } -export function getBindingIdentifiers(duplicates?: boolean): Object { +function getBindingIdentifiers( + duplicates: true, +): Record; +function getBindingIdentifiers( + duplicates?: false, +): Record; +function getBindingIdentifiers( + duplicates: boolean, +): Record; + +function getBindingIdentifiers( + duplicates?: boolean, +): Record { return t.getBindingIdentifiers(this.node, duplicates); } -export function getOuterBindingIdentifiers(duplicates?: boolean): Object { +export { getBindingIdentifiers }; + +function getOuterBindingIdentifiers( + duplicates: true, +): Record; +function getOuterBindingIdentifiers( + duplicates?: false, +): Record; +function getOuterBindingIdentifiers( + duplicates: boolean, +): Record; + +function getOuterBindingIdentifiers( + duplicates?: boolean, +): Record { return t.getOuterBindingIdentifiers(this.node, duplicates); } +export { getOuterBindingIdentifiers }; + // original source - https://github.com/babel/babel/blob/main/packages/babel-types/src/retrievers/getBindingIdentifiers.js // path.getBindingIdentifiers returns nodes where the following re-implementation // returns paths export function getBindingIdentifierPaths( - duplicates?: boolean = false, - outerOnly?: boolean = false, -): { [string]: NodePath } { + this: NodePath, + duplicates: boolean = false, + outerOnly: boolean = false, +): { + [x: string]: NodePath; +} { const path = this; let search = [].concat(path); const ids = Object.create(null); @@ -292,7 +354,10 @@ export function getBindingIdentifierPaths( } export function getOuterBindingIdentifierPaths( + this: NodePath, duplicates?: boolean, -): { [string]: NodePath } { +): { + [x: string]: NodePath; +} { return this.getBindingIdentifierPaths(duplicates, true); } diff --git a/packages/babel-traverse/src/path/generated/asserts.ts b/packages/babel-traverse/src/path/generated/asserts.ts new file mode 100755 index 0000000000..cf912c0d63 --- /dev/null +++ b/packages/babel-traverse/src/path/generated/asserts.ts @@ -0,0 +1,688 @@ +/* + * 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"; + +export interface NodePathAssetions { + assertAnyTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertArgumentPlaceholder( + opts?: object, + ): asserts this is NodePath; + assertArrayExpression( + opts?: object, + ): asserts this is NodePath; + assertArrayPattern(opts?: object): asserts this is NodePath; + assertArrayTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertArrowFunctionExpression( + opts?: object, + ): asserts this is NodePath; + assertAssignmentExpression( + opts?: object, + ): asserts this is NodePath; + assertAssignmentPattern( + opts?: object, + ): asserts this is NodePath; + assertAwaitExpression( + opts?: object, + ): asserts this is NodePath; + assertBigIntLiteral(opts?: object): asserts this is NodePath; + assertBinary(opts?: object): asserts this is NodePath; + assertBinaryExpression( + opts?: object, + ): asserts this is NodePath; + assertBindExpression( + opts?: object, + ): asserts this is NodePath; + assertBlock(opts?: object): asserts this is NodePath; + assertBlockParent(opts?: object): asserts this is NodePath; + assertBlockStatement( + opts?: object, + ): asserts this is NodePath; + assertBooleanLiteral( + opts?: object, + ): asserts this is NodePath; + assertBooleanLiteralTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertBooleanTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertBreakStatement( + opts?: object, + ): asserts this is NodePath; + assertCallExpression( + opts?: object, + ): asserts this is NodePath; + assertCatchClause(opts?: object): asserts this is NodePath; + assertClass(opts?: object): asserts this is NodePath; + assertClassBody(opts?: object): asserts this is NodePath; + assertClassDeclaration( + opts?: object, + ): asserts this is NodePath; + assertClassExpression( + opts?: object, + ): asserts this is NodePath; + assertClassImplements( + opts?: object, + ): asserts this is NodePath; + assertClassMethod(opts?: object): asserts this is NodePath; + assertClassPrivateMethod( + opts?: object, + ): asserts this is NodePath; + assertClassPrivateProperty( + opts?: object, + ): asserts this is NodePath; + assertClassProperty(opts?: object): asserts this is NodePath; + assertCompletionStatement( + opts?: object, + ): asserts this is NodePath; + assertConditional(opts?: object): asserts this is NodePath; + assertConditionalExpression( + opts?: object, + ): asserts this is NodePath; + assertContinueStatement( + opts?: object, + ): asserts this is NodePath; + assertDebuggerStatement( + opts?: object, + ): asserts this is NodePath; + assertDecimalLiteral( + opts?: object, + ): asserts this is NodePath; + assertDeclaration(opts?: object): asserts this is NodePath; + assertDeclareClass(opts?: object): asserts this is NodePath; + assertDeclareExportAllDeclaration( + opts?: object, + ): asserts this is NodePath; + assertDeclareExportDeclaration( + opts?: object, + ): asserts this is NodePath; + assertDeclareFunction( + opts?: object, + ): asserts this is NodePath; + assertDeclareInterface( + opts?: object, + ): asserts this is NodePath; + assertDeclareModule(opts?: object): asserts this is NodePath; + assertDeclareModuleExports( + opts?: object, + ): asserts this is NodePath; + assertDeclareOpaqueType( + opts?: object, + ): asserts this is NodePath; + assertDeclareTypeAlias( + opts?: object, + ): asserts this is NodePath; + assertDeclareVariable( + opts?: object, + ): asserts this is NodePath; + assertDeclaredPredicate( + opts?: object, + ): asserts this is NodePath; + assertDecorator(opts?: object): asserts this is NodePath; + assertDirective(opts?: object): asserts this is NodePath; + assertDirectiveLiteral( + opts?: object, + ): asserts this is NodePath; + assertDoExpression(opts?: object): asserts this is NodePath; + assertDoWhileStatement( + opts?: object, + ): asserts this is NodePath; + assertEmptyStatement( + opts?: object, + ): asserts this is NodePath; + assertEmptyTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertEnumBody(opts?: object): asserts this is NodePath; + assertEnumBooleanBody( + opts?: object, + ): asserts this is NodePath; + assertEnumBooleanMember( + opts?: object, + ): asserts this is NodePath; + assertEnumDeclaration( + opts?: object, + ): asserts this is NodePath; + assertEnumDefaultedMember( + opts?: object, + ): asserts this is NodePath; + assertEnumMember(opts?: object): asserts this is NodePath; + assertEnumNumberBody( + opts?: object, + ): asserts this is NodePath; + assertEnumNumberMember( + opts?: object, + ): asserts this is NodePath; + assertEnumStringBody( + opts?: object, + ): asserts this is NodePath; + assertEnumStringMember( + opts?: object, + ): asserts this is NodePath; + assertEnumSymbolBody( + opts?: object, + ): asserts this is NodePath; + assertExistsTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertExportAllDeclaration( + opts?: object, + ): asserts this is NodePath; + assertExportDeclaration( + opts?: object, + ): asserts this is NodePath; + assertExportDefaultDeclaration( + opts?: object, + ): asserts this is NodePath; + assertExportDefaultSpecifier( + opts?: object, + ): asserts this is NodePath; + assertExportNamedDeclaration( + opts?: object, + ): asserts this is NodePath; + assertExportNamespaceSpecifier( + opts?: object, + ): asserts this is NodePath; + assertExportSpecifier( + opts?: object, + ): asserts this is NodePath; + assertExpression(opts?: object): asserts this is NodePath; + assertExpressionStatement( + opts?: object, + ): asserts this is NodePath; + assertExpressionWrapper( + opts?: object, + ): asserts this is NodePath; + assertFile(opts?: object): asserts this is NodePath; + assertFlow(opts?: object): asserts this is NodePath; + assertFlowBaseAnnotation( + opts?: object, + ): asserts this is NodePath; + assertFlowDeclaration( + opts?: object, + ): asserts this is NodePath; + assertFlowPredicate(opts?: object): asserts this is NodePath; + assertFlowType(opts?: object): asserts this is NodePath; + assertFor(opts?: object): asserts this is NodePath; + assertForInStatement( + opts?: object, + ): asserts this is NodePath; + assertForOfStatement( + opts?: object, + ): asserts this is NodePath; + assertForStatement(opts?: object): asserts this is NodePath; + assertForXStatement(opts?: object): asserts this is NodePath; + assertFunction(opts?: object): asserts this is NodePath; + assertFunctionDeclaration( + opts?: object, + ): asserts this is NodePath; + assertFunctionExpression( + opts?: object, + ): asserts this is NodePath; + assertFunctionParent( + opts?: object, + ): asserts this is NodePath; + assertFunctionTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertFunctionTypeParam( + opts?: object, + ): asserts this is NodePath; + assertGenericTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertIdentifier(opts?: object): asserts this is NodePath; + assertIfStatement(opts?: object): asserts this is NodePath; + assertImmutable(opts?: object): asserts this is NodePath; + assertImport(opts?: object): asserts this is NodePath; + assertImportAttribute( + opts?: object, + ): asserts this is NodePath; + assertImportDeclaration( + opts?: object, + ): asserts this is NodePath; + assertImportDefaultSpecifier( + opts?: object, + ): asserts this is NodePath; + assertImportNamespaceSpecifier( + opts?: object, + ): asserts this is NodePath; + assertImportSpecifier( + opts?: object, + ): asserts this is NodePath; + assertInferredPredicate( + opts?: object, + ): asserts this is NodePath; + assertInterfaceDeclaration( + opts?: object, + ): asserts this is NodePath; + assertInterfaceExtends( + opts?: object, + ): asserts this is NodePath; + assertInterfaceTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertInterpreterDirective( + opts?: object, + ): asserts this is NodePath; + assertIntersectionTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertJSX(opts?: object): asserts this is NodePath; + assertJSXAttribute(opts?: object): asserts this is NodePath; + assertJSXClosingElement( + opts?: object, + ): asserts this is NodePath; + assertJSXClosingFragment( + opts?: object, + ): asserts this is NodePath; + assertJSXElement(opts?: object): asserts this is NodePath; + assertJSXEmptyExpression( + opts?: object, + ): asserts this is NodePath; + assertJSXExpressionContainer( + opts?: object, + ): asserts this is NodePath; + assertJSXFragment(opts?: object): asserts this is NodePath; + assertJSXIdentifier(opts?: object): asserts this is NodePath; + assertJSXMemberExpression( + opts?: object, + ): asserts this is NodePath; + assertJSXNamespacedName( + opts?: object, + ): asserts this is NodePath; + assertJSXOpeningElement( + opts?: object, + ): asserts this is NodePath; + assertJSXOpeningFragment( + opts?: object, + ): asserts this is NodePath; + assertJSXSpreadAttribute( + opts?: object, + ): asserts this is NodePath; + assertJSXSpreadChild( + opts?: object, + ): asserts this is NodePath; + assertJSXText(opts?: object): asserts this is NodePath; + assertLVal(opts?: object): asserts this is NodePath; + assertLabeledStatement( + opts?: object, + ): asserts this is NodePath; + assertLiteral(opts?: object): asserts this is NodePath; + assertLogicalExpression( + opts?: object, + ): asserts this is NodePath; + assertLoop(opts?: object): asserts this is NodePath; + assertMemberExpression( + opts?: object, + ): asserts this is NodePath; + assertMetaProperty(opts?: object): asserts this is NodePath; + assertMethod(opts?: object): asserts this is NodePath; + assertMixedTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertModuleDeclaration( + opts?: object, + ): asserts this is NodePath; + assertModuleSpecifier( + opts?: object, + ): asserts this is NodePath; + assertNewExpression(opts?: object): asserts this is NodePath; + assertNoop(opts?: object): asserts this is NodePath; + assertNullLiteral(opts?: object): asserts this is NodePath; + assertNullLiteralTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertNullableTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertNumberLiteral(opts?: object): asserts this is NodePath; + assertNumberLiteralTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertNumberTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertNumericLiteral( + opts?: object, + ): asserts this is NodePath; + assertObjectExpression( + opts?: object, + ): asserts this is NodePath; + assertObjectMember(opts?: object): asserts this is NodePath; + assertObjectMethod(opts?: object): asserts this is NodePath; + assertObjectPattern(opts?: object): asserts this is NodePath; + assertObjectProperty( + opts?: object, + ): asserts this is NodePath; + assertObjectTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertObjectTypeCallProperty( + opts?: object, + ): asserts this is NodePath; + assertObjectTypeIndexer( + opts?: object, + ): asserts this is NodePath; + assertObjectTypeInternalSlot( + opts?: object, + ): asserts this is NodePath; + assertObjectTypeProperty( + opts?: object, + ): asserts this is NodePath; + assertObjectTypeSpreadProperty( + opts?: object, + ): asserts this is NodePath; + assertOpaqueType(opts?: object): asserts this is NodePath; + assertOptionalCallExpression( + opts?: object, + ): asserts this is NodePath; + assertOptionalMemberExpression( + opts?: object, + ): asserts this is NodePath; + assertParenthesizedExpression( + opts?: object, + ): asserts this is NodePath; + assertPattern(opts?: object): asserts this is NodePath; + assertPatternLike(opts?: object): asserts this is NodePath; + assertPipelineBareFunction( + opts?: object, + ): asserts this is NodePath; + assertPipelinePrimaryTopicReference( + opts?: object, + ): asserts this is NodePath; + assertPipelineTopicExpression( + opts?: object, + ): asserts this is NodePath; + assertPlaceholder(opts?: object): asserts this is NodePath; + assertPrivate(opts?: object): asserts this is NodePath; + assertPrivateName(opts?: object): asserts this is NodePath; + assertProgram(opts?: object): asserts this is NodePath; + assertProperty(opts?: object): asserts this is NodePath; + assertPureish(opts?: object): asserts this is NodePath; + assertQualifiedTypeIdentifier( + opts?: object, + ): asserts this is NodePath; + assertRecordExpression( + opts?: object, + ): asserts this is NodePath; + assertRegExpLiteral(opts?: object): asserts this is NodePath; + assertRegexLiteral(opts?: object): asserts this is NodePath; + assertRestElement(opts?: object): asserts this is NodePath; + assertRestProperty(opts?: object): asserts this is NodePath; + assertReturnStatement( + opts?: object, + ): asserts this is NodePath; + assertScopable(opts?: object): asserts this is NodePath; + assertSequenceExpression( + opts?: object, + ): asserts this is NodePath; + assertSpreadElement(opts?: object): asserts this is NodePath; + assertSpreadProperty( + opts?: object, + ): asserts this is NodePath; + assertStatement(opts?: object): asserts this is NodePath; + assertStaticBlock(opts?: object): asserts this is NodePath; + assertStringLiteral(opts?: object): asserts this is NodePath; + assertStringLiteralTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertStringTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertSuper(opts?: object): asserts this is NodePath; + assertSwitchCase(opts?: object): asserts this is NodePath; + assertSwitchStatement( + opts?: object, + ): asserts this is NodePath; + assertSymbolTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertTSAnyKeyword(opts?: object): asserts this is NodePath; + assertTSArrayType(opts?: object): asserts this is NodePath; + assertTSAsExpression( + opts?: object, + ): asserts this is NodePath; + assertTSBaseType(opts?: object): asserts this is NodePath; + assertTSBigIntKeyword( + opts?: object, + ): asserts this is NodePath; + assertTSBooleanKeyword( + opts?: object, + ): asserts this is NodePath; + assertTSCallSignatureDeclaration( + opts?: object, + ): asserts this is NodePath; + assertTSConditionalType( + opts?: object, + ): asserts this is NodePath; + assertTSConstructSignatureDeclaration( + opts?: object, + ): asserts this is NodePath; + assertTSConstructorType( + opts?: object, + ): asserts this is NodePath; + assertTSDeclareFunction( + opts?: object, + ): asserts this is NodePath; + assertTSDeclareMethod( + opts?: object, + ): asserts this is NodePath; + assertTSEntityName(opts?: object): asserts this is NodePath; + assertTSEnumDeclaration( + opts?: object, + ): asserts this is NodePath; + assertTSEnumMember(opts?: object): asserts this is NodePath; + assertTSExportAssignment( + opts?: object, + ): asserts this is NodePath; + assertTSExpressionWithTypeArguments( + opts?: object, + ): asserts this is NodePath; + assertTSExternalModuleReference( + opts?: object, + ): asserts this is NodePath; + assertTSFunctionType( + opts?: object, + ): asserts this is NodePath; + assertTSImportEqualsDeclaration( + opts?: object, + ): asserts this is NodePath; + assertTSImportType(opts?: object): asserts this is NodePath; + assertTSIndexSignature( + opts?: object, + ): asserts this is NodePath; + assertTSIndexedAccessType( + opts?: object, + ): asserts this is NodePath; + assertTSInferType(opts?: object): asserts this is NodePath; + assertTSInterfaceBody( + opts?: object, + ): asserts this is NodePath; + assertTSInterfaceDeclaration( + opts?: object, + ): asserts this is NodePath; + assertTSIntersectionType( + opts?: object, + ): asserts this is NodePath; + assertTSIntrinsicKeyword( + opts?: object, + ): asserts this is NodePath; + assertTSLiteralType(opts?: object): asserts this is NodePath; + assertTSMappedType(opts?: object): asserts this is NodePath; + assertTSMethodSignature( + opts?: object, + ): asserts this is NodePath; + assertTSModuleBlock(opts?: object): asserts this is NodePath; + assertTSModuleDeclaration( + opts?: object, + ): asserts this is NodePath; + assertTSNamedTupleMember( + opts?: object, + ): asserts this is NodePath; + assertTSNamespaceExportDeclaration( + opts?: object, + ): asserts this is NodePath; + assertTSNeverKeyword( + opts?: object, + ): asserts this is NodePath; + assertTSNonNullExpression( + opts?: object, + ): asserts this is NodePath; + assertTSNullKeyword(opts?: object): asserts this is NodePath; + assertTSNumberKeyword( + opts?: object, + ): asserts this is NodePath; + assertTSObjectKeyword( + opts?: object, + ): asserts this is NodePath; + assertTSOptionalType( + opts?: object, + ): asserts this is NodePath; + assertTSParameterProperty( + opts?: object, + ): asserts this is NodePath; + assertTSParenthesizedType( + opts?: object, + ): asserts this is NodePath; + assertTSPropertySignature( + opts?: object, + ): asserts this is NodePath; + assertTSQualifiedName( + opts?: object, + ): asserts this is NodePath; + assertTSRestType(opts?: object): asserts this is NodePath; + assertTSStringKeyword( + opts?: object, + ): asserts this is NodePath; + assertTSSymbolKeyword( + opts?: object, + ): asserts this is NodePath; + assertTSThisType(opts?: object): asserts this is NodePath; + assertTSTupleType(opts?: object): asserts this is NodePath; + assertTSType(opts?: object): asserts this is NodePath; + assertTSTypeAliasDeclaration( + opts?: object, + ): asserts this is NodePath; + assertTSTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertTSTypeAssertion( + opts?: object, + ): asserts this is NodePath; + assertTSTypeElement(opts?: object): asserts this is NodePath; + assertTSTypeLiteral(opts?: object): asserts this is NodePath; + assertTSTypeOperator( + opts?: object, + ): asserts this is NodePath; + assertTSTypeParameter( + opts?: object, + ): asserts this is NodePath; + assertTSTypeParameterDeclaration( + opts?: object, + ): asserts this is NodePath; + assertTSTypeParameterInstantiation( + opts?: object, + ): asserts this is NodePath; + assertTSTypePredicate( + opts?: object, + ): asserts this is NodePath; + assertTSTypeQuery(opts?: object): asserts this is NodePath; + assertTSTypeReference( + opts?: object, + ): asserts this is NodePath; + assertTSUndefinedKeyword( + opts?: object, + ): asserts this is NodePath; + assertTSUnionType(opts?: object): asserts this is NodePath; + assertTSUnknownKeyword( + opts?: object, + ): asserts this is NodePath; + assertTSVoidKeyword(opts?: object): asserts this is NodePath; + assertTaggedTemplateExpression( + opts?: object, + ): asserts this is NodePath; + assertTemplateElement( + opts?: object, + ): asserts this is NodePath; + assertTemplateLiteral( + opts?: object, + ): asserts this is NodePath; + assertTerminatorless( + opts?: object, + ): asserts this is NodePath; + assertThisExpression( + opts?: object, + ): asserts this is NodePath; + assertThisTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertThrowStatement( + opts?: object, + ): asserts this is NodePath; + assertTryStatement(opts?: object): asserts this is NodePath; + assertTupleExpression( + opts?: object, + ): asserts this is NodePath; + assertTupleTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertTypeAlias(opts?: object): asserts this is NodePath; + assertTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertTypeCastExpression( + opts?: object, + ): asserts this is NodePath; + assertTypeParameter(opts?: object): asserts this is NodePath; + assertTypeParameterDeclaration( + opts?: object, + ): asserts this is NodePath; + assertTypeParameterInstantiation( + opts?: object, + ): asserts this is NodePath; + assertTypeofTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertUnaryExpression( + opts?: object, + ): asserts this is NodePath; + assertUnaryLike(opts?: object): asserts this is NodePath; + assertUnionTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertUpdateExpression( + opts?: object, + ): asserts this is NodePath; + assertUserWhitespacable( + opts?: object, + ): asserts this is NodePath; + assertV8IntrinsicIdentifier( + opts?: object, + ): asserts this is NodePath; + assertVariableDeclaration( + opts?: object, + ): asserts this is NodePath; + assertVariableDeclarator( + opts?: object, + ): asserts this is NodePath; + assertVariance(opts?: object): asserts this is NodePath; + assertVoidTypeAnnotation( + opts?: object, + ): asserts this is NodePath; + assertWhile(opts?: object): asserts this is NodePath; + assertWhileStatement( + opts?: object, + ): asserts this is NodePath; + assertWithStatement(opts?: object): asserts this is NodePath; + assertYieldExpression( + opts?: object, + ): asserts this is NodePath; +} diff --git a/packages/babel-traverse/src/path/generated/validators.ts b/packages/babel-traverse/src/path/generated/validators.ts new file mode 100755 index 0000000000..985a24d4e7 --- /dev/null +++ b/packages/babel-traverse/src/path/generated/validators.ts @@ -0,0 +1,432 @@ +/* + * 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"; + +export interface NodePathValidators { + isAnyTypeAnnotation(opts?: object): this is NodePath; + isArgumentPlaceholder(opts?: object): this is NodePath; + isArrayExpression(opts?: object): this is NodePath; + isArrayPattern(opts?: object): this is NodePath; + isArrayTypeAnnotation(opts?: object): this is NodePath; + isArrowFunctionExpression( + opts?: object, + ): this is NodePath; + isAssignmentExpression( + opts?: object, + ): this is NodePath; + isAssignmentPattern(opts?: object): this is NodePath; + isAwaitExpression(opts?: object): this is NodePath; + isBigIntLiteral(opts?: object): this is NodePath; + isBinary(opts?: object): this is NodePath; + isBinaryExpression(opts?: object): this is NodePath; + isBindExpression(opts?: object): this is NodePath; + isBlock(opts?: object): this is NodePath; + isBlockParent(opts?: object): this is NodePath; + isBlockStatement(opts?: object): this is NodePath; + isBooleanLiteral(opts?: object): this is NodePath; + isBooleanLiteralTypeAnnotation( + opts?: object, + ): this is NodePath; + isBooleanTypeAnnotation( + opts?: object, + ): this is NodePath; + isBreakStatement(opts?: object): this is NodePath; + isCallExpression(opts?: object): this is NodePath; + isCatchClause(opts?: object): this is NodePath; + isClass(opts?: object): this is NodePath; + isClassBody(opts?: object): this is NodePath; + isClassDeclaration(opts?: object): this is NodePath; + isClassExpression(opts?: object): this is NodePath; + isClassImplements(opts?: object): this is NodePath; + isClassMethod(opts?: object): this is NodePath; + isClassPrivateMethod(opts?: object): this is NodePath; + isClassPrivateProperty( + opts?: object, + ): this is NodePath; + isClassProperty(opts?: object): this is NodePath; + isCompletionStatement(opts?: object): this is NodePath; + isConditional(opts?: object): this is NodePath; + isConditionalExpression( + opts?: object, + ): this is NodePath; + isContinueStatement(opts?: object): this is NodePath; + isDebuggerStatement(opts?: object): this is NodePath; + isDecimalLiteral(opts?: object): this is NodePath; + isDeclaration(opts?: object): this is NodePath; + isDeclareClass(opts?: object): this is NodePath; + isDeclareExportAllDeclaration( + opts?: object, + ): this is NodePath; + isDeclareExportDeclaration( + opts?: object, + ): this is NodePath; + isDeclareFunction(opts?: object): this is NodePath; + isDeclareInterface(opts?: object): this is NodePath; + isDeclareModule(opts?: object): this is NodePath; + isDeclareModuleExports( + opts?: object, + ): this is NodePath; + isDeclareOpaqueType(opts?: object): this is NodePath; + isDeclareTypeAlias(opts?: object): this is NodePath; + isDeclareVariable(opts?: object): this is NodePath; + isDeclaredPredicate(opts?: object): this is NodePath; + isDecorator(opts?: object): this is NodePath; + isDirective(opts?: object): this is NodePath; + isDirectiveLiteral(opts?: object): this is NodePath; + isDoExpression(opts?: object): this is NodePath; + isDoWhileStatement(opts?: object): this is NodePath; + isEmptyStatement(opts?: object): this is NodePath; + isEmptyTypeAnnotation(opts?: object): this is NodePath; + isEnumBody(opts?: object): this is NodePath; + isEnumBooleanBody(opts?: object): this is NodePath; + isEnumBooleanMember(opts?: object): this is NodePath; + isEnumDeclaration(opts?: object): this is NodePath; + isEnumDefaultedMember(opts?: object): this is NodePath; + isEnumMember(opts?: object): this is NodePath; + isEnumNumberBody(opts?: object): this is NodePath; + isEnumNumberMember(opts?: object): this is NodePath; + isEnumStringBody(opts?: object): this is NodePath; + isEnumStringMember(opts?: object): this is NodePath; + isEnumSymbolBody(opts?: object): this is NodePath; + isExistsTypeAnnotation( + opts?: object, + ): this is NodePath; + isExportAllDeclaration( + opts?: object, + ): this is NodePath; + isExportDeclaration(opts?: object): this is NodePath; + isExportDefaultDeclaration( + opts?: object, + ): this is NodePath; + isExportDefaultSpecifier( + opts?: object, + ): this is NodePath; + isExportNamedDeclaration( + opts?: object, + ): this is NodePath; + isExportNamespaceSpecifier( + opts?: object, + ): this is NodePath; + isExportSpecifier(opts?: object): this is NodePath; + isExpression(opts?: object): this is NodePath; + isExpressionStatement(opts?: object): this is NodePath; + isExpressionWrapper(opts?: object): this is NodePath; + isFile(opts?: object): this is NodePath; + isFlow(opts?: object): this is NodePath; + isFlowBaseAnnotation(opts?: object): this is NodePath; + isFlowDeclaration(opts?: object): this is NodePath; + isFlowPredicate(opts?: object): this is NodePath; + isFlowType(opts?: object): this is NodePath; + isFor(opts?: object): this is NodePath; + isForInStatement(opts?: object): this is NodePath; + isForOfStatement(opts?: object): this is NodePath; + isForStatement(opts?: object): this is NodePath; + isForXStatement(opts?: object): this is NodePath; + isFunction(opts?: object): this is NodePath; + isFunctionDeclaration(opts?: object): this is NodePath; + isFunctionExpression(opts?: object): this is NodePath; + isFunctionParent(opts?: object): this is NodePath; + isFunctionTypeAnnotation( + opts?: object, + ): this is NodePath; + isFunctionTypeParam(opts?: object): this is NodePath; + isGenericTypeAnnotation( + opts?: object, + ): this is NodePath; + isIdentifier(opts?: object): this is NodePath; + isIfStatement(opts?: object): this is NodePath; + isImmutable(opts?: object): this is NodePath; + isImport(opts?: object): this is NodePath; + isImportAttribute(opts?: object): this is NodePath; + isImportDeclaration(opts?: object): this is NodePath; + isImportDefaultSpecifier( + opts?: object, + ): this is NodePath; + isImportNamespaceSpecifier( + opts?: object, + ): this is NodePath; + isImportSpecifier(opts?: object): this is NodePath; + isInferredPredicate(opts?: object): this is NodePath; + isInterfaceDeclaration( + opts?: object, + ): this is NodePath; + isInterfaceExtends(opts?: object): this is NodePath; + isInterfaceTypeAnnotation( + opts?: object, + ): this is NodePath; + isInterpreterDirective( + opts?: object, + ): this is NodePath; + isIntersectionTypeAnnotation( + opts?: object, + ): this is NodePath; + isJSX(opts?: object): this is NodePath; + isJSXAttribute(opts?: object): this is NodePath; + isJSXClosingElement(opts?: object): this is NodePath; + isJSXClosingFragment(opts?: object): this is NodePath; + isJSXElement(opts?: object): this is NodePath; + isJSXEmptyExpression(opts?: object): this is NodePath; + isJSXExpressionContainer( + opts?: object, + ): this is NodePath; + isJSXFragment(opts?: object): this is NodePath; + isJSXIdentifier(opts?: object): this is NodePath; + isJSXMemberExpression(opts?: object): this is NodePath; + isJSXNamespacedName(opts?: object): this is NodePath; + isJSXOpeningElement(opts?: object): this is NodePath; + isJSXOpeningFragment(opts?: object): this is NodePath; + isJSXSpreadAttribute(opts?: object): this is NodePath; + isJSXSpreadChild(opts?: object): this is NodePath; + isJSXText(opts?: object): this is NodePath; + isLVal(opts?: object): this is NodePath; + isLabeledStatement(opts?: object): this is NodePath; + isLiteral(opts?: object): this is NodePath; + isLogicalExpression(opts?: object): this is NodePath; + isLoop(opts?: object): this is NodePath; + isMemberExpression(opts?: object): this is NodePath; + isMetaProperty(opts?: object): this is NodePath; + isMethod(opts?: object): this is NodePath; + isMixedTypeAnnotation(opts?: object): this is NodePath; + isModuleDeclaration(opts?: object): this is NodePath; + isModuleSpecifier(opts?: object): this is NodePath; + isNewExpression(opts?: object): this is NodePath; + isNoop(opts?: object): this is NodePath; + isNullLiteral(opts?: object): this is NodePath; + isNullLiteralTypeAnnotation( + opts?: object, + ): this is NodePath; + isNullableTypeAnnotation( + opts?: object, + ): this is NodePath; + isNumberLiteral(opts?: object): this is NodePath; + isNumberLiteralTypeAnnotation( + opts?: object, + ): this is NodePath; + isNumberTypeAnnotation( + opts?: object, + ): this is NodePath; + isNumericLiteral(opts?: object): this is NodePath; + isObjectExpression(opts?: object): this is NodePath; + isObjectMember(opts?: object): this is NodePath; + isObjectMethod(opts?: object): this is NodePath; + isObjectPattern(opts?: object): this is NodePath; + isObjectProperty(opts?: object): this is NodePath; + isObjectTypeAnnotation( + opts?: object, + ): this is NodePath; + isObjectTypeCallProperty( + opts?: object, + ): this is NodePath; + isObjectTypeIndexer(opts?: object): this is NodePath; + isObjectTypeInternalSlot( + opts?: object, + ): this is NodePath; + isObjectTypeProperty(opts?: object): this is NodePath; + isObjectTypeSpreadProperty( + opts?: object, + ): this is NodePath; + isOpaqueType(opts?: object): this is NodePath; + isOptionalCallExpression( + opts?: object, + ): this is NodePath; + isOptionalMemberExpression( + opts?: object, + ): this is NodePath; + isParenthesizedExpression( + opts?: object, + ): this is NodePath; + isPattern(opts?: object): this is NodePath; + isPatternLike(opts?: object): this is NodePath; + isPipelineBareFunction( + opts?: object, + ): this is NodePath; + isPipelinePrimaryTopicReference( + opts?: object, + ): this is NodePath; + isPipelineTopicExpression( + opts?: object, + ): this is NodePath; + isPlaceholder(opts?: object): this is NodePath; + isPrivate(opts?: object): this is NodePath; + isPrivateName(opts?: object): this is NodePath; + isProgram(opts?: object): this is NodePath; + isProperty(opts?: object): this is NodePath; + isPureish(opts?: object): this is NodePath; + isQualifiedTypeIdentifier( + opts?: object, + ): this is NodePath; + isRecordExpression(opts?: object): this is NodePath; + isRegExpLiteral(opts?: object): this is NodePath; + isRegexLiteral(opts?: object): this is NodePath; + isRestElement(opts?: object): this is NodePath; + isRestProperty(opts?: object): this is NodePath; + isReturnStatement(opts?: object): this is NodePath; + isScopable(opts?: object): this is NodePath; + isSequenceExpression(opts?: object): this is NodePath; + isSpreadElement(opts?: object): this is NodePath; + isSpreadProperty(opts?: object): this is NodePath; + isStatement(opts?: object): this is NodePath; + isStaticBlock(opts?: object): this is NodePath; + isStringLiteral(opts?: object): this is NodePath; + isStringLiteralTypeAnnotation( + opts?: object, + ): this is NodePath; + isStringTypeAnnotation( + opts?: object, + ): this is NodePath; + isSuper(opts?: object): this is NodePath; + isSwitchCase(opts?: object): this is NodePath; + isSwitchStatement(opts?: object): this is NodePath; + isSymbolTypeAnnotation( + opts?: object, + ): this is NodePath; + isTSAnyKeyword(opts?: object): this is NodePath; + isTSArrayType(opts?: object): this is NodePath; + isTSAsExpression(opts?: object): this is NodePath; + isTSBaseType(opts?: object): this is NodePath; + isTSBigIntKeyword(opts?: object): this is NodePath; + isTSBooleanKeyword(opts?: object): this is NodePath; + isTSCallSignatureDeclaration( + opts?: object, + ): this is NodePath; + isTSConditionalType(opts?: object): this is NodePath; + isTSConstructSignatureDeclaration( + opts?: object, + ): this is NodePath; + isTSConstructorType(opts?: object): this is NodePath; + isTSDeclareFunction(opts?: object): this is NodePath; + isTSDeclareMethod(opts?: object): this is NodePath; + isTSEntityName(opts?: object): this is NodePath; + isTSEnumDeclaration(opts?: object): this is NodePath; + isTSEnumMember(opts?: object): this is NodePath; + isTSExportAssignment(opts?: object): this is NodePath; + isTSExpressionWithTypeArguments( + opts?: object, + ): this is NodePath; + isTSExternalModuleReference( + opts?: object, + ): this is NodePath; + isTSFunctionType(opts?: object): this is NodePath; + isTSImportEqualsDeclaration( + opts?: object, + ): this is NodePath; + isTSImportType(opts?: object): this is NodePath; + isTSIndexSignature(opts?: object): this is NodePath; + isTSIndexedAccessType(opts?: object): this is NodePath; + isTSInferType(opts?: object): this is NodePath; + isTSInterfaceBody(opts?: object): this is NodePath; + isTSInterfaceDeclaration( + opts?: object, + ): this is NodePath; + isTSIntersectionType(opts?: object): this is NodePath; + isTSIntrinsicKeyword(opts?: object): this is NodePath; + isTSLiteralType(opts?: object): this is NodePath; + isTSMappedType(opts?: object): this is NodePath; + isTSMethodSignature(opts?: object): this is NodePath; + isTSModuleBlock(opts?: object): this is NodePath; + isTSModuleDeclaration(opts?: object): this is NodePath; + isTSNamedTupleMember(opts?: object): this is NodePath; + isTSNamespaceExportDeclaration( + opts?: object, + ): this is NodePath; + isTSNeverKeyword(opts?: object): this is NodePath; + isTSNonNullExpression(opts?: object): this is NodePath; + isTSNullKeyword(opts?: object): this is NodePath; + isTSNumberKeyword(opts?: object): this is NodePath; + isTSObjectKeyword(opts?: object): this is NodePath; + isTSOptionalType(opts?: object): this is NodePath; + isTSParameterProperty(opts?: object): this is NodePath; + isTSParenthesizedType(opts?: object): this is NodePath; + isTSPropertySignature(opts?: object): this is NodePath; + isTSQualifiedName(opts?: object): this is NodePath; + isTSRestType(opts?: object): this is NodePath; + isTSStringKeyword(opts?: object): this is NodePath; + isTSSymbolKeyword(opts?: object): this is NodePath; + isTSThisType(opts?: object): this is NodePath; + isTSTupleType(opts?: object): this is NodePath; + isTSType(opts?: object): this is NodePath; + isTSTypeAliasDeclaration( + opts?: object, + ): this is NodePath; + isTSTypeAnnotation(opts?: object): this is NodePath; + isTSTypeAssertion(opts?: object): this is NodePath; + isTSTypeElement(opts?: object): this is NodePath; + isTSTypeLiteral(opts?: object): this is NodePath; + isTSTypeOperator(opts?: object): this is NodePath; + isTSTypeParameter(opts?: object): this is NodePath; + isTSTypeParameterDeclaration( + opts?: object, + ): this is NodePath; + isTSTypeParameterInstantiation( + opts?: object, + ): this is NodePath; + isTSTypePredicate(opts?: object): this is NodePath; + isTSTypeQuery(opts?: object): this is NodePath; + isTSTypeReference(opts?: object): this is NodePath; + isTSUndefinedKeyword(opts?: object): this is NodePath; + isTSUnionType(opts?: object): this is NodePath; + isTSUnknownKeyword(opts?: object): this is NodePath; + isTSVoidKeyword(opts?: object): this is NodePath; + isTaggedTemplateExpression( + opts?: object, + ): this is NodePath; + isTemplateElement(opts?: object): this is NodePath; + isTemplateLiteral(opts?: object): this is NodePath; + isTerminatorless(opts?: object): this is NodePath; + isThisExpression(opts?: object): this is NodePath; + isThisTypeAnnotation(opts?: object): this is NodePath; + isThrowStatement(opts?: object): this is NodePath; + isTryStatement(opts?: object): this is NodePath; + isTupleExpression(opts?: object): this is NodePath; + isTupleTypeAnnotation(opts?: object): this is NodePath; + isTypeAlias(opts?: object): this is NodePath; + isTypeAnnotation(opts?: object): this is NodePath; + isTypeCastExpression(opts?: object): this is NodePath; + isTypeParameter(opts?: object): this is NodePath; + isTypeParameterDeclaration( + opts?: object, + ): this is NodePath; + isTypeParameterInstantiation( + opts?: object, + ): this is NodePath; + isTypeofTypeAnnotation( + opts?: object, + ): this is NodePath; + isUnaryExpression(opts?: object): this is NodePath; + isUnaryLike(opts?: object): this is NodePath; + isUnionTypeAnnotation(opts?: object): this is NodePath; + isUpdateExpression(opts?: object): this is NodePath; + isUserWhitespacable(opts?: object): this is NodePath; + isV8IntrinsicIdentifier( + opts?: object, + ): this is NodePath; + isVariableDeclaration(opts?: object): this is NodePath; + isVariableDeclarator(opts?: object): this is NodePath; + isVariance(opts?: object): this is NodePath; + isVoidTypeAnnotation(opts?: object): this is NodePath; + isWhile(opts?: object): this is NodePath; + isWhileStatement(opts?: object): this is NodePath; + isWithStatement(opts?: object): this is NodePath; + isYieldExpression(opts?: object): this is NodePath; + isReferencedIdentifier(opts?: object): boolean; + isReferencedMemberExpression(opts?: object): boolean; + isBindingIdentifier(opts?: object): boolean; + isStatement(opts?: object): this is NodePath; + isExpression(opts?: object): this is NodePath; + isScope(opts?: object): boolean; + isReferenced(opts?: object): boolean; + isBlockScoped(opts?: object): boolean; + isVar(opts?: object): boolean; + isUser(opts?: object): boolean; + isGenerated(opts?: object): boolean; + isPure(opts?: object): boolean; + isFlow(opts?: object): this is NodePath; + isRestProperty(opts?: object): boolean; + isSpreadProperty(opts?: object): boolean; + isExistentialTypeParam(opts?: object): boolean; + isNumericLiteralTypeAnnotation(opts?: object): boolean; + isForAwaitStatement(opts?: object): boolean; +} diff --git a/packages/babel-traverse/src/path/generated/virtual-types.ts b/packages/babel-traverse/src/path/generated/virtual-types.ts new file mode 100755 index 0000000000..9ac07fc424 --- /dev/null +++ b/packages/babel-traverse/src/path/generated/virtual-types.ts @@ -0,0 +1,26 @@ +/* + * 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 { + 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; + SpreadProperty: t.RestElement; + ExistentialTypeParam: t.ExistsTypeAnnotation; + NumericLiteralTypeAnnotation: t.NumberLiteralTypeAnnotation; + ForAwaitStatement: t.ForOfStatement; +} diff --git a/packages/babel-traverse/src/path/index.js b/packages/babel-traverse/src/path/index.ts similarity index 75% rename from packages/babel-traverse/src/path/index.js rename to packages/babel-traverse/src/path/index.ts index fb13ec9bf4..c81e498dd3 100644 --- a/packages/babel-traverse/src/path/index.js +++ b/packages/babel-traverse/src/path/index.ts @@ -3,6 +3,7 @@ import type TraversalContext from "../context"; import * as virtualTypes from "./lib/virtual-types"; import buildDebug from "debug"; import traverse from "../index"; +import type { Visitor } from "../types"; import Scope from "../scope"; import * as t from "@babel/types"; import { path as pathCache } from "../cache"; @@ -20,6 +21,8 @@ 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 type { NodePathAssetions } from "./generated/asserts"; +import type { NodePathValidators } from "./generated/validators"; const debug = buildDebug("babel"); @@ -27,8 +30,8 @@ export const REMOVED = 1 << 0; export const SHOULD_STOP = 1 << 1; export const SHOULD_SKIP = 1 << 2; -export default class NodePath { - constructor(hub: HubInterface, parent: Object) { +class NodePath { + constructor(hub: HubInterface, parent: t.Node) { this.parent = parent; this.hub = hub; this.data = null; @@ -37,26 +40,40 @@ export default class NodePath { this.scope = null; } - declare parent: Object; + declare parent: t.Node; declare hub: HubInterface; - declare data: Object; + declare data: object; declare context: TraversalContext; declare scope: Scope; contexts: Array = []; state: any = null; - opts: ?Object = null; + opts: any = null; // this.shouldSkip = false; this.shouldStop = false; this.removed = false; _traverseFlags: number = 0; - skipKeys: ?Object = null; - parentPath: ?NodePath = null; - container: ?Object | Array = null; - listKey: ?string = null; - key: ?string = null; - node: ?Object = null; - type: ?string = null; + skipKeys: any = null; + parentPath: NodePath | null = null; + container: object | null | Array = null; + listKey: string | null = null; + key: string | number | null = null; + node: T = null; + type: string | null = null; - static get({ hub, parentPath, parent, container, listKey, key }): NodePath { + static get({ + hub, + parentPath, + parent, + container, + listKey, + key, + }: { + hub?; + parentPath; + parent; + container; + listKey?; + key; + }): NodePath { if (!hub && parentPath) { hub = parentPath.hub; } @@ -104,22 +121,27 @@ export default class NodePath { return val; } - buildCodeFrameError(msg: string, Error: typeof Error = SyntaxError): Error { + buildCodeFrameError( + msg: string, + Error: new () => Error = SyntaxError, + ): Error { return this.hub.buildError(this.node, msg, Error); } - traverse(visitor: Object, state?: any) { + traverse(visitor: Visitor, state: T): void; + traverse(visitor: Visitor): void; + traverse(visitor: any, state?: any) { traverse(this.node, visitor, this.scope, state, this); } - set(key: string, node: Object) { + set(key: string, node: any) { t.validate(this.node, key, node); this.node[key] = node; } getPathLocation(): string { const parts = []; - let path = this; + let path: NodePath = this; do { let key = path.key; if (path.inList) key = `${path.listKey}[${key}]`; @@ -203,7 +225,7 @@ Object.assign( NodePath_comments, ); -for (const type of (t.TYPES: Array)) { +for (const type of t.TYPES) { const typeKey = `is${type}`; const fn = t[typeKey]; NodePath.prototype[typeKey] = function (opts) { @@ -227,3 +249,23 @@ for (const type of Object.keys(virtualTypes)) { return virtualType.checkPath(this, opts); }; } + +type NodePathMixins = typeof NodePath_ancestry & + typeof NodePath_inference & + typeof NodePath_replacement & + typeof NodePath_evaluation & + typeof NodePath_conversion & + typeof NodePath_introspection & + typeof NodePath_context & + typeof NodePath_removal & + typeof NodePath_modification & + typeof NodePath_family & + typeof NodePath_comments; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +interface NodePath + extends NodePathAssetions, + NodePathValidators, + NodePathMixins {} + +export default NodePath; diff --git a/packages/babel-traverse/src/path/inference/index.js b/packages/babel-traverse/src/path/inference/index.ts similarity index 91% rename from packages/babel-traverse/src/path/inference/index.js rename to packages/babel-traverse/src/path/inference/index.ts index 69cd126a68..86eac2da72 100644 --- a/packages/babel-traverse/src/path/inference/index.js +++ b/packages/babel-traverse/src/path/inference/index.ts @@ -1,4 +1,4 @@ -import type NodePath from "./index"; +import type NodePath from "../index"; import * as inferers from "./inferers"; import * as t from "@babel/types"; @@ -6,7 +6,7 @@ import * as t from "@babel/types"; * Infer the type of the current `NodePath`. */ -export function getTypeAnnotation(): Object { +export function getTypeAnnotation(): t.FlowType { if (this.typeAnnotation) return this.typeAnnotation; let type = this._getTypeAnnotation() || t.anyTypeAnnotation(); @@ -23,7 +23,7 @@ const typeAnnotationInferringNodes = new WeakSet(); * todo: split up this method */ -export function _getTypeAnnotation(): ?Object { +export function _getTypeAnnotation(): any { const node = this.node; if (!node) { @@ -106,7 +106,7 @@ export function couldBeBaseType(name: string): boolean { if (t.isAnyTypeAnnotation(type)) return true; if (t.isUnionTypeAnnotation(type)) { - for (const type2 of (type.types: Array)) { + for (const type2 of type.types) { if (t.isAnyTypeAnnotation(type2) || _isBaseType(name, type2, true)) { return true; } @@ -117,13 +117,14 @@ export function couldBeBaseType(name: string): boolean { } } -export function baseTypeStrictlyMatches(right: NodePath) { +export function baseTypeStrictlyMatches(rightArg: NodePath): boolean { const left = this.getTypeAnnotation(); - right = right.getTypeAnnotation(); + const right = rightArg.getTypeAnnotation(); if (!t.isAnyTypeAnnotation(left) && t.isFlowBaseAnnotation(left)) { return right.type === left.type; } + return false; } export function isGenericType(genericName: string): boolean { diff --git a/packages/babel-traverse/src/path/inference/inferer-reference.js b/packages/babel-traverse/src/path/inference/inferer-reference.ts similarity index 87% rename from packages/babel-traverse/src/path/inference/inferer-reference.js rename to packages/babel-traverse/src/path/inference/inferer-reference.ts index bb6ab6827a..fc745dd143 100644 --- a/packages/babel-traverse/src/path/inference/inferer-reference.js +++ b/packages/babel-traverse/src/path/inference/inferer-reference.ts @@ -1,7 +1,8 @@ import type NodePath from "../index"; import * as t from "@babel/types"; +import type Binding from "../../scope/binding"; -export default function (node: Object) { +export default function (node: any) { if (!this.isReferenced()) return; // check if a binding exists of this value and if so then return a union type of all @@ -86,7 +87,7 @@ function getTypeAnnotationBindingConstantViolations(binding, path, name) { constantViolations = constantViolations.concat(functionConstantViolations); // push on inferred types of violated paths - for (const violation of (constantViolations: Array)) { + for (const violation of constantViolations) { types.push(violation.getTypeAnnotation()); } } @@ -106,7 +107,7 @@ function getTypeAnnotationBindingConstantViolations(binding, path, name) { return t.createUnionTypeAnnotation(types); } -function getConstantViolationsBefore(binding, path, functions) { +function getConstantViolationsBefore(binding: Binding, path, functions?) { const violations = binding.constantViolations.slice(); violations.unshift(binding.path); return violations.filter(violation => { @@ -117,7 +118,10 @@ function getConstantViolationsBefore(binding, path, functions) { }); } -function inferAnnotationFromBinaryExpression(name, path) { +function inferAnnotationFromBinaryExpression( + name: string, + path: NodePath, +) { const operator = path.node.operator; const right = path.get("right").resolve(); @@ -144,14 +148,14 @@ function inferAnnotationFromBinaryExpression(name, path) { if (operator !== "===" && operator !== "==") return; // - let typeofPath; - let typePath; + let typeofPath: NodePath; + let typePath: NodePath; if (left.isUnaryExpression({ operator: "typeof" })) { typeofPath = left; - typePath = right; + typePath = right as NodePath; } else if (right.isUnaryExpression({ operator: "typeof" })) { typeofPath = right; - typePath = left; + typePath = left as NodePath; } if (!typeofPath) return; @@ -159,14 +163,16 @@ function inferAnnotationFromBinaryExpression(name, path) { if (!typeofPath.get("argument").isIdentifier({ name })) return; // ensure that the type path is a Literal - typePath = typePath.resolve(); + typePath = typePath.resolve() as NodePath; if (!typePath.isLiteral()) return; // and that it's a string so we can infer it + // @ts-expect-error todo(flow->ts): value is not defined for NullLiteral and some other const typeValue = typePath.node.value; if (typeof typeValue !== "string") return; // turn type value into a type annotation + // @ts-expect-error todo(flow->ts): move validation from helper or relax type constraint to just a string return t.createTypeAnnotationBasedOnTypeof(typeValue); } @@ -188,7 +194,11 @@ function getParentConditionalPath(binding, path, name) { } } -function getConditionalAnnotation(binding, path, name) { +function getConditionalAnnotation( + binding: Binding, + path: NodePath, + name?, +) { const ifStatement = getParentConditionalPath(binding, path, name); if (!ifStatement) return; diff --git a/packages/babel-traverse/src/path/inference/inferers.js b/packages/babel-traverse/src/path/inference/inferers.ts similarity index 100% rename from packages/babel-traverse/src/path/inference/inferers.js rename to packages/babel-traverse/src/path/inference/inferers.ts diff --git a/packages/babel-traverse/src/path/introspection.js b/packages/babel-traverse/src/path/introspection.ts similarity index 87% rename from packages/babel-traverse/src/path/introspection.js rename to packages/babel-traverse/src/path/introspection.ts index c816765c2c..0e01e4f20c 100644 --- a/packages/babel-traverse/src/path/introspection.js +++ b/packages/babel-traverse/src/path/introspection.ts @@ -11,6 +11,7 @@ import * as t from "@babel/types"; */ export function matchesPattern( + this: NodePath, pattern: string, allowPartial?: boolean, ): boolean { @@ -22,7 +23,7 @@ export function matchesPattern( * if the array has any items, otherwise we just check if it's falsy. */ -export function has(key): boolean { +export function has(this: NodePath, key: string): boolean { const val = this.node && this.node[key]; if (val && Array.isArray(val)) { return !!val.length; @@ -35,7 +36,7 @@ export function has(key): boolean { * Description */ -export function isStatic() { +export function isStatic(this: NodePath): boolean { return this.scope.isStatic(this.node); } @@ -49,7 +50,7 @@ export const is = has; * Opposite of `has`. */ -export function isnt(key): boolean { +export function isnt(this: NodePath, key: string): boolean { return !this.has(key); } @@ -57,7 +58,7 @@ export function isnt(key): boolean { * Check whether the path node `key` strict equals `value`. */ -export function equals(key, value): boolean { +export function equals(this: NodePath, key: string, value): boolean { return this.node[key] === value; } @@ -66,7 +67,7 @@ export function equals(key, value): boolean { * been removed yet we still internally know the type and need it to calculate node replacement. */ -export function isNodeType(type: string): boolean { +export function isNodeType(this: NodePath, type: string): boolean { return t.isType(this.type, type); } @@ -80,7 +81,7 @@ export function isNodeType(type: string): boolean { * to tell the path replacement that it's ok to replace this with an expression. */ -export function canHaveVariableDeclarationOrExpression() { +export function canHaveVariableDeclarationOrExpression(this: NodePath) { return ( (this.key === "init" || this.key === "left") && this.parentPath.isFor() ); @@ -94,7 +95,10 @@ export function canHaveVariableDeclarationOrExpression() { * is the same as containing a block statement. */ -export function canSwapBetweenExpressionAndStatement(replacement) { +export function canSwapBetweenExpressionAndStatement( + this: NodePath, + replacement: t.Node, +): boolean { if (this.key !== "body" || !this.parentPath.isArrowFunctionExpression()) { return false; } @@ -112,7 +116,10 @@ export function canSwapBetweenExpressionAndStatement(replacement) { * Check whether the current path references a completion record */ -export function isCompletionRecord(allowInsideFunction?) { +export function isCompletionRecord( + this: NodePath, + allowInsideFunction?: boolean, +): boolean { let path = this; let first = true; @@ -141,14 +148,14 @@ export function isCompletionRecord(allowInsideFunction?) { * so we can explode it if necessary. */ -export function isStatementOrBlock() { +export function isStatementOrBlock(this: NodePath): boolean { if ( this.parentPath.isLabeledStatement() || t.isBlockStatement(this.container) ) { return false; } else { - return t.STATEMENT_OR_BLOCK_KEYS.includes(this.key); + return t.STATEMENT_OR_BLOCK_KEYS.includes(this.key as string); } } @@ -156,7 +163,11 @@ export function isStatementOrBlock() { * Check if the currently assigned path references the `importName` of `moduleSource`. */ -export function referencesImport(moduleSource, importName) { +export function referencesImport( + this: NodePath, + moduleSource: string, + importName: string, +): boolean { if (!this.isReferencedIdentifier()) return false; const binding = this.scope.getBinding(this.node.name); @@ -181,7 +192,10 @@ export function referencesImport(moduleSource, importName) { return true; } - if (path.isImportSpecifier() && path.node.imported.name === importName) { + if ( + path.isImportSpecifier() && + t.isIdentifier(path.node.imported, { name: importName }) + ) { return true; } @@ -192,7 +206,7 @@ export function referencesImport(moduleSource, importName) { * Get the source code associated with this node. */ -export function getSource() { +export function getSource(this: NodePath): string { const node = this.node; if (node.end) { const code = this.hub.getCode(); @@ -201,7 +215,7 @@ export function getSource() { return ""; } -export function willIMaybeExecuteBefore(target) { +export function willIMaybeExecuteBefore(this: NodePath, target): boolean { return this._guessExecutionStatusRelativeTo(target) !== "after"; } @@ -282,6 +296,7 @@ type RelativeExecutionStatus = "before" | "after" | "unknown"; */ export function _guessExecutionStatusRelativeTo( + this: NodePath, target: NodePath, ): RelativeExecutionStatus { // check if the two paths are in different functions, we can't track execution of these @@ -354,8 +369,8 @@ export function _guessExecutionStatusRelativeTo( // otherwise we're associated by a parent node, check which key comes before the other const keys = t.VISITOR_KEYS[commonPath.type]; const keyPosition = { - this: keys.indexOf(divergence.this.parentKey), - target: keys.indexOf(divergence.target.parentKey), + this: keys.indexOf(divergence.this.parentKey as string), + target: keys.indexOf(divergence.target.parentKey as string), }; return keyPosition.target > keyPosition.this ? "before" : "after"; } @@ -367,6 +382,7 @@ export function _guessExecutionStatusRelativeTo( const executionOrderCheckedNodes = new WeakSet(); export function _guessExecutionStatusRelativeToDifferentFunctions( + this: NodePath, target: NodePath, ): RelativeExecutionStatus { if ( @@ -423,12 +439,15 @@ export function _guessExecutionStatusRelativeToDifferentFunctions( /** * Resolve a "pointer" `NodePath` to it's absolute path. */ - -export function resolve(dangerous, resolved) { +export function resolve(this: NodePath, dangerous?, resolved?) { return this._resolve(dangerous, resolved) || this; } -export function _resolve(dangerous?, resolved?): ?NodePath { +export function _resolve( + this: NodePath, + dangerous?, + resolved?, +): NodePath | undefined | null { // detect infinite recursion // todo: possibly have a max length on this just to be safe if (resolved && resolved.indexOf(this) >= 0) return; @@ -444,6 +463,7 @@ export function _resolve(dangerous?, resolved?): ?NodePath { // otherwise it's a request for a pattern and that's a bit more tricky } } else if (this.isReferencedIdentifier()) { + // @ts-expect-error todo(flow->ts): think about options to improve type refinements const binding = this.scope.getBinding(this.node.name); if (!binding) return; @@ -460,6 +480,7 @@ export function _resolve(dangerous?, resolved?): ?NodePath { return ret; } } else if (this.isTypeCastExpression()) { + // @ ts-ignore todo: babel-types return this.get("expression").resolve(dangerous, resolved); } else if (dangerous && this.isMemberExpression()) { // this is dangerous, as non-direct target assignments will mutate it's state @@ -468,13 +489,14 @@ export function _resolve(dangerous?, resolved?): ?NodePath { const targetKey = this.toComputedKey(); if (!t.isLiteral(targetKey)) return; + // @ts-expect-error todo(flow->ts): NullLiteral const targetName = targetKey.value; const target = this.get("object").resolve(dangerous, resolved); if (target.isObjectExpression()) { const props = target.get("properties"); - for (const prop of (props: Array)) { + for (const prop of props as any[]) { if (!prop.isProperty()) continue; const key = prop.get("key"); @@ -496,7 +518,7 @@ export function _resolve(dangerous?, resolved?): ?NodePath { } } -export function isConstantExpression() { +export function isConstantExpression(this: NodePath) { if (this.isIdentifier()) { const binding = this.scope.getBinding(this.node.name); if (!binding) return false; @@ -518,7 +540,7 @@ export function isConstantExpression() { } if (this.isUnaryExpression()) { - if (this.get("operator").node !== "void") { + if (this.node.operator !== "void") { return false; } @@ -535,7 +557,7 @@ export function isConstantExpression() { return false; } -export function isInStrictMode() { +export function isInStrictMode(this: NodePath) { const start = this.isProgram() ? this : this.parentPath; const strictParent = start.find(path => { @@ -552,10 +574,11 @@ export function isInStrictMode() { return false; } - let { node } = path; - if (path.isFunction()) node = node.body; + const body: t.BlockStatement | t.Program = path.isFunction() + ? (path.node.body as t.BlockStatement) + : (path.node as t.Program); - for (const directive of node.directives) { + for (const directive of body.directives) { if (directive.value.value === "use strict") { return true; } diff --git a/packages/babel-traverse/src/path/lib/hoister.js b/packages/babel-traverse/src/path/lib/hoister.ts similarity index 89% rename from packages/babel-traverse/src/path/lib/hoister.js rename to packages/babel-traverse/src/path/lib/hoister.ts index 7e7f850346..7a6b043528 100644 --- a/packages/babel-traverse/src/path/lib/hoister.js +++ b/packages/babel-traverse/src/path/lib/hoister.ts @@ -1,7 +1,11 @@ import { react } from "@babel/types"; import * as t from "@babel/types"; +import type Scope from "../../scope"; +import type NodePath from "../index"; +import type Binding from "../../scope/binding"; +import type { Visitor } from "../../types"; -const referenceVisitor = { +const referenceVisitor: Visitor = { // This visitor looks for bindings to establish a topmost scope for hoisting. ReferencedIdentifier(path, state) { // Don't hoist regular JSX identifiers ('div', 'span', etc). @@ -49,8 +53,16 @@ const referenceVisitor = { }, }; -export default class PathHoister { - constructor(path, scope) { +export default class PathHoister { + breakOnScopePaths: NodePath[]; + bindings: { [k: string]: Binding }; + mutableBinding: boolean; + private scopes: Scope[]; + scope: Scope; + private path: NodePath; + private attachAfter: boolean; + + constructor(path: NodePath, scope: Scope) { // Storage for scopes we can't hoist above. this.breakOnScopePaths = []; // Storage for bindings that may affect what path we can hoist to. @@ -131,7 +143,7 @@ export default class PathHoister { path = binding.path; // We also move past any constant violations. - for (const violationPath of (binding.constantViolations: Array)) { + for (const violationPath of binding.constantViolations) { if (this.getAttachmentParentForPath(violationPath).key > path.key) { path = violationPath; } @@ -156,10 +168,11 @@ export default class PathHoister { if (this.scope === scope) return; // needs to be attached to the body - const bodies = scope.path.get("body").get("body"); + const bodies = scope.path.get("body").get("body") as NodePath[]; for (let i = 0; i < bodies.length; i++) { // Don't attach to something that's going to get hoisted, // like a default parameter + // @ts-expect-error todo(flow->ts): avoid mutating the node, introducing new fields if (bodies[i].node._blockHoist) continue; return bodies[i]; } @@ -221,6 +234,7 @@ export default class PathHoister { // generate declaration and insert it to our point let uid = attachTo.scope.generateUidIdentifier("ref"); + // @ts-expect-error todo(flow->ts): more specific type for this.path const declarator = t.variableDeclarator(uid, this.path.node); const insertFn = this.attachAfter ? "insertAfter" : "insertBefore"; @@ -234,7 +248,7 @@ export default class PathHoister { if (parent.isJSXElement() && this.path.container === parent.node.children) { // turning the `span` in `
` to an expression so we need to wrap it with // an expression container - uid = t.JSXExpressionContainer(uid); + uid = t.jsxExpressionContainer(uid); } this.path.replaceWith(t.cloneNode(uid)); diff --git a/packages/babel-traverse/src/path/lib/removal-hooks.js b/packages/babel-traverse/src/path/lib/removal-hooks.ts similarity index 100% rename from packages/babel-traverse/src/path/lib/removal-hooks.js rename to packages/babel-traverse/src/path/lib/removal-hooks.ts diff --git a/packages/babel-traverse/src/path/lib/virtual-types.js b/packages/babel-traverse/src/path/lib/virtual-types.ts similarity index 94% rename from packages/babel-traverse/src/path/lib/virtual-types.js rename to packages/babel-traverse/src/path/lib/virtual-types.ts index d7072d6f6e..9ce9ec73d1 100644 --- a/packages/babel-traverse/src/path/lib/virtual-types.js +++ b/packages/babel-traverse/src/path/lib/virtual-types.ts @@ -4,7 +4,7 @@ import * as t from "@babel/types"; export const ReferencedIdentifier = { types: ["Identifier", "JSXIdentifier"], - checkPath(path: NodePath, opts?: Object): boolean { + checkPath(path: NodePath, opts?: any): boolean { const { node, parent } = path; if (!t.isIdentifier(node, opts) && !t.isJSXMemberExpression(parent, opts)) { if (t.isJSXIdentifier(node, opts)) { @@ -116,6 +116,7 @@ export const Flow = { } else if (t.isImportDeclaration(node)) { return node.importKind === "type" || node.importKind === "typeof"; } else if (t.isExportDeclaration(node)) { + // @ts-expect-error todo(flow->ts) `exportKind` does not exist on ExportAllDeclaration return node.exportKind === "type"; } else if (t.isImportSpecifier(node)) { return node.importKind === "type" || node.importKind === "typeof"; @@ -150,7 +151,7 @@ export const NumericLiteralTypeAnnotation = { export const ForAwaitStatement = { types: ["ForOfStatement"], - checkPath({ node }: NodePath): boolean { + checkPath({ node }: NodePath): boolean { return node.await === true; }, }; diff --git a/packages/babel-traverse/src/path/modification.js b/packages/babel-traverse/src/path/modification.ts similarity index 71% rename from packages/babel-traverse/src/path/modification.js rename to packages/babel-traverse/src/path/modification.ts index f425425efe..fc4ac0dd4b 100644 --- a/packages/babel-traverse/src/path/modification.js +++ b/packages/babel-traverse/src/path/modification.ts @@ -4,15 +4,16 @@ import { path as pathCache } from "../cache"; import PathHoister from "./lib/hoister"; import NodePath from "./index"; import * as t from "@babel/types"; +import type Scope from "../scope"; /** * Insert the provided nodes before the current one. */ -export function insertBefore(nodes) { +export function insertBefore(this: NodePath, nodes_: t.Node | t.Node[]) { this._assertUnremoved(); - nodes = this._verifyNodeList(nodes); + const nodes = this._verifyNodeList(nodes_); const { parentPath } = this; @@ -28,17 +29,18 @@ export function insertBefore(nodes) { (parentPath.isForStatement() && this.key === "init") ) { if (this.node) nodes.push(this.node); + // @ts-expect-error todo(flow->ts): check that nodes is an array of statements return this.replaceExpressionWithStatements(nodes); } else if (Array.isArray(this.container)) { return this._containerInsertBefore(nodes); } else if (this.isStatementOrBlock()) { + const node = this.node as t.Statement; const shouldInsertCurrentNode = - this.node && - (!this.isExpressionStatement() || this.node.expression != null); + node && + (!this.isExpressionStatement() || + (node as t.ExpressionStatement).expression != null); - this.replaceWith( - t.blockStatement(shouldInsertCurrentNode ? [this.node] : []), - ); + this.replaceWith(t.blockStatement(shouldInsertCurrentNode ? [node] : [])); return this.unshiftContainer("body", nodes); } else { throw new Error( @@ -48,11 +50,12 @@ export function insertBefore(nodes) { } } -export function _containerInsert(from, nodes) { +export function _containerInsert(this: NodePath, from, nodes) { this.updateSiblingKeys(from, nodes.length); const paths = []; + // @ts-expect-error todo(flow->ts): this.container could be a NodePath this.container.splice(from, 0, ...nodes); for (let i = 0; i < nodes.length; i++) { const to = from + i; @@ -78,11 +81,12 @@ export function _containerInsert(from, nodes) { return paths; } -export function _containerInsertBefore(nodes) { +export function _containerInsertBefore(this: NodePath, nodes) { return this._containerInsert(this.key, nodes); } -export function _containerInsertAfter(nodes) { +export function _containerInsertAfter(this: NodePath, nodes) { + // @ts-expect-error todo(flow->ts): this.key could be a string return this._containerInsert(this.key + 1, nodes); } @@ -91,10 +95,10 @@ export function _containerInsertAfter(nodes) { * expression, ensure that the completion record is correct by pushing the current node. */ -export function insertAfter(nodes) { +export function insertAfter(this: NodePath, nodes_: t.Node | t.Node[]) { this._assertUnremoved(); - nodes = this._verifyNodeList(nodes); + const nodes = this._verifyNodeList(nodes_); const { parentPath } = this; if ( @@ -121,31 +125,36 @@ export function insertAfter(nodes) { (parentPath.isForStatement() && this.key === "init") ) { if (this.node) { + const node = this.node as t.Expression | t.VariableDeclaration; let { scope } = this; // Inserting after the computed key of a method should insert the // temporary binding in the method's parent's scope. - if (parentPath.isMethod({ computed: true, key: this.node })) { + if (parentPath.isMethod({ computed: true, key: node })) { scope = scope.parent; } const temp = scope.generateDeclaredUidIdentifier(); nodes.unshift( t.expressionStatement( - t.assignmentExpression("=", t.cloneNode(temp), this.node), + // @ts-expect-error todo(flow->ts): This can be a variable + // declaraion in the "init" of a for statement, but that's + // invalid here. + t.assignmentExpression("=", t.cloneNode(temp), node), ), ); nodes.push(t.expressionStatement(t.cloneNode(temp))); } + // @ts-expect-error todo(flow->ts): check that nodes is an array of statements return this.replaceExpressionWithStatements(nodes); } else if (Array.isArray(this.container)) { return this._containerInsertAfter(nodes); } else if (this.isStatementOrBlock()) { + const node = this.node as t.Statement; const shouldInsertCurrentNode = - this.node && - (!this.isExpressionStatement() || this.node.expression != null); + node && + (!this.isExpressionStatement() || + (node as t.ExpressionStatement).expression != null); - this.replaceWith( - t.blockStatement(shouldInsertCurrentNode ? [this.node] : []), - ); + this.replaceWith(t.blockStatement(shouldInsertCurrentNode ? [node] : [])); return this.pushContainer("body", nodes); } else { throw new Error( @@ -159,7 +168,11 @@ export function insertAfter(nodes) { * Update all sibling node paths after `fromIndex` by `incrementBy`. */ -export function updateSiblingKeys(fromIndex, incrementBy) { +export function updateSiblingKeys( + this: NodePath, + fromIndex: number, + incrementBy: number, +) { if (!this.parent) return; const paths = pathCache.get(this.parent); @@ -170,12 +183,15 @@ export function updateSiblingKeys(fromIndex, incrementBy) { } } -export function _verifyNodeList(nodes) { +export function _verifyNodeList( + this: NodePath, + nodes: t.Node | t.Node[], +): t.Node[] { if (!nodes) { return []; } - if (nodes.constructor !== Array) { + if (!Array.isArray(nodes)) { nodes = [nodes]; } @@ -204,7 +220,11 @@ export function _verifyNodeList(nodes) { return nodes; } -export function unshiftContainer(listKey, nodes) { +export function unshiftContainer( + listKey: string, + nodes: Nodes, +): NodePath[] { + // todo: NodePaths this._assertUnremoved(); nodes = this._verifyNodeList(nodes); @@ -222,10 +242,14 @@ export function unshiftContainer(listKey, nodes) { return path._containerInsertBefore(nodes); } -export function pushContainer(listKey, nodes) { +export function pushContainer( + this: NodePath, + listKey: string, + nodes: t.Node | t.Node[], +) { this._assertUnremoved(); - nodes = this._verifyNodeList(nodes); + const verifiedNodes = this._verifyNodeList(nodes); // get an invisible path that represents the last node + 1 and replace it with our // nodes, effectively inlining it @@ -239,15 +263,17 @@ export function pushContainer(listKey, nodes) { key: container.length, }).setContext(this.context); - return path.replaceWithMultiple(nodes); + return path.replaceWithMultiple(verifiedNodes); } /** * Hoist the current node to the highest scope possible and return a UID * referencing it. */ - -export function hoist(scope = this.scope) { - const hoister = new PathHoister(this, scope); +export function hoist( + this: NodePath, + scope: Scope = this.scope, +) { + const hoister = new PathHoister(this, scope); return hoister.run(); } diff --git a/packages/babel-traverse/src/path/removal.js b/packages/babel-traverse/src/path/removal.ts similarity index 67% rename from packages/babel-traverse/src/path/removal.js rename to packages/babel-traverse/src/path/removal.ts index a8bd4523fd..b4366f7676 100644 --- a/packages/babel-traverse/src/path/removal.js +++ b/packages/babel-traverse/src/path/removal.ts @@ -2,9 +2,9 @@ import { hooks } from "./lib/removal-hooks"; import { path as pathCache } from "../cache"; -import { REMOVED, SHOULD_SKIP } from "./index"; +import NodePath, { REMOVED, SHOULD_SKIP } from "./index"; -export function remove() { +export function remove(this: NodePath) { this._assertUnremoved(); this.resync(); @@ -22,34 +22,34 @@ export function remove() { this._markRemoved(); } -export function _removeFromScope() { +export function _removeFromScope(this: NodePath) { const bindings = this.getBindingIdentifiers(); Object.keys(bindings).forEach(name => this.scope.removeBinding(name)); } -export function _callRemovalHooks() { - for (const fn of (hooks: Array)) { +export function _callRemovalHooks(this: NodePath) { + for (const fn of hooks) { if (fn(this, this.parentPath)) return true; } } -export function _remove() { +export function _remove(this: NodePath) { if (Array.isArray(this.container)) { - this.container.splice(this.key, 1); - this.updateSiblingKeys(this.key, -1); + this.container.splice(this.key as number, 1); + this.updateSiblingKeys(this.key as number, -1); } else { this._replaceWith(null); } } -export function _markRemoved() { +export function _markRemoved(this: NodePath) { // this.shouldSkip = true; this.removed = true; this._traverseFlags |= SHOULD_SKIP | REMOVED; if (this.parent) pathCache.get(this.parent).delete(this.node); this.node = null; } -export function _assertUnremoved() { +export function _assertUnremoved(this: NodePath) { if (this.removed) { throw this.buildCodeFrameError( "NodePath has been removed so is read-only.", diff --git a/packages/babel-traverse/src/path/replacement.js b/packages/babel-traverse/src/path/replacement.ts similarity index 85% rename from packages/babel-traverse/src/path/replacement.js rename to packages/babel-traverse/src/path/replacement.ts index dc15128051..3774fa53ff 100644 --- a/packages/babel-traverse/src/path/replacement.js +++ b/packages/babel-traverse/src/path/replacement.ts @@ -22,7 +22,7 @@ const hoistVariablesVisitor = { const exprs = []; - for (const declar of (path.node.declarations: Array)) { + for (const declar of path.node.declarations as Array) { if (declar.init) { exprs.push( t.expressionStatement( @@ -44,7 +44,10 @@ const hoistVariablesVisitor = { * - Remove the current node. */ -export function replaceWithMultiple(nodes: Array) { +export function replaceWithMultiple>( + nodes: Nodes, +): NodePath[] { + // todo NodePaths this.resync(); nodes = this._verifyNodeList(nodes); @@ -70,7 +73,7 @@ export function replaceWithMultiple(nodes: Array) { * easier to use, your transforms will be extremely brittle. */ -export function replaceWithSourceString(replacement) { +export function replaceWithSourceString(this: NodePath, replacement) { this.resync(); try { @@ -101,7 +104,7 @@ export function replaceWithSourceString(replacement) { * Replace the current node with another. */ -export function replaceWith(replacement) { +export function replaceWith(this: NodePath, replacement: t.Node | NodePath) { this.resync(); if (this.removed) { @@ -187,15 +190,16 @@ export function replaceWith(replacement) { * Description */ -export function _replaceWith(node) { +export function _replaceWith(this: NodePath, node) { if (!this.container) { throw new ReferenceError("Container is falsy"); } if (this.inList) { + // @ts-expect-error todo(flow->ts): check if t.validate accepts a numeric key t.validate(this.parent, this.key, [node]); } else { - t.validate(this.parent, this.key, node); + t.validate(this.parent, this.key as string, node); } this.debug(`Replace with ${node?.type}`); @@ -210,7 +214,10 @@ export function _replaceWith(node) { * extremely important to retain original semantics. */ -export function replaceExpressionWithStatements(nodes: Array) { +export function replaceExpressionWithStatements( + this: NodePath, + nodes: Array, +) { this.resync(); const toSequenceExpression = t.toSequenceExpression(nodes, this.scope); @@ -225,12 +232,17 @@ export function replaceExpressionWithStatements(nodes: Array) { const container = t.arrowFunctionExpression([], t.blockStatement(nodes)); this.replaceWith(t.callExpression(container, [])); + // replaceWith changes the type of "this", but it isn't trackable by TS + type ThisType = NodePath< + t.CallExpression & { callee: t.ArrowFunctionExpression } + >; + this.traverse(hoistVariablesVisitor); // add implicit returns to all ending expression statements - const completionRecords: Array = this.get( - "callee", - ).getCompletionRecords(); + const completionRecords: Array = (this as ThisType) + .get("callee") + .getCompletionRecords(); for (const path of completionRecords) { if (!path.isExpressionStatement()) continue; @@ -239,7 +251,7 @@ export function replaceExpressionWithStatements(nodes: Array) { let uid = loop.getData("expressionReplacementReturnUid"); if (!uid) { - const callee = this.get("callee"); + const callee = (this as ThisType).get("callee"); uid = callee.scope.generateDeclaredUidIdentifier("ret"); callee .get("body") @@ -259,26 +271,26 @@ export function replaceExpressionWithStatements(nodes: Array) { } } - const callee = this.get("callee"); + const callee = this.get("callee") as NodePath; callee.arrowFunctionToExpression(); // (() => await xxx)() -> await (async () => await xxx)(); if ( isParentAsync && traverse.hasType( - this.get("callee.body").node, + (this.get("callee.body") as NodePath).node, "AwaitExpression", t.FUNCTION_TYPES, ) ) { callee.set("async", true); - this.replaceWith(t.awaitExpression(this.node)); + this.replaceWith(t.awaitExpression((this as ThisType).node)); } return callee.get("body.body"); } -export function replaceInline(nodes: Object | Array) { +export function replaceInline(this: NodePath, nodes: t.Node | Array) { this.resync(); if (Array.isArray(nodes)) { diff --git a/packages/babel-traverse/src/scope/binding.js b/packages/babel-traverse/src/scope/binding.ts similarity index 78% rename from packages/babel-traverse/src/scope/binding.js rename to packages/babel-traverse/src/scope/binding.ts index 341ecdfb6a..8c6ce408b2 100644 --- a/packages/babel-traverse/src/scope/binding.js +++ b/packages/babel-traverse/src/scope/binding.ts @@ -1,5 +1,16 @@ import type NodePath from "../path"; +import type * as t from "@babel/types"; +import type Scope from "./index"; +type BindingKind = + | "var" + | "let" + | "const" + | "module" + | "hoisted" + | "param" + | "local" + | "unknown"; /** * This class is responsible for a binding inside of a scope. * @@ -12,7 +23,22 @@ import type NodePath from "../path"; */ export default class Binding { - constructor({ identifier, scope, path, kind }) { + identifier: t.Identifier; + scope: Scope; + path: NodePath; + kind: BindingKind; + + constructor({ + identifier, + scope, + path, + kind, + }: { + identifier: t.Identifier; + scope: Scope; + path: NodePath; + kind: BindingKind; + }) { this.identifier = identifier; this.scope = scope; this.path = path; @@ -53,7 +79,7 @@ export default class Binding { * Register a constant violation with the provided `path`. */ - reassign(path: Object) { + reassign(path: any) { this.constant = false; if (this.constantViolations.indexOf(path) !== -1) { return; diff --git a/packages/babel-traverse/src/scope/index.js b/packages/babel-traverse/src/scope/index.ts similarity index 82% rename from packages/babel-traverse/src/scope/index.js rename to packages/babel-traverse/src/scope/index.ts index 9e76fd7497..312364132d 100644 --- a/packages/babel-traverse/src/scope/index.js +++ b/packages/babel-traverse/src/scope/index.ts @@ -1,26 +1,57 @@ import Renamer from "./lib/renamer"; import type NodePath from "../path"; import traverse from "../index"; +import type { TraverseOptions } from "../index"; import Binding from "./binding"; import globals from "globals"; import * as t from "@babel/types"; import { scope as scopeCache } from "../cache"; +import type { Visitor } from "../types"; // Recursively gathers the identifying names of a node. -function gatherNodeParts(node: Object, parts: Array) { +function gatherNodeParts(node: t.Node, parts: any[]) { switch (node?.type) { default: if (t.isModuleDeclaration(node)) { - if (node.source) { + if ( + (t.isExportAllDeclaration(node) || + t.isExportNamedDeclaration(node) || + t.isImportDeclaration(node)) && + node.source + ) { gatherNodeParts(node.source, parts); - } else if (node.specifiers && node.specifiers.length) { + } else if ( + (t.isExportNamedDeclaration(node) || t.isImportDeclaration(node)) && + node.specifiers && + node.specifiers.length + ) { for (const e of node.specifiers) gatherNodeParts(e, parts); - } else if (node.declaration) { + } else if ( + (t.isExportDefaultDeclaration(node) || + t.isExportNamedDeclaration(node)) && + node.declaration + ) { gatherNodeParts(node.declaration, parts); } } else if (t.isModuleSpecifier(node)) { + // todo(flow->ts): should condition instead be: + // ``` + // t.isExportSpecifier(node) || + // t.isImportDefaultSpecifier(node) || + // t.isImportNamespaceSpecifier(node) || + // t.isImportSpecifier(node) + // ``` + // allowing only nodes with `.local`? + // @ts-expect-error todo(flow->ts) gatherNodeParts(node.local, parts); } else if (t.isLiteral(node)) { + // todo(flow->ts): should condition be stricter to ensure value is there + // ``` + // !t.isNullLiteral(node) && + // !t.isRegExpLiteral(node) && + // !t.isTemplateLiteral(node) + // ``` + // @ts-expect-error todo(flow->ts) parts.push(node.value); } break; @@ -147,11 +178,17 @@ function gatherNodeParts(node: Object, parts: Array) { } // +interface CollectVisitorState { + assignments: NodePath[]; + references: NodePath[]; + constantViolations: NodePath[]; +} -const collectorVisitor = { +const collectorVisitor: Visitor = { For(path) { - for (const key of (t.FOR_INIT_KEYS: Array)) { - const declar = path.get(key); + for (const key of t.FOR_INIT_KEYS) { + // todo: might be not needed with improvement to babel-types + const declar = path.get(key) as NodePath; // delegate block scope handling to the `BlockScoped` method if (declar.isVar()) { const parentScope = @@ -166,6 +203,7 @@ const collectorVisitor = { if (path.isBlockScoped()) return; // this will be hit again once we traverse into it after this iteration + // @ts-expect-error todo(flow->ts): might be not correct for export all declaration if (path.isExportDeclaration() && path.get("declaration").isDeclaration()) { return; } @@ -190,6 +228,7 @@ const collectorVisitor = { ExportDeclaration: { exit(path) { const { node, scope } = path; + // @ts-expect-error todo(flow->ts) declaration is not present on ExportAllDeclaration const declar = node.declaration; if (t.isClassDeclaration(declar) || t.isFunctionDeclaration(declar)) { const id = declar.id; @@ -198,7 +237,7 @@ const collectorVisitor = { const binding = scope.getBinding(id.name); if (binding) binding.reference(path); } else if (t.isVariableDeclaration(declar)) { - for (const decl of (declar.declarations: Array)) { + for (const decl of declar.declarations) { for (const name of Object.keys(t.getBindingIdentifiers(decl))) { const binding = scope.getBinding(name); if (binding) binding.reference(path); @@ -209,6 +248,7 @@ const collectorVisitor = { }, LabeledStatement(path) { + // @ts-expect-error todo(flow->ts): possible bug - statement might not have name and so should not be added as global path.scope.getProgramParent().addGlobal(path.node); path.scope.getBlockParent().registerDeclaration(path); }, @@ -245,7 +285,7 @@ const collectorVisitor = { Block(path) { const paths = path.get("body"); - for (const bodyPath of (paths: Array)) { + for (const bodyPath of paths) { if (bodyPath.isFunctionDeclaration()) { path.scope.getBlockParent().registerDeclaration(bodyPath); } @@ -281,11 +321,25 @@ const collectorVisitor = { let uid = 0; export default class Scope { + uid; + + path: NodePath; + block: t.Node; + + labels; + inited; + + bindings: { [name: string]: Binding }; + references: object; + globals: object; + uids: object; + data: object; + crawling: boolean; + /** * This searches the current "scope" and collects all references/bindings * within. */ - constructor(path: NodePath) { const { node } = path; const cached = scopeCache.get(node); @@ -330,11 +384,16 @@ export default class Scope { return this.path.hub; } + traverse( + node: t.Node | t.Node[], + opts: TraverseOptions, + state: S, + ): void; + traverse(node: t.Node | t.Node[], opts?: TraverseOptions, state?: any): void; /** * Traverse node with current scope and path. */ - - traverse(node: Object, opts: Object, state?) { + traverse(node: any, opts: any, state?) { traverse(node, opts, this, state, this.path); } @@ -360,7 +419,7 @@ export default class Scope { * Generate a unique `_id1` binding. */ - generateUid(name: string = "temp") { + generateUid(name: string = "temp"): string { name = t .toIdentifier(name) .replace(/^_+/, "") @@ -395,7 +454,7 @@ export default class Scope { return `_${id}`; } - generateUidBasedOnNode(node: Object, defaultName?: String) { + generateUidBasedOnNode(node: t.Node, defaultName?: string) { const parts = []; gatherNodeParts(node, parts); @@ -409,7 +468,7 @@ export default class Scope { * Generate a unique identifier based on a node. */ - generateUidIdentifierBasedOnNode(node: Object, defaultName?: String): Object { + generateUidIdentifierBasedOnNode(node: t.Node, defaultName?: string) { return t.identifier(this.generateUidBasedOnNode(node, defaultName)); } @@ -423,7 +482,7 @@ export default class Scope { * - Bound identifiers */ - isStatic(node: Object): boolean { + isStatic(node: t.Node): boolean { if (t.isThisExpression(node) || t.isSuper(node)) { return true; } @@ -444,7 +503,7 @@ export default class Scope { * Possibly generate a memoised identifier if it is not static and has consequences. */ - maybeGenerateMemoised(node: Object, dontPush?: boolean): ?Object { + maybeGenerateMemoised(node: t.Node, dontPush?: boolean) { if (this.isStatic(node)) { return null; } else { @@ -457,7 +516,12 @@ export default class Scope { } } - checkBlockScopedCollisions(local, kind: string, name: string, id: Object) { + checkBlockScopedCollisions( + local: Binding, + kind: string, + name: string, + id: any, + ) { // ignore parameters if (kind === "param") return; @@ -483,7 +547,7 @@ export default class Scope { } } - rename(oldName: string, newName: string, block?) { + rename(oldName: string, newName?: string, block?: t.Node) { const binding = this.getBinding(oldName); if (binding) { newName = newName || this.generateUidIdentifier(oldName).name; @@ -501,7 +565,7 @@ export default class Scope { dump() { const sep = "-".repeat(60); console.log(sep); - let scope = this; + let scope: Scope = this; do { console.log("#", scope.block.type); for (const name of Object.keys(scope.bindings)) { @@ -518,7 +582,7 @@ export default class Scope { } // TODO: (Babel 8) Split i in two parameters, and use an object of flags - toArray(node: Object, i?: number | boolean, allowArrayLike?: boolean) { + toArray(node: t.Node, i?: number | boolean, allowArrayLike?: boolean) { if (t.isIdentifier(node)) { const binding = this.getBinding(node.name); if (binding?.constant && binding.path.isGenericType("Array")) { @@ -567,6 +631,7 @@ export default class Scope { helperName = "maybeArrayLike"; } + // @ts-expect-error todo(flow->ts): t.Node is not valid to use in args, function argument typeneeds to be clarified return t.callExpression(this.hub.addHelper(helperName), args); } @@ -578,7 +643,7 @@ export default class Scope { return this.labels.get(name); } - registerLabel(path: NodePath) { + registerLabel(path: NodePath) { this.labels.set(path.node.label.name, path); } @@ -589,18 +654,19 @@ export default class Scope { this.registerBinding("hoisted", path.get("id"), path); } else if (path.isVariableDeclaration()) { const declarations = path.get("declarations"); - for (const declar of (declarations: Array)) { + for (const declar of declarations) { this.registerBinding(path.node.kind, declar); } } else if (path.isClassDeclaration()) { this.registerBinding("let", path); } else if (path.isImportDeclaration()) { const specifiers = path.get("specifiers"); - for (const specifier of (specifiers: Array)) { + for (const specifier of specifiers) { this.registerBinding("module", specifier); } } else if (path.isExportDeclaration()) { - const declar = path.get("declaration"); + // todo: improve babel-types + const declar = path.get("declaration") as NodePath; if ( declar.isClassDeclaration() || declar.isFunctionDeclaration() || @@ -625,7 +691,11 @@ export default class Scope { } } - registerBinding(kind: string, path: NodePath, bindingPath = path) { + registerBinding( + kind: Binding["kind"], + path: NodePath, + bindingPath: NodePath = path, + ) { if (!kind) throw new ReferenceError("no `kind`"); if (path.isVariableDeclaration()) { @@ -642,7 +712,7 @@ export default class Scope { for (const name of Object.keys(ids)) { parent.references[name] = true; - for (const id of (ids[name]: Array)) { + for (const id of ids[name]) { const local = this.getOwnBinding(name); if (local) { @@ -668,12 +738,13 @@ export default class Scope { } } - addGlobal(node: Object) { + // todo: flow->ts maybe add more specific type + addGlobal(node: Extract) { this.globals[node.name] = node; } - hasUid(name): boolean { - let scope = this; + hasUid(name: string): boolean { + let scope: Scope = this; do { if (scope.uids[name]) return true; @@ -683,7 +754,7 @@ export default class Scope { } hasGlobal(name: string): boolean { - let scope = this; + let scope: Scope = this; do { if (scope.globals[name]) return true; @@ -696,7 +767,7 @@ export default class Scope { return !!this.getProgramParent().references[name]; } - isPure(node, constantsOnly?: boolean) { + isPure(node: t.Node, constantsOnly?: boolean) { if (t.isIdentifier(node)) { const binding = this.getBinding(node.name); if (!binding) return false; @@ -718,12 +789,12 @@ export default class Scope { this.isPure(node.right, constantsOnly) ); } else if (t.isArrayExpression(node)) { - for (const elem of (node.elements: Array)) { + for (const elem of node.elements) { if (!this.isPure(elem, constantsOnly)) return false; } return true; } else if (t.isObjectExpression(node)) { - for (const prop of (node.properties: Array)) { + for (const prop of node.properties) { if (!this.isPure(prop, constantsOnly)) return false; } return true; @@ -732,6 +803,7 @@ export default class Scope { if (node.kind === "get" || node.kind === "set") return false; return true; } else if (t.isProperty(node)) { + // @ts-expect-error todo(flow->ts): computed in not present on private properties if (node.computed && !this.isPure(node.key, constantsOnly)) return false; return this.isPure(node.value, constantsOnly); } else if (t.isUnaryExpression(node)) { @@ -743,7 +815,7 @@ export default class Scope { this.isPure(node.quasi, constantsOnly) ); } else if (t.isTemplateLiteral(node)) { - for (const expression of (node.expressions: Array)) { + for (const expression of node.expressions) { if (!this.isPure(expression, constantsOnly)) return false; } return true; @@ -756,7 +828,7 @@ export default class Scope { * Set some arbitrary data on the current scope. */ - setData(key, val) { + setData(key: string, val: any) { return (this.data[key] = val); } @@ -764,8 +836,8 @@ export default class Scope { * Recursively walk up scope tree looking for the data `key`. */ - getData(key) { - let scope = this; + getData(key: string): any { + let scope: Scope = this; do { const data = scope.data[key]; if (data != null) return data; @@ -777,8 +849,8 @@ export default class Scope { * remove it. */ - removeData(key) { - let scope = this; + removeData(key: string) { + let scope: Scope = this; do { const data = scope.data[key]; if (data != null) scope.data[key] = null; @@ -820,7 +892,7 @@ export default class Scope { const programParent = this.getProgramParent(); if (programParent.crawling) return; - const state = { + const state: CollectVisitorState = { references: [], constantViolations: [], assignments: [], @@ -860,11 +932,11 @@ export default class Scope { } push(opts: { - id: Object, - init: ?Object, - unique: ?boolean, - _blockHoist: ?number, - kind: "var" | "let", + id: t.LVal; + init?: t.Expression; + unique?: boolean; + _blockHoist?: number | undefined; + kind?: "var" | "let"; }) { let path = this.path; @@ -878,6 +950,7 @@ export default class Scope { if (path.isLoop() || path.isCatchClause() || path.isFunction()) { path.ensureBlock(); + // @ts-expect-error todo(flow->ts): improve types path = path.get("body"); } @@ -890,6 +963,7 @@ export default class Scope { if (!declarPath) { const declar = t.variableDeclaration(kind, []); + // @ts-expect-error todo(flow->ts): avoid modifying nodes declar._blockHoist = blockHoist; [declarPath] = path.unshiftContainer("body", [declar]); @@ -906,7 +980,7 @@ export default class Scope { */ getProgramParent() { - let scope = this; + let scope: Scope = this; do { if (scope.path.isProgram()) { return scope; @@ -919,8 +993,8 @@ export default class Scope { * Walk up the scope tree until we hit either a Function or return null. */ - getFunctionParent() { - let scope = this; + getFunctionParent(): Scope | null { + let scope: Scope = this; do { if (scope.path.isFunctionParent()) { return scope; @@ -935,7 +1009,7 @@ export default class Scope { */ getBlockParent() { - let scope = this; + let scope: Scope = this; do { if (scope.path.isBlockParent()) { return scope; @@ -950,10 +1024,10 @@ export default class Scope { * Walks the scope tree and gathers **all** bindings. */ - getAllBindings(): Object { + getAllBindings(): any { const ids = Object.create(null); - let scope = this; + let scope: Scope = this; do { for (const key of Object.keys(scope.bindings)) { if (key in ids === false) { @@ -970,11 +1044,11 @@ export default class Scope { * Walks the scope tree and gathers all declarations of `kind`. */ - getAllBindingsOfKind(): Object { + getAllBindingsOfKind(...kinds: string[]): any { const ids = Object.create(null); - for (const kind of (arguments: Array)) { - let scope = this; + for (const kind of kinds) { + let scope: Scope = this; do { for (const name of Object.keys(scope.bindings)) { const binding = scope.bindings[name]; @@ -987,12 +1061,12 @@ export default class Scope { return ids; } - bindingIdentifierEquals(name: string, node: Object): boolean { + bindingIdentifierEquals(name: string, node: t.Node): boolean { return this.getBindingIdentifier(name) === node; } - getBinding(name: string) { - let scope = this; + getBinding(name: string): Binding | undefined { + let scope: Scope = this; let previousPath; do { @@ -1016,15 +1090,17 @@ export default class Scope { } while ((scope = scope.parent)); } - getOwnBinding(name: string) { + getOwnBinding(name: string): Binding | undefined { return this.bindings[name]; } - getBindingIdentifier(name: string) { + // todo: return probably can be undefined… + getBindingIdentifier(name: string): t.Identifier { return this.getBinding(name)?.identifier; } - getOwnBindingIdentifier(name: string) { + // todo: flow->ts return probably can be undefined + getOwnBindingIdentifier(name: string): t.Identifier { const binding = this.bindings[name]; return binding?.identifier; } @@ -1051,7 +1127,7 @@ export default class Scope { * Move a binding of `name` to another `scope`. */ - moveBindingTo(name, scope) { + moveBindingTo(name: string, scope: Scope) { const info = this.getBinding(name); if (info) { info.scope.removeOwnBinding(name); @@ -1069,7 +1145,7 @@ export default class Scope { this.getBinding(name)?.scope.removeOwnBinding(name); // clear uids with this name - https://github.com/babel/babel/issues/2101 - let scope = this; + let scope: Scope = this; do { if (scope.uids[name]) { scope.uids[name] = false; diff --git a/packages/babel-traverse/src/scope/lib/renamer.js b/packages/babel-traverse/src/scope/lib/renamer.ts similarity index 95% rename from packages/babel-traverse/src/scope/lib/renamer.js rename to packages/babel-traverse/src/scope/lib/renamer.ts index 2b24795e17..28f753ae59 100644 --- a/packages/babel-traverse/src/scope/lib/renamer.js +++ b/packages/babel-traverse/src/scope/lib/renamer.ts @@ -1,8 +1,9 @@ import Binding from "../binding"; import splitExportDeclaration from "@babel/helper-split-export-declaration"; import * as t from "@babel/types"; +import type { Visitor } from "../../types"; -const renameVisitor = { +const renameVisitor: Visitor = { ReferencedIdentifier({ node }, state) { if (node.name === state.oldName) { node.name = state.newName; @@ -135,11 +136,6 @@ export default class Renamer { this.binding.identifier.name = newName; } - if (binding.type === "hoisted") { - // https://github.com/babel/babel/issues/2435 - // todo: hoist and convert function to a let - } - if (parentDeclar) { this.maybeConvertFromClassFunctionDeclaration(parentDeclar); this.maybeConvertFromClassFunctionExpression(parentDeclar); diff --git a/packages/babel-traverse/src/types.ts b/packages/babel-traverse/src/types.ts new file mode 100644 index 0000000000..2114cd97ad --- /dev/null +++ b/packages/babel-traverse/src/types.ts @@ -0,0 +1,31 @@ +import * as t from "@babel/types"; +import { NodePath } from "./index"; +import { VirtualTypeAliases } from "./path/generated/virtual-types"; + +export type Visitor = VisitNodeObject & + { + [Type in t.Node["type"]]?: VisitNode>; + } & + { + [K in keyof t.Aliases]?: VisitNode; + } & + { + [K in keyof VirtualTypeAliases]?: VisitNode; + } & { + [k: string]: VisitNode; + }; + +export type VisitNode = + | VisitNodeFunction + | VisitNodeObject; + +export type VisitNodeFunction = ( + this: S, + path: NodePath

, + state: S, +) => void; + +export interface VisitNodeObject { + enter?: VisitNodeFunction; + exit?: VisitNodeFunction; +} diff --git a/packages/babel-traverse/src/visitors.js b/packages/babel-traverse/src/visitors.ts similarity index 95% rename from packages/babel-traverse/src/visitors.js rename to packages/babel-traverse/src/visitors.ts index 51908c7beb..5faaf38668 100644 --- a/packages/babel-traverse/src/visitors.js +++ b/packages/babel-traverse/src/visitors.ts @@ -50,7 +50,7 @@ export function explode(visitor) { ensureCallbackArrays(visitor); // add type wrappers - for (const nodeType of (Object.keys(visitor): Array)) { + for (const nodeType of Object.keys(visitor)) { if (shouldIgnoreKey(nodeType)) continue; const wrapper = virtualTypes[nodeType]; @@ -66,7 +66,7 @@ export function explode(visitor) { delete visitor[nodeType]; if (wrapper.types) { - for (const type of (wrapper.types: Array)) { + for (const type of wrapper.types) { // merge the visitor if necessary or just put it back in if (visitor[type]) { mergePair(visitor[type], fns); @@ -85,7 +85,7 @@ export function explode(visitor) { const fns = visitor[nodeType]; - let aliases: ?Array = t.FLIPPED_ALIAS_KEYS[nodeType]; + let aliases: Array | undefined = t.FLIPPED_ALIAS_KEYS[nodeType]; const deprecratedKey = t.DEPRECATED_KEYS[nodeType]; if (deprecratedKey) { @@ -176,9 +176,9 @@ function validateVisitorMethods(path, val) { } export function merge( - visitors: Array, - states: Array = [], - wrapper?: ?Function, + visitors: any[], + states: any[] = [], + wrapper?: Function | null, ) { const rootVisitor = {}; @@ -204,7 +204,7 @@ export function merge( return rootVisitor; } -function wrapWithStateOrWrapper(oldVisitor, state, wrapper: ?Function) { +function wrapWithStateOrWrapper(oldVisitor, state, wrapper?: Function | null) { const newVisitor = {}; for (const key of Object.keys(oldVisitor)) { diff --git a/packages/babel-traverse/test/modification.js b/packages/babel-traverse/test/modification.js index d26e4e68ad..fdac086a47 100644 --- a/packages/babel-traverse/test/modification.js +++ b/packages/babel-traverse/test/modification.js @@ -182,9 +182,9 @@ describe("modification", function () { const tagName = path.node.openingElement.name.name; if (tagName !== "span") return; path.insertBefore( - t.JSXElement( - t.JSXOpeningElement(t.JSXIdentifier("div"), [], false), - t.JSXClosingElement(t.JSXIdentifier("div")), + t.jsxElement( + t.jsxOpeningElement(t.jsxIdentifier("div"), [], false), + t.jsxClosingElement(t.jsxIdentifier("div")), [], ), ); @@ -292,9 +292,9 @@ describe("modification", function () { const tagName = path.node.openingElement.name.name; if (tagName !== "span") return; path.insertAfter( - t.JSXElement( - t.JSXOpeningElement(t.JSXIdentifier("div"), [], false), - t.JSXClosingElement(t.JSXIdentifier("div")), + t.jsxElement( + t.jsxOpeningElement(t.jsxIdentifier("div"), [], false), + t.jsxClosingElement(t.jsxIdentifier("div")), [], ), ); diff --git a/packages/babel-types/src/traverse/traverse.ts b/packages/babel-types/src/traverse/traverse.ts index 43e0c47f6e..cfebda4d9e 100644 --- a/packages/babel-types/src/traverse/traverse.ts +++ b/packages/babel-types/src/traverse/traverse.ts @@ -40,9 +40,9 @@ export default function traverse( function traverseSimpleImpl( node: any, - enter: Function | undefined | null, - exit: Function | undefined | null, - state: T | undefined | null, + enter: Function | undefined, + exit: Function | undefined, + state: T | undefined, ancestors: TraversalAncestors, ) { const keys = VISITOR_KEYS[node.type]; diff --git a/packages/babel-types/src/validators/isType.ts b/packages/babel-types/src/validators/isType.ts index 646675c943..22f1352c2f 100644 --- a/packages/babel-types/src/validators/isType.ts +++ b/packages/babel-types/src/validators/isType.ts @@ -14,10 +14,7 @@ export default function isType( /** * Test if a `nodeType` is a `targetType` or if `targetType` is an alias of `nodeType`. */ -export default function isType( - nodeType: string | undefined | null, - targetType: string, -): boolean { +export default function isType(nodeType: string, targetType: string): boolean { if (nodeType === targetType) return true; // This is a fast-path. If the test above failed, but an alias key is found, then the