diff --git a/packages/babel-helper-builder-binary-assignment-operator-visitor/src/index.ts b/packages/babel-helper-builder-binary-assignment-operator-visitor/src/index.ts index 7cc8513ac3ed..a98fd672aea5 100644 --- a/packages/babel-helper-builder-binary-assignment-operator-visitor/src/index.ts +++ b/packages/babel-helper-builder-binary-assignment-operator-visitor/src/index.ts @@ -3,7 +3,13 @@ import { assignmentExpression, sequenceExpression } from "@babel/types"; import type { Visitor } from "@babel/traverse"; import type * as t from "@babel/types"; -export default function (opts: { build: Function; operator: string }) { +export default function (opts: { + build: ( + left: t.Expression | t.PrivateName, + right: t.Expression, + ) => t.Expression; + operator: t.BinaryExpression["operator"]; +}) { const { build, operator } = opts; const visitor: Visitor = { @@ -12,7 +18,7 @@ export default function (opts: { build: Function; operator: string }) { if (node.operator !== operator + "=") return; const nodes: t.AssignmentExpression[] = []; - // @ts-expect-error todo(flow->ts) + // @ts-expect-error Fixme: node.left can be a TSAsExpression const exploded = explode(node.left, nodes, this, scope); nodes.push( assignmentExpression( diff --git a/packages/babel-helper-member-expression-to-functions/src/index.ts b/packages/babel-helper-member-expression-to-functions/src/index.ts index 3b8867711a50..efaf7293366c 100644 --- a/packages/babel-helper-member-expression-to-functions/src/index.ts +++ b/packages/babel-helper-member-expression-to-functions/src/index.ts @@ -25,7 +25,7 @@ import type * as t from "@babel/types"; import { willPathCastToBoolean } from "./util"; class AssignmentMemoiser { - private _map: WeakMap; + private _map: WeakMap; constructor() { this._map = new WeakMap(); } @@ -49,7 +49,7 @@ class AssignmentMemoiser { return value; } - set(key: t.Expression, value: t.LVal, count: number) { + set(key: t.Expression, value: t.Identifier, count: number) { return this._map.set(key, { count, value }); } } @@ -66,11 +66,12 @@ function toNonOptional( if (path.isOptionalCallExpression()) { const callee = path.get("callee"); if (path.node.optional && callee.isOptionalMemberExpression()) { - const { object } = callee.node; - const context = path.scope.maybeGenerateMemoised(object) || object; + // object must be a conditional expression because the optional private access in object has been transformed + const object = callee.node.object as t.ConditionalExpression; + const context = path.scope.maybeGenerateMemoised(object); callee .get("object") - .replaceWith(assignmentExpression("=", context as t.LVal, object)); + .replaceWith(assignmentExpression("=", context, object)); return callExpression(memberExpression(base, identifier("call")), [ context, diff --git a/packages/babel-helper-replace-supers/src/index.ts b/packages/babel-helper-replace-supers/src/index.ts index bb0135a9ae6b..6bb6db81cd58 100644 --- a/packages/babel-helper-replace-supers/src/index.ts +++ b/packages/babel-helper-replace-supers/src/index.ts @@ -24,6 +24,12 @@ export { skipAllButComputedKey, } from "@babel/helper-environment-visitor"; +type ThisRef = + | { + memo: t.AssignmentExpression; + this: t.Identifier; + } + | { this: t.ThisExpression }; /** * Creates an expression which result is the proto of objectRef. * @@ -35,7 +41,12 @@ export { * * helpers.getPrototypeOf(CLASS.prototype) */ -function getPrototypeOfExpression(objectRef, isStatic, file, isPrivateMethod) { +function getPrototypeOfExpression( + objectRef: t.Identifier, + isStatic: boolean, + file: File, + isPrivateMethod: boolean, +) { objectRef = cloneNode(objectRef); const targetRef = isStatic || isPrivateMethod @@ -79,8 +90,8 @@ type SharedState = { isDerivedConstructor: boolean; isStatic: boolean; isPrivateMethod: boolean; - getObjectRef: Function; - getSuperRef: Function; + getObjectRef: () => t.Identifier; + getSuperRef: () => t.Identifier; // we dont need boundGet here, but memberExpressionToFunctions handler needs it. boundGet: HandlerState["get"]; }; @@ -93,11 +104,25 @@ type SuperMember = NodePath< } >; -const specHandlers = { +interface SpecHandler + extends Pick< + Handler, + "get" | "set" | "destructureSet" | "call" | "optionalCall" | "memoise" + > { + _get( + this: Handler & SpecHandler, + superMember: SuperMember, + thisRefs: ThisRef, + ): t.CallExpression; + _getThisRefs(): ThisRef; + prop(this: Handler & SpecHandler, superMember: SuperMember): t.Expression; +} + +const specHandlers: SpecHandler = { memoise( - this: Handler & typeof specHandlers, + this: Handler & SpecHandler, superMember: SuperMember, - count, + count: number, ) { const { scope, node } = superMember; const { computed, property } = node; @@ -113,7 +138,7 @@ const specHandlers = { this.memoiser.set(property, memo, count); }, - prop(this: Handler & typeof specHandlers, superMember: SuperMember) { + prop(this: Handler & SpecHandler, superMember: SuperMember) { const { computed, property } = superMember.node; if (this.memoiser.has(property)) { return cloneNode(this.memoiser.get(property)); @@ -126,14 +151,14 @@ const specHandlers = { return stringLiteral((property as t.Identifier).name); }, - get(this: Handler & typeof specHandlers, superMember: SuperMember) { + get(this: Handler & SpecHandler, superMember: SuperMember) { return this._get(superMember, this._getThisRefs()); }, _get( - this: Handler & typeof specHandlers, + this: Handler & SpecHandler, superMember: SuperMember, - thisRefs, + thisRefs: ThisRef, ) { const proto = getPrototypeOfExpression( this.getObjectRef(), @@ -142,13 +167,14 @@ const specHandlers = { this.isPrivateMethod, ); return callExpression(this.file.addHelper("get"), [ + // @ts-ignore memo does not exist when this.isDerivedConstructor is false thisRefs.memo ? sequenceExpression([thisRefs.memo, proto]) : proto, this.prop(superMember), thisRefs.this, ]); }, - _getThisRefs(this: Handler & typeof specHandlers) { + _getThisRefs(this: Handler & SpecHandler): ThisRef { if (!this.isDerivedConstructor) { return { this: thisExpression() }; } @@ -159,7 +185,11 @@ const specHandlers = { }; }, - set(this: Handler & typeof specHandlers, superMember: SuperMember, value) { + set( + this: Handler & SpecHandler, + superMember: SuperMember, + value: t.Expression, + ) { const thisRefs = this._getThisRefs(); const proto = getPrototypeOfExpression( this.getObjectRef(), @@ -168,6 +198,7 @@ const specHandlers = { this.isPrivateMethod, ); return callExpression(this.file.addHelper("set"), [ + // @ts-ignore memo does not exist when this.isDerivedConstructor is false thisRefs.memo ? sequenceExpression([thisRefs.memo, proto]) : proto, this.prop(superMember), value, @@ -176,16 +207,17 @@ const specHandlers = { ]); }, - destructureSet( - this: Handler & typeof specHandlers, - superMember: SuperMember, - ) { + destructureSet(this: Handler & SpecHandler, superMember: SuperMember) { throw superMember.buildCodeFrameError( `Destructuring to a super field is not supported yet.`, ); }, - call(this: Handler & typeof specHandlers, superMember: SuperMember, args) { + call( + this: Handler & SpecHandler, + superMember: SuperMember, + args: t.CallExpression["arguments"], + ) { const thisRefs = this._getThisRefs(); return optimiseCall( this._get(superMember, thisRefs), @@ -196,9 +228,9 @@ const specHandlers = { }, optionalCall( - this: Handler & typeof specHandlers, + this: Handler & SpecHandler, superMember: SuperMember, - args, + args: t.CallExpression["arguments"], ) { const thisRefs = this._getThisRefs(); return optimiseCall( @@ -242,7 +274,11 @@ const looseHandlers = { return memberExpression(object, prop, computed); }, - set(this: Handler & typeof specHandlers, superMember: SuperMember, value) { + set( + this: Handler & typeof specHandlers, + superMember: SuperMember, + value: t.Expression, + ) { const { computed } = superMember.node; const prop = this.prop(superMember); @@ -263,14 +299,18 @@ const looseHandlers = { return memberExpression(thisExpression(), prop, computed); }, - call(this: Handler & typeof specHandlers, superMember: SuperMember, args) { + call( + this: Handler & typeof specHandlers, + superMember: SuperMember, + args: t.CallExpression["arguments"], + ) { return optimiseCall(this.get(superMember), thisExpression(), args, false); }, optionalCall( this: Handler & typeof specHandlers, superMember: SuperMember, - args, + args: t.CallExpression["arguments"], ) { return optimiseCall(this.get(superMember), thisExpression(), args, true); }, diff --git a/packages/babel-plugin-proposal-async-generator-functions/src/for-await.ts b/packages/babel-plugin-proposal-async-generator-functions/src/for-await.ts index c3df45b3f604..fce810c5e9bc 100644 --- a/packages/babel-plugin-proposal-async-generator-functions/src/for-await.ts +++ b/packages/babel-plugin-proposal-async-generator-functions/src/for-await.ts @@ -1,4 +1,5 @@ import { types as t, template } from "@babel/core"; +import type { NodePath } from "@babel/traverse"; const buildForAwait = template(` async function wrapper() { @@ -29,7 +30,10 @@ const buildForAwait = template(` } `); -export default function (path, { getAsyncIterator }) { +export default function ( + path: NodePath, + { getAsyncIterator }: { getAsyncIterator: t.Identifier }, +) { const { node, scope, parent } = path; const stepKey = scope.generateUidIdentifier("step"); diff --git a/packages/babel-plugin-proposal-class-static-block/src/index.ts b/packages/babel-plugin-proposal-class-static-block/src/index.ts index a31a0288c078..ed596bd04468 100644 --- a/packages/babel-plugin-proposal-class-static-block/src/index.ts +++ b/packages/babel-plugin-proposal-class-static-block/src/index.ts @@ -1,5 +1,6 @@ import { declare } from "@babel/helper-plugin-utils"; import syntaxClassStaticBlock from "@babel/plugin-syntax-class-static-block"; +import type { Scope } from "@babel/traverse"; import { enableFeature, @@ -10,11 +11,11 @@ import type * as t from "@babel/types"; /** * Generate a uid that is not in `denyList` * - * @param {*} scope + * @param {Scope} scope * @param {Set} denyList a deny list that the generated uid should avoid * @returns */ -function generateUid(scope, denyList: Set) { +function generateUid(scope: Scope, denyList: Set) { const name = ""; let uid; let i = 1; diff --git a/packages/babel-plugin-proposal-function-bind/src/index.ts b/packages/babel-plugin-proposal-function-bind/src/index.ts index 4a26c705cf2f..739317a08ae5 100644 --- a/packages/babel-plugin-proposal-function-bind/src/index.ts +++ b/packages/babel-plugin-proposal-function-bind/src/index.ts @@ -1,11 +1,12 @@ import { declare } from "@babel/helper-plugin-utils"; import syntaxFunctionBind from "@babel/plugin-syntax-function-bind"; import { types as t } from "@babel/core"; +import type { Scope } from "@babel/traverse"; export default declare(api => { api.assertVersion(7); - function getTempId(scope) { + function getTempId(scope: Scope) { let id = scope.path.getData("functionBind"); if (id) return t.cloneNode(id); @@ -13,15 +14,18 @@ export default declare(api => { return scope.path.setData("functionBind", id); } - function getStaticContext(bind, scope) { - const object = bind.object || bind.callee.object; + function getStaticContext(bind: t.BindExpression, scope: Scope) { + const object = + bind.object || + // @ts-ignore Fixme: should check bind.callee type first + bind.callee.object; return ( scope.isStatic(object) && (t.isSuper(object) ? t.thisExpression() : object) ); } - function inferBindContext(bind, scope) { + function inferBindContext(bind: t.BindExpression, scope: Scope) { const staticContext = getStaticContext(bind, scope); if (staticContext) return t.cloneNode(staticContext); @@ -32,9 +36,11 @@ export default declare(api => { bind.callee, ]); } else { + // @ts-ignore Fixme: should check bind.callee type first bind.callee.object = t.assignmentExpression( "=", tempId, + // @ts-ignore Fixme: should check bind.callee type first bind.callee.object, ); } diff --git a/packages/babel-plugin-proposal-function-sent/src/index.ts b/packages/babel-plugin-proposal-function-sent/src/index.ts index b9b45b5fd2b9..98d43139798e 100644 --- a/packages/babel-plugin-proposal-function-sent/src/index.ts +++ b/packages/babel-plugin-proposal-function-sent/src/index.ts @@ -7,11 +7,14 @@ import type { Visitor } from "@babel/traverse"; export default declare(api => { api.assertVersion(7); - const isFunctionSent = node => + const isFunctionSent = (node: t.MetaProperty) => t.isIdentifier(node.meta, { name: "function" }) && t.isIdentifier(node.property, { name: "sent" }); - const hasBeenReplaced = (node, sentId) => + const hasBeenReplaced = ( + node: t.Node, + sentId: string, + ): node is t.AssignmentExpression => t.isAssignmentExpression(node) && t.isIdentifier(node.left, { name: sentId }); diff --git a/packages/babel-plugin-proposal-json-strings/src/index.ts b/packages/babel-plugin-proposal-json-strings/src/index.ts index 10e1c7bc9df0..039ac2fa4dae 100644 --- a/packages/babel-plugin-proposal-json-strings/src/index.ts +++ b/packages/babel-plugin-proposal-json-strings/src/index.ts @@ -6,7 +6,7 @@ import type { NodePath } from "@babel/traverse"; export default declare(api => { api.assertVersion(7); const regex = /(\\*)([\u2028\u2029])/g; - function replace(match, escapes, separator) { + function replace(match: string, escapes: string, separator: string) { // If there's an odd number, that means the separator itself was escaped. // "\X" escapes X. // "\\X" escapes the backslash, so X is unescaped. diff --git a/packages/babel-plugin-proposal-partial-application/src/index.ts b/packages/babel-plugin-proposal-partial-application/src/index.ts index 6f88b628258d..29ba99d14eca 100644 --- a/packages/babel-plugin-proposal-partial-application/src/index.ts +++ b/packages/babel-plugin-proposal-partial-application/src/index.ts @@ -1,6 +1,7 @@ import { declare } from "@babel/helper-plugin-utils"; import syntaxPartialApplication from "@babel/plugin-syntax-partial-application"; import { types as t } from "@babel/core"; +import type { Scope } from "@babel/traverse"; export default declare(api => { api.assertVersion(7); @@ -11,56 +12,67 @@ export default declare(api => { * @param node a callExpression node * @returns boolean */ - function hasArgumentPlaceholder(node) { + function hasArgumentPlaceholder(node: t.CallExpression) { return node.arguments.some(arg => t.isArgumentPlaceholder(arg)); } - function unwrapArguments(node, scope) { - const init = []; - for (let i = 0; i < node.arguments.length; i++) { - if ( - !t.isArgumentPlaceholder(node.arguments[i]) && - !t.isImmutable(node.arguments[i]) - ) { - const id = scope.generateUidIdentifierBasedOnNode( - node.arguments[i], - "param", - ); + function unwrapArguments( + { arguments: args }: t.CallExpression, + scope: Scope, + ) { + const init: t.AssignmentExpression[] = []; + for (let i = 0; i < args.length; i++) { + const node = args[i]; + if (!t.isArgumentPlaceholder(node) && !t.isImmutable(node)) { + const id = scope.generateUidIdentifierBasedOnNode(node, "param"); scope.push({ id }); - if (t.isSpreadElement(node.arguments[i])) { + if (t.isSpreadElement(node)) { init.push( t.assignmentExpression( "=", t.cloneNode(id), - t.arrayExpression([t.spreadElement(node.arguments[i].argument)]), + t.arrayExpression([t.spreadElement(node.argument)]), ), ); - node.arguments[i].argument = t.cloneNode(id); + node.argument = t.cloneNode(id); } else { init.push( - t.assignmentExpression("=", t.cloneNode(id), node.arguments[i]), + t.assignmentExpression( + "=", + t.cloneNode(id), + // @ts-ignore Fixme: may need to handle JSXNamespacedName here + node, + ), ); - node.arguments[i] = t.cloneNode(id); + args[i] = t.cloneNode(id); } } } return init; } - function replacePlaceholders(node, scope) { - const placeholders = []; - const args = []; + type CallArgsWithoutPlaceholder = Exclude< + t.CallExpression["arguments"][number], + t.ArgumentPlaceholder + >[]; + + function replacePlaceholders( + node: t.CallExpression, + scope: Scope, + ): [t.Identifier[], CallArgsWithoutPlaceholder] { + const placeholders: t.Identifier[] = []; + const newArgs: CallArgsWithoutPlaceholder = []; node.arguments.forEach(arg => { if (t.isArgumentPlaceholder(arg)) { const id = scope.generateUid("_argPlaceholder"); placeholders.push(t.identifier(id)); - args.push(t.identifier(id)); + newArgs.push(t.identifier(id)); } else { - args.push(arg); + newArgs.push(arg); } }); - return [placeholders, args]; + return [placeholders, newArgs]; } return { diff --git a/packages/babel-plugin-proposal-private-property-in-object/src/index.ts b/packages/babel-plugin-proposal-private-property-in-object/src/index.ts index fb6384e05232..c359eedcd193 100644 --- a/packages/babel-plugin-proposal-private-property-in-object/src/index.ts +++ b/packages/babel-plugin-proposal-private-property-in-object/src/index.ts @@ -7,7 +7,7 @@ import { } from "@babel/helper-create-class-features-plugin"; import annotateAsPure from "@babel/helper-annotate-as-pure"; import type * as t from "@babel/types"; -import type { NodePath } from "@babel/traverse"; +import type { NodePath, Scope } from "@babel/traverse"; export interface Options { loose?: boolean; @@ -23,29 +23,40 @@ export default declare((api, opt: Options) => { // The visitor of this plugin is only effective when not compiling // private fields and methods. - const classWeakSets = new WeakMap(); - const fieldsWeakSets = new WeakMap(); + const classWeakSets: WeakMap = new WeakMap(); + const fieldsWeakSets: WeakMap< + t.ClassPrivateProperty | t.ClassPrivateMethod, + t.Identifier + > = new WeakMap(); - function unshadow(name, targetScope, scope) { + function unshadow(name: string, targetScope: Scope, scope: Scope) { while (scope !== targetScope) { if (scope.hasOwnBinding(name)) scope.rename(name); scope = scope.parent; } } - function injectToFieldInit(fieldPath, expr, before = false) { + function injectToFieldInit( + fieldPath: NodePath, + expr: t.Expression, + before = false, + ) { if (fieldPath.node.value) { + const value = fieldPath.get("value"); if (before) { - fieldPath.get("value").insertBefore(expr); + value.insertBefore(expr); } else { - fieldPath.get("value").insertAfter(expr); + value.insertAfter(expr); } } else { fieldPath.set("value", t.unaryExpression("void", expr)); } } - function injectInitialization(classPath, init) { + function injectInitialization( + classPath: NodePath, + init: t.Expression, + ) { let firstFieldPath; let consturctorPath; @@ -71,12 +82,22 @@ export default declare((api, opt: Options) => { } } - function getWeakSetId(weakSets, outerClass, reference, name = "", inject) { - let id = classWeakSets.get(reference.node); + function getWeakSetId( + weakSets: WeakMap, + outerClass: NodePath, + reference: NodePath, + name = "", + inject: ( + reference: NodePath, + expression: t.Expression, + before?: boolean, + ) => void, + ) { + let id = weakSets.get(reference.node); if (!id) { id = outerClass.scope.generateUidIdentifier(`${name || ""} brandCheck`); - classWeakSets.set(reference.node, id); + weakSets.set(reference.node, id); inject(reference, template.expression.ast`${t.cloneNode(id)}.add(this)`); @@ -105,13 +126,17 @@ export default declare((api, opt: Options) => { const { name } = node.left.id; - let privateElement; + let privateElement: NodePath< + t.ClassPrivateMethod | t.ClassPrivateProperty + >; const outerClass = path.findParent(path => { if (!path.isClass()) return false; - privateElement = path - .get("body.body") - .find(({ node }) => t.isPrivate(node) && node.key.id.name === name); + privateElement = path.get("body.body").find( + ({ node }) => + // fixme: Support class accessor property + t.isPrivate(node) && node.key.id.name === name, + ) as NodePath; return !!privateElement; }) as NodePath; @@ -124,7 +149,7 @@ export default declare((api, opt: Options) => { return; } - if (privateElement.isMethod()) { + if (privateElement.node.type === "ClassPrivateMethod") { if (privateElement.node.static) { if (outerClass.node.id) { unshadow(outerClass.node.id.name, outerClass.scope, path.scope); @@ -153,10 +178,10 @@ export default declare((api, opt: Options) => { // Private fields might not all be initialized: see the 'halfConstructed' // example at https://v8.dev/features/private-brand-checks. - const id = getWeakSetId( + const id = getWeakSetId( fieldsWeakSets, outerClass, - privateElement, + privateElement as NodePath, privateElement.node.key.id.name, injectToFieldInit, ); diff --git a/packages/babel-plugin-proposal-record-and-tuple/src/index.ts b/packages/babel-plugin-proposal-record-and-tuple/src/index.ts index 2c148a3451f8..b41c5197f63d 100644 --- a/packages/babel-plugin-proposal-record-and-tuple/src/index.ts +++ b/packages/babel-plugin-proposal-record-and-tuple/src/index.ts @@ -34,6 +34,10 @@ type State = { programPath: NodePath; }; +// program -> cacheKey -> localBindingName +type Cache = Map; +type ImportCache = WeakMap; + export default declare((api, options: Options) => { api.assertVersion(7); @@ -48,16 +52,28 @@ export default declare((api, options: Options) => { !!options.polyfillModuleName, ); - // program -> cacheKey -> localBindingName - const importCaches = new WeakMap(); + const importCaches: ImportCache = new WeakMap(); - function getOr(map, key, getDefault) { + function getOr(map: Map, key: K, getDefault: () => V): V; + function getOr( + map: WeakMap, + key: K, + getDefault: () => V, + ): V; + function getOr( + map: WeakMap, + key: K, + getDefault: () => V, + ) { let value = map.get(key); if (!value) map.set(key, (value = getDefault())); return value; } - function getBuiltIn(name, programPath) { + function getBuiltIn( + name: "Record" | "Tuple", + programPath: NodePath, + ) { if (!shouldImportPolyfill) return t.identifier(name); if (!programPath) { throw new Error("Internal error: unable to find the Program node."); @@ -65,7 +81,11 @@ export default declare((api, options: Options) => { const cacheKey = `${name}:${isModule(programPath)}`; - const cache = getOr(importCaches, programPath.node, () => new Map()); + const cache = getOr( + importCaches, + programPath.node, + () => new Map(), + ); const localBindingName = getOr(cache, cacheKey, () => { return addNamed(programPath, name, polyfillModuleName, { importedInterop: "uncompiled", diff --git a/packages/babel-plugin-syntax-typescript/src/index.ts b/packages/babel-plugin-syntax-typescript/src/index.ts index 933b8e8c5d8a..01fb877d5a88 100644 --- a/packages/babel-plugin-syntax-typescript/src/index.ts +++ b/packages/babel-plugin-syntax-typescript/src/index.ts @@ -1,7 +1,8 @@ import { declare } from "@babel/helper-plugin-utils"; +import type { ParserPlugin } from "@babel/parser"; -function removePlugin(plugins, name) { - const indices = []; +function removePlugin(plugins: ParserPlugin[], name: string) { + const indices: number[] = []; plugins.forEach((plugin, i) => { const n = Array.isArray(plugin) ? plugin[0] : plugin; diff --git a/packages/babel-plugin-transform-block-scoped-functions/src/index.ts b/packages/babel-plugin-transform-block-scoped-functions/src/index.ts index 1e392452232b..811645d8bfd0 100644 --- a/packages/babel-plugin-transform-block-scoped-functions/src/index.ts +++ b/packages/babel-plugin-transform-block-scoped-functions/src/index.ts @@ -1,16 +1,14 @@ import { declare } from "@babel/helper-plugin-utils"; import { types as t } from "@babel/core"; +import type { NodePath } from "@babel/traverse"; export default declare(api => { api.assertVersion(7); - function statementList(key, path) { - const paths = path.get(key); - + function transformStatementList(paths: NodePath[]) { for (const path of paths) { - const func = path.node; if (!path.isFunctionDeclaration()) continue; - + const func = path.node; const declar = t.variableDeclaration("let", [ t.variableDeclarator(func.id, t.toExpression(func)), ]); @@ -39,11 +37,11 @@ export default declare(api => { return; } - statementList("body", path); + transformStatementList(path.get("body")); }, SwitchCase(path) { - statementList("consequent", path); + transformStatementList(path.get("consequent")); }, }, }; diff --git a/packages/babel-plugin-transform-block-scoping/src/index.ts b/packages/babel-plugin-transform-block-scoping/src/index.ts index edf7daf82eb9..02a78b553b51 100644 --- a/packages/babel-plugin-transform-block-scoping/src/index.ts +++ b/packages/babel-plugin-transform-block-scoping/src/index.ts @@ -3,7 +3,7 @@ import type { NodePath, Visitor, Scope } from "@babel/traverse"; import { visitor as tdzVisitor } from "./tdz"; import type { TDZVisitorState } from "./tdz"; import { traverse, template, types as t } from "@babel/core"; -import type { File } from "@babel/core"; +import type { PluginPass } from "@babel/core"; const DONE = new WeakSet(); @@ -67,7 +67,7 @@ export default declare((api, opts: Options) => { path.ensureBlock(); const blockScoping = new BlockScoping( path, - path.get("body"), + path.get("body") as NodePath, parent, scope, throwIfClosureRequired, @@ -109,21 +109,27 @@ export default declare((api, opts: Options) => { blockScoping.run(); } }, - } as Visitor, + }, }; }); -function ignoreBlock(path) { +function ignoreBlock(path: NodePath) { return t.isLoop(path.parent) || t.isCatchClause(path.parent); } -const buildRetCheck = template(` +const buildRetCheck = template.statement(` if (typeof RETURN === "object") return RETURN.v; `); -function isBlockScoped(node) { +function isBlockScoped(node: t.Node): node is t.VariableDeclaration { if (!t.isVariableDeclaration(node)) return false; - if (node[t.BLOCK_SCOPED_SYMBOL]) return true; + if ( + // @ts-expect-error Fixme: document symbol properties + node[t.BLOCK_SCOPED_SYMBOL] + ) { + return true; + } + if (node.kind !== "let" && node.kind !== "const") return false; return true; } @@ -132,7 +138,7 @@ function isBlockScoped(node) { * If there is a loop ancestor closer than the closest function, we * consider ourselves to be in a loop. */ -function isInLoop(path) { +function isInLoop(path: NodePath) { const loopOrFunctionParent = path.find( path => path.isLoop() || path.isFunction(), ); @@ -141,10 +147,10 @@ function isInLoop(path) { } function convertBlockScopedToVar( - path, - node, - parent, - scope, + path: NodePath, + node: t.VariableDeclaration, + parent: t.Node, + scope: Scope, moveBindingsToParent = false, ) { if (!node) { @@ -159,6 +165,7 @@ function convertBlockScopedToVar( } } + // @ts-expect-error todo(flow->ts): avoid mutations node[t.BLOCK_SCOPED_SYMBOL] = true; node.kind = "var"; @@ -173,15 +180,12 @@ function convertBlockScopedToVar( } } -function isVar(node): node is t.VariableDeclaration { +function isVar(node: t.Node): node is t.VariableDeclaration { return t.isVariableDeclaration(node, { kind: "var" }) && !isBlockScoped(node); } interface LetReferenceVisitorState extends TDZVisitorState { - tdzEnabled: boolean; loopDepth: number; - addHelper: (name) => any; - letReferences: any; closurify: boolean; } @@ -260,12 +264,12 @@ const hoistVarDeclarationsVisitor: Visitor = { type LoopVisitorState = { inSwitchCase: boolean; hasBreakContinue: boolean; - innerLabels: any[]; + innerLabels: string[]; hasReturn: boolean; ignoreLabeless: boolean; loopIgnored: WeakSet; isLoop: boolean; - map: any; + map: Map; }; const loopLabelVisitor: Visitor = { @@ -298,7 +302,7 @@ const continuationVisitor: Visitor = { }, }; -function loopNodeTo(node) { +function loopNodeTo(node: t.Node) { if (t.isBreakStatement(node)) { return "break"; } else if (t.isContinueStatement(node)) { @@ -360,7 +364,7 @@ const loopVisitor: Visitor = { } state.hasBreakContinue = true; - state.map[loopText] = node; + state.map.set(loopText, node); replace = t.stringLiteral(loopText); } @@ -383,7 +387,7 @@ const loopVisitor: Visitor = { }, }; -function isStrict(path) { +function isStrict(path: NodePath) { return !!path.find(({ node }) => { if (t.isProgram(node)) { if (node.sourceType === "module") return true; @@ -396,31 +400,31 @@ function isStrict(path) { } class BlockScoping { - private parent: any; - private state: any; + private parent: t.Node; + private state: PluginPass; private scope: Scope; private throwIfClosureRequired: boolean; private tdzEnabled: boolean; - private blockPath: NodePath; - private block: t.Node; + private blockPath: NodePath; + private block: t.Block | t.SwitchStatement; private outsideLetReferences: Map; private hasLetReferences: boolean; - private letReferences: any; - private body: any[]; + private letReferences: Map; + private body: t.Statement[]; // todo(flow->ts) add more specific type private loopParent: t.Node; private loopLabel: t.Identifier; private loopPath: NodePath; private loop: t.Loop; - private has: any; + private has: LoopVisitorState; constructor( loopPath: NodePath | undefined | null, - blockPath: NodePath, - parent: any, + blockPath: NodePath, + parent: t.Node, scope: Scope, throwIfClosureRequired: boolean, tdzEnabled: boolean, - state: any, + state: PluginPass, ) { this.parent = parent; this.scope = scope; @@ -669,15 +673,14 @@ class BlockScoping { const args = Array.from(outsideRefs.values(), node => t.cloneNode(node)); const params = args.map(id => t.cloneNode(id)); - const isSwitch = this.blockPath.isSwitchStatement(); + const isSwitch = block.type === "SwitchStatement"; // build the closure that we're going to wrap the block with, possible wrapping switch(){} const fn = t.functionExpression( null, params, - // @ts-expect-error todo(flow->ts) improve block type annotations t.blockStatement(isSwitch ? [block] : block.body), - ); + ) as t.FunctionExpression & { params: t.Identifier[] }; // continuation this.addContinuations(fn); @@ -730,20 +733,19 @@ class BlockScoping { index = this.body.length - 1; } - let callPath; + let callPath: NodePath; // replace the current block body with the one we're going to build if (isSwitch) { const { parentPath, listKey, key } = this.blockPath; this.blockPath.replaceWithMultiple(this.body); - callPath = parentPath.get(listKey)[key + index]; + callPath = parentPath.get(listKey)[(key as number) + index]; } else { - // @ts-expect-error todo(flow->ts) improve block type annotations block.body = this.body; callPath = this.blockPath.get("body")[index]; } - const placeholder = callPath.get(placeholderPath); + const placeholder = callPath.get(placeholderPath) as NodePath; let fnPath; if (this.loop) { @@ -773,7 +775,7 @@ class BlockScoping { * Reference: https://github.com/babel/babel/issues/1078 */ - addContinuations(fn) { + addContinuations(fn: t.FunctionExpression & { params: t.Identifier[] }) { const state: ContinuationVisitorState = { reassignments: {}, returnStatements: [], @@ -834,17 +836,24 @@ class BlockScoping { } } - const addDeclarationsFromChild = (path, node) => { - node = node || path.node; + const addDeclarationsFromChild = ( + path: NodePath, + node: t.Statement | t.SwitchCase, + ) => { if ( t.isClassDeclaration(node) || t.isFunctionDeclaration(node) || isBlockScoped(node) ) { if (isBlockScoped(node)) { - convertBlockScopedToVar(path, node, block, this.scope); + convertBlockScopedToVar( + path as NodePath, + node, + block, + this.scope, + ); } - if (node.declarations) { + if (node.type === "VariableDeclaration") { for (let i = 0; i < node.declarations.length; i++) { declarators.push(node.declarations[i]); } @@ -857,22 +866,11 @@ class BlockScoping { } }; - // @ts-expect-error todo(flow->ts) check block node type instead - if (block.body) { - const declarPaths = this.blockPath.get("body"); - // @ts-expect-error todo(flow->ts) - for (let i = 0; i < block.body.length; i++) { - // @ts-expect-error todo(flow->ts) - addDeclarationsFromChild(declarPaths[i]); - } - } - - // @ts-expect-error todo(flow->ts) check block node type instead - if (block.cases) { - const declarPaths = this.blockPath.get("cases"); - // @ts-expect-error todo(flow->ts) + if (block.type === "SwitchStatement") { + const declarPaths = (this.blockPath as NodePath).get( + "cases", + ); for (let i = 0; i < block.cases.length; i++) { - // @ts-expect-error todo(flow->ts) const consequents = block.cases[i].consequent; for (let j = 0; j < consequents.length; j++) { @@ -880,6 +878,11 @@ class BlockScoping { addDeclarationsFromChild(declarPaths[i], declar); } } + } else { + const declarPaths = (this.blockPath as NodePath).get("body"); + for (let i = 0; i < block.body.length; i++) { + addDeclarationsFromChild(declarPaths[i], declarPaths[i].node); + } } // @@ -933,7 +936,7 @@ class BlockScoping { innerLabels: [], hasReturn: false, isLoop: !!this.loop, - map: {}, + map: new Map(), loopIgnored: new WeakSet(), }; @@ -988,11 +991,11 @@ class BlockScoping { const has = this.has; if (has.hasBreakContinue) { - for (const key of Object.keys(has.map)) { + for (const key of has.map.keys()) { body.push( t.ifStatement( t.binaryExpression("===", t.identifier(ret), t.stringLiteral(key)), - has.map[key], + has.map.get(key), ), ); } diff --git a/packages/babel-plugin-transform-block-scoping/src/tdz.ts b/packages/babel-plugin-transform-block-scoping/src/tdz.ts index c6ad42755634..5f3bdcbcb0d2 100644 --- a/packages/babel-plugin-transform-block-scoping/src/tdz.ts +++ b/packages/babel-plugin-transform-block-scoping/src/tdz.ts @@ -1,7 +1,10 @@ -import { types as t, template } from "@babel/core"; -import type { Visitor } from "@babel/traverse"; +import { types as t, template, type PluginPass } from "@babel/core"; +import type { NodePath, Scope, Visitor } from "@babel/traverse"; -function getTDZStatus(refPath, bindingPath) { +function getTDZStatus( + refPath: NodePath, + bindingPath: NodePath, +) { const executionStatus = bindingPath._guessExecutionStatusRelativeTo(refPath); if (executionStatus === "before") { @@ -13,14 +16,22 @@ function getTDZStatus(refPath, bindingPath) { } } -function buildTDZAssert(node, state) { +function buildTDZAssert( + node: t.Identifier | t.JSXIdentifier, + state: TDZVisitorState, +) { return t.callExpression(state.addHelper("temporalRef"), [ + // @ts-expect-error Fixme: we may need to handle JSXIdentifier node, t.stringLiteral(node.name), ]); } -function isReference(node, scope, state) { +function isReference( + node: t.Identifier | t.JSXIdentifier, + scope: Scope, + state: TDZVisitorState, +) { const declared = state.letReferences.get(node.name); if (!declared) return false; @@ -32,7 +43,8 @@ const visitedMaybeTDZNodes = new WeakSet(); export interface TDZVisitorState { tdzEnabled: boolean; - addHelper: (name) => any; + addHelper: PluginPass["addHelper"]; + letReferences: Map; } export const visitor: Visitor = { diff --git a/packages/babel-plugin-transform-computed-properties/src/index.ts b/packages/babel-plugin-transform-computed-properties/src/index.ts index efab06d1fbe2..06b89b7b9b05 100644 --- a/packages/babel-plugin-transform-computed-properties/src/index.ts +++ b/packages/babel-plugin-transform-computed-properties/src/index.ts @@ -1,10 +1,21 @@ import { declare } from "@babel/helper-plugin-utils"; -import { template, types as t } from "@babel/core"; +import { template, types as t, type PluginPass } from "@babel/core"; +import type { Scope } from "@babel/traverse"; export interface Options { loose?: boolean; } +type PropertyInfo = { + scope: Scope; + objId: t.Identifier; + body: t.Statement[]; + computedProps: t.ObjectMember[]; + initPropExpression: t.ObjectExpression; + getMutatorId: () => t.Identifier; + state: PluginPass; +}; + export default declare((api, options: Options) => { api.assertVersion(7); @@ -15,14 +26,21 @@ export default declare((api, options: Options) => { ? pushComputedPropsLoose : pushComputedPropsSpec; - const buildMutatorMapAssign = template(` + const buildMutatorMapAssign = template.statements(` MUTATOR_MAP_REF[KEY] = MUTATOR_MAP_REF[KEY] || {}; MUTATOR_MAP_REF[KEY].KIND = VALUE; `); - function getValue(prop) { + /** + * Get value of an object member under object expression. + * Returns a function expression if prop is a ObjectMethod. + * + * @param {t.ObjectMember} prop + * @returns t.Expression + */ + function getValue(prop: t.ObjectMember) { if (t.isObjectProperty(prop)) { - return prop.value; + return prop.value as t.Expression; } else if (t.isObjectMethod(prop)) { return t.functionExpression( null, @@ -34,28 +52,30 @@ export default declare((api, options: Options) => { } } - function pushAssign(objId, prop, body) { - if (prop.kind === "get" && prop.kind === "set") { - pushMutatorDefine(objId, prop); - } else { - body.push( - t.expressionStatement( - t.assignmentExpression( - "=", - t.memberExpression( - t.cloneNode(objId), - prop.key, - prop.computed || t.isLiteral(prop.key), - ), - // @ts-expect-error todo(flow->ts): double-check type error - getValue(prop), + function pushAssign( + objId: t.Identifier, + prop: t.ObjectMember, + body: t.Statement[], + ) { + body.push( + t.expressionStatement( + t.assignmentExpression( + "=", + t.memberExpression( + t.cloneNode(objId), + prop.key, + prop.computed || t.isLiteral(prop.key), ), + getValue(prop), ), - ); - } + ), + ); } - function pushMutatorDefine({ body, getMutatorId, scope }, prop) { + function pushMutatorDefine( + { body, getMutatorId, scope }: PropertyInfo, + prop: t.ObjectMethod, + ) { let key = !prop.computed && t.isIdentifier(prop.key) ? t.stringLiteral(prop.key.name) @@ -70,18 +90,21 @@ export default declare((api, options: Options) => { } body.push( - ...(buildMutatorMapAssign({ + ...buildMutatorMapAssign({ MUTATOR_MAP_REF: getMutatorId(), KEY: t.cloneNode(key), VALUE: getValue(prop), KIND: t.identifier(prop.kind), - }) as t.Statement[]), + }), ); } - function pushComputedPropsLoose(info) { + function pushComputedPropsLoose(info: PropertyInfo) { for (const prop of info.computedProps) { - if (prop.kind === "get" || prop.kind === "set") { + if ( + t.isObjectMethod(prop) && + (prop.kind === "get" || prop.kind === "set") + ) { pushMutatorDefine(info, prop); } else { pushAssign(t.cloneNode(info.objId), prop, info.body); @@ -89,20 +112,26 @@ export default declare((api, options: Options) => { } } - function pushComputedPropsSpec(info) { + function pushComputedPropsSpec(info: PropertyInfo) { const { objId, body, computedProps, state } = info; for (const prop of computedProps) { - const key = t.toComputedKey(prop); + // PrivateName must not be in ObjectExpression + const key = t.toComputedKey(prop) as t.Expression; - if (prop.kind === "get" || prop.kind === "set") { + if ( + t.isObjectMethod(prop) && + (prop.kind === "get" || prop.kind === "set") + ) { pushMutatorDefine(info, prop); } else { + // the value of ObjectProperty in ObjectExpression must be an expression + const value = getValue(prop) as t.Expression; if (computedProps.length === 1) { return t.callExpression(state.addHelper("defineProperty"), [ info.initPropExpression, key, - getValue(prop), + value, ]); } else { body.push( @@ -110,7 +139,7 @@ export default declare((api, options: Options) => { t.callExpression(state.addHelper("defineProperty"), [ t.cloneNode(objId), key, - getValue(prop), + value, ]), ), ); @@ -137,12 +166,14 @@ export default declare((api, options: Options) => { // put all getters/setters into the first object expression as well as all initialisers up // to the first computed property - const initProps = []; - const computedProps = []; + const initProps: t.ObjectMember[] = []; + const computedProps: t.ObjectMember[] = []; let foundComputed = false; for (const prop of node.properties) { - // @ts-ignore SpreadElement must not have computed property + if (t.isSpreadElement(prop)) { + continue; + } if (prop.computed) { foundComputed = true; } @@ -164,7 +195,7 @@ export default declare((api, options: Options) => { ]), ); - let mutatorRef; + let mutatorRef: t.Identifier; const getMutatorId = function () { if (!mutatorRef) { diff --git a/packages/babel-plugin-transform-duplicate-keys/src/index.ts b/packages/babel-plugin-transform-duplicate-keys/src/index.ts index 654c825ef497..f6491648ae2e 100644 --- a/packages/babel-plugin-transform-duplicate-keys/src/index.ts +++ b/packages/babel-plugin-transform-duplicate-keys/src/index.ts @@ -1,7 +1,9 @@ import { declare } from "@babel/helper-plugin-utils"; import { types as t } from "@babel/core"; -function getName(key) { +function getName( + key: t.Identifier | t.StringLiteral | t.NumericLiteral | t.BigIntLiteral, +) { if (t.isIdentifier(key)) { return key.name; } @@ -34,7 +36,14 @@ export default declare(api => { const alreadySeenSetters = Object.create(null); for (const prop of plainProps) { - const name = getName(prop.key); + const name = getName( + // prop must be non-computed + prop.key as + | t.Identifier + | t.StringLiteral + | t.NumericLiteral + | t.BigIntLiteral, + ); let isDuplicate = false; // @ts-ignore prop.kind is not defined in ObjectProperty switch (prop.kind) { diff --git a/packages/babel-plugin-transform-exponentiation-operator/src/index.ts b/packages/babel-plugin-transform-exponentiation-operator/src/index.ts index 680b8b24e8e0..1d68e9fb40d0 100644 --- a/packages/babel-plugin-transform-exponentiation-operator/src/index.ts +++ b/packages/babel-plugin-transform-exponentiation-operator/src/index.ts @@ -14,7 +14,11 @@ export default declare(api => { build(left, right) { return t.callExpression( t.memberExpression(t.identifier("Math"), t.identifier("pow")), - [left, right], + [ + // left can be PrivateName only if operator is `"in"` + left as t.Expression, + right, + ], ); }, }),