diff --git a/Gulpfile.mjs b/Gulpfile.mjs index 42b0d2159b..01a1ba99fa 100644 --- a/Gulpfile.mjs +++ b/Gulpfile.mjs @@ -149,7 +149,7 @@ async function generateRuntimeHelpers() { return generateHelpers( `./packages/babel-helpers/scripts/generate-helpers.js`, `./packages/babel-helpers/src/`, - "helpers-generated.js", + "helpers-generated.ts", "@babel/helpers" ); } diff --git a/packages/babel-core/src/tools/build-external-helpers.ts b/packages/babel-core/src/tools/build-external-helpers.ts index 0e9cfa9a1c..e0e4d73bca 100644 --- a/packages/babel-core/src/tools/build-external-helpers.ts +++ b/packages/babel-core/src/tools/build-external-helpers.ts @@ -7,7 +7,7 @@ import File from "../transformation/file/file"; // Wrapped to avoid wasting time parsing this when almost no-one uses // build-external-helpers. const buildUmdWrapper = replacements => - template` + template.statement` (function (root, factory) { if (typeof define === "function" && define.amd) { define(AMD_ARGUMENTS, factory); @@ -21,10 +21,10 @@ const buildUmdWrapper = replacements => }); `(replacements); -function buildGlobal(allowlist) { +function buildGlobal(allowlist?: Array) { const namespace = t.identifier("babelHelpers"); - const body = []; + const body: t.Statement[] = []; const container = t.functionExpression( null, [t.identifier("global")], @@ -65,8 +65,8 @@ function buildGlobal(allowlist) { return tree; } -function buildModule(allowlist) { - const body = []; +function buildModule(allowlist?: Array) { + const body: t.Statement[] = []; const refs = buildHelpers(body, null, allowlist); body.unshift( @@ -81,10 +81,10 @@ function buildModule(allowlist) { return t.program(body, [], "module"); } -function buildUmd(allowlist) { +function buildUmd(allowlist?: Array) { const namespace = t.identifier("babelHelpers"); - const body = []; + const body: t.Statement[] = []; body.push( t.variableDeclaration("var", [ t.variableDeclarator(namespace, t.identifier("global")), @@ -105,14 +105,14 @@ function buildUmd(allowlist) { AMD_ARGUMENTS: t.arrayExpression([t.stringLiteral("exports")]), FACTORY_BODY: body, UMD_ROOT: t.identifier("this"), - }) as t.Statement, + }), ]); } -function buildVar(allowlist) { +function buildVar(allowlist?: Array) { const namespace = t.identifier("babelHelpers"); - const body = []; + const body: t.Statement[] = []; body.push( t.variableDeclaration("var", [ t.variableDeclarator(namespace, t.objectExpression([])), @@ -124,8 +124,12 @@ function buildVar(allowlist) { return tree; } -function buildHelpers(body, namespace, allowlist) { - const getHelperReference = name => { +function buildHelpers( + body: t.Statement[], + namespace: t.Expression | null, + allowlist?: Array, +) { + const getHelperReference = (name: string) => { return namespace ? t.memberExpression(namespace, t.identifier(name)) : t.identifier(`_${name}`); @@ -148,7 +152,7 @@ export default function ( allowlist?: Array, outputType: "global" | "module" | "umd" | "var" = "global", ) { - let tree; + let tree: t.Program; const build = { global: buildGlobal, diff --git a/packages/babel-core/src/transformation/file/file.ts b/packages/babel-core/src/transformation/file/file.ts index 0dcf28b31e..66316bf8c1 100644 --- a/packages/babel-core/src/transformation/file/file.ts +++ b/packages/babel-core/src/transformation/file/file.ts @@ -174,7 +174,7 @@ export default class File { ); } - addHelper(name: string): any { + addHelper(name: string): t.Identifier { const declar = this.declarations[name]; if (declar) return t.cloneNode(declar); @@ -209,6 +209,7 @@ export default class File { }); nodes.forEach(node => { + // @ts-expect-error node._compact = true; }); diff --git a/packages/babel-helpers/src/helpers-generated.js b/packages/babel-helpers/src/helpers-generated.ts similarity index 100% rename from packages/babel-helpers/src/helpers-generated.js rename to packages/babel-helpers/src/helpers-generated.ts diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.ts similarity index 99% rename from packages/babel-helpers/src/helpers.js rename to packages/babel-helpers/src/helpers.ts index 5994fb6826..7385e63a71 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.ts @@ -1,13 +1,17 @@ -// @flow - import template from "@babel/template"; +import type * as t from "@babel/types"; import * as generated from "./helpers-generated"; -const helpers = { __proto__: null, ...generated }; +interface Helper { + minVersion: string; + ast: () => t.Program; +} + +const helpers: Record = { __proto__: null, ...generated }; export default helpers; -const helper = (minVersion: string) => tpl => ({ +const helper = (minVersion: string) => (tpl: TemplateStringsArray) => ({ minVersion, ast: () => template.program.ast(tpl), }); diff --git a/packages/babel-helpers/src/index.js b/packages/babel-helpers/src/index.ts similarity index 74% rename from packages/babel-helpers/src/index.js rename to packages/babel-helpers/src/index.ts index 0677196142..f66f649ae9 100644 --- a/packages/babel-helpers/src/index.js +++ b/packages/babel-helpers/src/index.ts @@ -1,8 +1,10 @@ +import type { File } from "@babel/core"; +import type { NodePath, Visitor } from "@babel/traverse"; import traverse from "@babel/traverse"; import * as t from "@babel/types"; import helpers from "./helpers"; -function makePath(path) { +function makePath(path: NodePath) { const parts = []; for (; path.parentPath; path = path.parentPath) { @@ -14,23 +16,35 @@ function makePath(path) { } let fileClass = undefined; + +interface HelperMetadata { + globals: string[]; + localBindingNames: string[]; + dependencies: Map; + exportBindingAssignments: string[]; + exportPath: string; + exportName: string; + importBindingsReferences: string[]; + importPaths: string[]; +} + /** * Given a file AST for a given helper, get a bunch of metadata about it so that Babel can quickly render * the helper is whatever context it is needed in. */ -function getHelperMetadata(file) { - const globals = new Set(); - const localBindingNames = new Set(); +function getHelperMetadata(file: File): HelperMetadata { + const globals = new Set(); + const localBindingNames = new Set(); // Maps imported identifier -> helper name - const dependencies = new Map(); + const dependencies = new Map(); - let exportName; - let exportPath; - const exportBindingAssignments = []; - const importPaths = []; - const importBindingsReferences = []; + let exportName: string | undefined; + let exportPath: string | undefined; + const exportBindingAssignments: string[] = []; + const importPaths: string[] = []; + const importBindingsReferences: string[] = []; - const dependencyVisitor = { + const dependencyVisitor: Visitor = { ImportDeclaration(child) { const name = child.node.source.value; if (!helpers[name]) { @@ -75,7 +89,7 @@ function getHelperMetadata(file) { }, }; - const referenceVisitor = { + const referenceVisitor: Visitor = { Program(path) { const bindings = path.scope.getAllBindings(); @@ -88,7 +102,7 @@ function getHelperMetadata(file) { }, ReferencedIdentifier(child) { const name = child.node.name; - const binding = child.scope.getBinding(name, /* noGlobal */ true); + const binding = child.scope.getBinding(name); if (!binding) { globals.add(name); } else if (dependencies.has(binding.identifier)) { @@ -135,10 +149,18 @@ function getHelperMetadata(file) { }; } +type GetDependency = (name: string) => t.Expression; + /** * Given a helper AST and information about how it will be used, update the AST to match the usage. */ -function permuteHelperAST(file, metadata, id, localBindings, getDependency) { +function permuteHelperAST( + file: File, + metadata: HelperMetadata, + id?: t.Identifier | t.MemberExpression, + localBindings?: string[], + getDependency?: GetDependency, +) { if (localBindings && !id) { throw new Error("Unexpected local bindings for module-based helpers."); } @@ -155,13 +177,13 @@ function permuteHelperAST(file, metadata, id, localBindings, getDependency) { importPaths, } = metadata; - const dependenciesRefs = {}; + const dependenciesRefs: Record = {}; dependencies.forEach((name, id) => { dependenciesRefs[id.name] = (typeof getDependency === "function" && getDependency(name)) || id; }); - const toRename = {}; + const toRename: Record = {}; const bindings = new Set(localBindings || []); localBindingNames.forEach(name => { let newName = name; @@ -174,13 +196,16 @@ function permuteHelperAST(file, metadata, id, localBindings, getDependency) { toRename[exportName] = id.name; } - const visitor = { + const visitor: Visitor = { Program(path) { // We need to compute these in advance because removing nodes would // invalidate the paths. - const exp = path.get(exportPath); - const imps = importPaths.map(p => path.get(p)); - const impsBindingRefs = importBindingsReferences.map(p => path.get(p)); + const exp: NodePath = path.get(exportPath); + const imps: NodePath[] = importPaths.map(p => + path.get(p), + ); + const impsBindingRefs: NodePath[] = + importBindingsReferences.map(p => path.get(p)); const decl = exp.get("declaration"); if (id.type === "Identifier") { @@ -188,13 +213,15 @@ function permuteHelperAST(file, metadata, id, localBindings, getDependency) { exp.replaceWith(decl); } else { exp.replaceWith( - t.variableDeclaration("var", [t.variableDeclarator(id, decl.node)]), + t.variableDeclaration("var", [ + t.variableDeclarator(id, decl.node as t.Expression), + ]), ); } } else if (id.type === "MemberExpression") { if (decl.isFunctionDeclaration()) { exportBindingAssignments.forEach(assignPath => { - const assign = path.get(assignPath); + const assign: NodePath = path.get(assignPath); assign.replaceWith(t.assignmentExpression("=", id, assign.node)); }); exp.replaceWith(decl); @@ -206,7 +233,9 @@ function permuteHelperAST(file, metadata, id, localBindings, getDependency) { ); } else { exp.replaceWith( - t.expressionStatement(t.assignmentExpression("=", id, decl.node)), + t.expressionStatement( + t.assignmentExpression("=", id, decl.node as t.Expression), + ), ); } } else { @@ -231,8 +260,21 @@ function permuteHelperAST(file, metadata, id, localBindings, getDependency) { traverse(file.ast, visitor, file.scope); } -const helperData = Object.create(null); -function loadHelper(name) { +interface HelperData { + build: ( + getDependency: GetDependency, + id: t.Identifier | t.MemberExpression, + localBindings: string[], + ) => { + nodes: t.Program["body"]; + globals: string[]; + }; + minVersion: () => string; + dependencies: Map; +} + +const helperData: Record = Object.create(null); +function loadHelper(name: string) { if (!helperData[name]) { const helper = helpers[name]; if (!helper) { @@ -242,7 +284,7 @@ function loadHelper(name) { }); } - const fn = () => { + const fn = (): File => { const file = { ast: t.file(helper.ast()) }; if (fileClass) { return new fileClass( @@ -252,7 +294,7 @@ function loadHelper(name) { file, ); } - return file; + return file as File; }; const metadata = getHelperMetadata(fn()); @@ -278,9 +320,9 @@ function loadHelper(name) { } export function get( - name, - getDependency?: string => ?t.Expression, - id?, + name: string, + getDependency?: GetDependency, + id?: t.Identifier | t.MemberExpression, localBindings?: string[], ) { return loadHelper(name).build(getDependency, id, localBindings); @@ -290,7 +332,7 @@ export function minVersion(name: string) { return loadHelper(name).minVersion(); } -export function getDependencies(name: string): $ReadOnlyArray { +export function getDependencies(name: string): ReadonlyArray { return Array.from(loadHelper(name).dependencies.values()); } diff --git a/packages/babel-traverse/src/scope/index.ts b/packages/babel-traverse/src/scope/index.ts index 85153c2d29..0e36ad68ee 100644 --- a/packages/babel-traverse/src/scope/index.ts +++ b/packages/babel-traverse/src/scope/index.ts @@ -1031,7 +1031,7 @@ export default class Scope { * Walks the scope tree and gathers **all** bindings. */ - getAllBindings(): any { + getAllBindings(): Record { const ids = Object.create(null); let scope: Scope = this; diff --git a/tsconfig.json b/tsconfig.json index 93f02059ad..c4ad33c272 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -24,6 +24,7 @@ "./packages/babel-helper-transform-fixture-test-runner/src/**/*.ts", "./packages/babel-helper-validator-identifier/src/**/*.ts", "./packages/babel-helper-validator-option/src/**/*.ts", + "./packages/babel-helpers/src/**/*.ts", "./packages/babel-highlight/src/**/*.ts", "./packages/babel-plugin-bugfix-v8-spread-parameters-in-optional-chaining/src/**/*.ts", "./packages/babel-plugin-proposal-async-do-expressions/src/**/*.ts", @@ -107,6 +108,9 @@ "@babel/helper-validator-option": [ "./packages/babel-helper-validator-option/src" ], + "@babel/helpers": [ + "./packages/babel-helpers/src" + ], "@babel/highlight": [ "./packages/babel-highlight/src" ],