convert @babel/helpers to typescript (#13679)

Co-authored-by: Federico Ciardi <fed.ciardi@gmail.com>
This commit is contained in:
王清雨 2021-08-17 04:39:30 +08:00 committed by GitHub
parent 10640b2aad
commit b00bd94ad8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 106 additions and 51 deletions

View File

@ -149,7 +149,7 @@ async function generateRuntimeHelpers() {
return generateHelpers( return generateHelpers(
`./packages/babel-helpers/scripts/generate-helpers.js`, `./packages/babel-helpers/scripts/generate-helpers.js`,
`./packages/babel-helpers/src/`, `./packages/babel-helpers/src/`,
"helpers-generated.js", "helpers-generated.ts",
"@babel/helpers" "@babel/helpers"
); );
} }

View File

@ -7,7 +7,7 @@ import File from "../transformation/file/file";
// Wrapped to avoid wasting time parsing this when almost no-one uses // Wrapped to avoid wasting time parsing this when almost no-one uses
// build-external-helpers. // build-external-helpers.
const buildUmdWrapper = replacements => const buildUmdWrapper = replacements =>
template` template.statement`
(function (root, factory) { (function (root, factory) {
if (typeof define === "function" && define.amd) { if (typeof define === "function" && define.amd) {
define(AMD_ARGUMENTS, factory); define(AMD_ARGUMENTS, factory);
@ -21,10 +21,10 @@ const buildUmdWrapper = replacements =>
}); });
`(replacements); `(replacements);
function buildGlobal(allowlist) { function buildGlobal(allowlist?: Array<string>) {
const namespace = t.identifier("babelHelpers"); const namespace = t.identifier("babelHelpers");
const body = []; const body: t.Statement[] = [];
const container = t.functionExpression( const container = t.functionExpression(
null, null,
[t.identifier("global")], [t.identifier("global")],
@ -65,8 +65,8 @@ function buildGlobal(allowlist) {
return tree; return tree;
} }
function buildModule(allowlist) { function buildModule(allowlist?: Array<string>) {
const body = []; const body: t.Statement[] = [];
const refs = buildHelpers(body, null, allowlist); const refs = buildHelpers(body, null, allowlist);
body.unshift( body.unshift(
@ -81,10 +81,10 @@ function buildModule(allowlist) {
return t.program(body, [], "module"); return t.program(body, [], "module");
} }
function buildUmd(allowlist) { function buildUmd(allowlist?: Array<string>) {
const namespace = t.identifier("babelHelpers"); const namespace = t.identifier("babelHelpers");
const body = []; const body: t.Statement[] = [];
body.push( body.push(
t.variableDeclaration("var", [ t.variableDeclaration("var", [
t.variableDeclarator(namespace, t.identifier("global")), t.variableDeclarator(namespace, t.identifier("global")),
@ -105,14 +105,14 @@ function buildUmd(allowlist) {
AMD_ARGUMENTS: t.arrayExpression([t.stringLiteral("exports")]), AMD_ARGUMENTS: t.arrayExpression([t.stringLiteral("exports")]),
FACTORY_BODY: body, FACTORY_BODY: body,
UMD_ROOT: t.identifier("this"), UMD_ROOT: t.identifier("this"),
}) as t.Statement, }),
]); ]);
} }
function buildVar(allowlist) { function buildVar(allowlist?: Array<string>) {
const namespace = t.identifier("babelHelpers"); const namespace = t.identifier("babelHelpers");
const body = []; const body: t.Statement[] = [];
body.push( body.push(
t.variableDeclaration("var", [ t.variableDeclaration("var", [
t.variableDeclarator(namespace, t.objectExpression([])), t.variableDeclarator(namespace, t.objectExpression([])),
@ -124,8 +124,12 @@ function buildVar(allowlist) {
return tree; return tree;
} }
function buildHelpers(body, namespace, allowlist) { function buildHelpers(
const getHelperReference = name => { body: t.Statement[],
namespace: t.Expression | null,
allowlist?: Array<string>,
) {
const getHelperReference = (name: string) => {
return namespace return namespace
? t.memberExpression(namespace, t.identifier(name)) ? t.memberExpression(namespace, t.identifier(name))
: t.identifier(`_${name}`); : t.identifier(`_${name}`);
@ -148,7 +152,7 @@ export default function (
allowlist?: Array<string>, allowlist?: Array<string>,
outputType: "global" | "module" | "umd" | "var" = "global", outputType: "global" | "module" | "umd" | "var" = "global",
) { ) {
let tree; let tree: t.Program;
const build = { const build = {
global: buildGlobal, global: buildGlobal,

View File

@ -174,7 +174,7 @@ export default class File {
); );
} }
addHelper(name: string): any { addHelper(name: string): t.Identifier {
const declar = this.declarations[name]; const declar = this.declarations[name];
if (declar) return t.cloneNode(declar); if (declar) return t.cloneNode(declar);
@ -209,6 +209,7 @@ export default class File {
}); });
nodes.forEach(node => { nodes.forEach(node => {
// @ts-expect-error
node._compact = true; node._compact = true;
}); });

View File

@ -1,13 +1,17 @@
// @flow
import template from "@babel/template"; import template from "@babel/template";
import type * as t from "@babel/types";
import * as generated from "./helpers-generated"; import * as generated from "./helpers-generated";
const helpers = { __proto__: null, ...generated }; interface Helper {
minVersion: string;
ast: () => t.Program;
}
const helpers: Record<string, Helper> = { __proto__: null, ...generated };
export default helpers; export default helpers;
const helper = (minVersion: string) => tpl => ({ const helper = (minVersion: string) => (tpl: TemplateStringsArray) => ({
minVersion, minVersion,
ast: () => template.program.ast(tpl), ast: () => template.program.ast(tpl),
}); });

View File

@ -1,8 +1,10 @@
import type { File } from "@babel/core";
import type { NodePath, Visitor } from "@babel/traverse";
import traverse from "@babel/traverse"; import traverse from "@babel/traverse";
import * as t from "@babel/types"; import * as t from "@babel/types";
import helpers from "./helpers"; import helpers from "./helpers";
function makePath(path) { function makePath(path: NodePath) {
const parts = []; const parts = [];
for (; path.parentPath; path = path.parentPath) { for (; path.parentPath; path = path.parentPath) {
@ -14,23 +16,35 @@ function makePath(path) {
} }
let fileClass = undefined; let fileClass = undefined;
interface HelperMetadata {
globals: string[];
localBindingNames: string[];
dependencies: Map<t.Identifier, string>;
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 * 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. * the helper is whatever context it is needed in.
*/ */
function getHelperMetadata(file) { function getHelperMetadata(file: File): HelperMetadata {
const globals = new Set(); const globals = new Set<string>();
const localBindingNames = new Set(); const localBindingNames = new Set<string>();
// Maps imported identifier -> helper name // Maps imported identifier -> helper name
const dependencies = new Map(); const dependencies = new Map<t.Identifier, string>();
let exportName; let exportName: string | undefined;
let exportPath; let exportPath: string | undefined;
const exportBindingAssignments = []; const exportBindingAssignments: string[] = [];
const importPaths = []; const importPaths: string[] = [];
const importBindingsReferences = []; const importBindingsReferences: string[] = [];
const dependencyVisitor = { const dependencyVisitor: Visitor = {
ImportDeclaration(child) { ImportDeclaration(child) {
const name = child.node.source.value; const name = child.node.source.value;
if (!helpers[name]) { if (!helpers[name]) {
@ -75,7 +89,7 @@ function getHelperMetadata(file) {
}, },
}; };
const referenceVisitor = { const referenceVisitor: Visitor = {
Program(path) { Program(path) {
const bindings = path.scope.getAllBindings(); const bindings = path.scope.getAllBindings();
@ -88,7 +102,7 @@ function getHelperMetadata(file) {
}, },
ReferencedIdentifier(child) { ReferencedIdentifier(child) {
const name = child.node.name; const name = child.node.name;
const binding = child.scope.getBinding(name, /* noGlobal */ true); const binding = child.scope.getBinding(name);
if (!binding) { if (!binding) {
globals.add(name); globals.add(name);
} else if (dependencies.has(binding.identifier)) { } 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. * 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) { if (localBindings && !id) {
throw new Error("Unexpected local bindings for module-based helpers."); throw new Error("Unexpected local bindings for module-based helpers.");
} }
@ -155,13 +177,13 @@ function permuteHelperAST(file, metadata, id, localBindings, getDependency) {
importPaths, importPaths,
} = metadata; } = metadata;
const dependenciesRefs = {}; const dependenciesRefs: Record<string, t.Expression> = {};
dependencies.forEach((name, id) => { dependencies.forEach((name, id) => {
dependenciesRefs[id.name] = dependenciesRefs[id.name] =
(typeof getDependency === "function" && getDependency(name)) || id; (typeof getDependency === "function" && getDependency(name)) || id;
}); });
const toRename = {}; const toRename: Record<string, string> = {};
const bindings = new Set(localBindings || []); const bindings = new Set(localBindings || []);
localBindingNames.forEach(name => { localBindingNames.forEach(name => {
let newName = name; let newName = name;
@ -174,13 +196,16 @@ function permuteHelperAST(file, metadata, id, localBindings, getDependency) {
toRename[exportName] = id.name; toRename[exportName] = id.name;
} }
const visitor = { const visitor: Visitor = {
Program(path) { Program(path) {
// We need to compute these in advance because removing nodes would // We need to compute these in advance because removing nodes would
// invalidate the paths. // invalidate the paths.
const exp = path.get(exportPath); const exp: NodePath<t.ExportDefaultDeclaration> = path.get(exportPath);
const imps = importPaths.map(p => path.get(p)); const imps: NodePath<t.ImportDeclaration>[] = importPaths.map(p =>
const impsBindingRefs = importBindingsReferences.map(p => path.get(p)); path.get(p),
);
const impsBindingRefs: NodePath<t.Identifier>[] =
importBindingsReferences.map(p => path.get(p));
const decl = exp.get("declaration"); const decl = exp.get("declaration");
if (id.type === "Identifier") { if (id.type === "Identifier") {
@ -188,13 +213,15 @@ function permuteHelperAST(file, metadata, id, localBindings, getDependency) {
exp.replaceWith(decl); exp.replaceWith(decl);
} else { } else {
exp.replaceWith( 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") { } else if (id.type === "MemberExpression") {
if (decl.isFunctionDeclaration()) { if (decl.isFunctionDeclaration()) {
exportBindingAssignments.forEach(assignPath => { exportBindingAssignments.forEach(assignPath => {
const assign = path.get(assignPath); const assign: NodePath<t.Expression> = path.get(assignPath);
assign.replaceWith(t.assignmentExpression("=", id, assign.node)); assign.replaceWith(t.assignmentExpression("=", id, assign.node));
}); });
exp.replaceWith(decl); exp.replaceWith(decl);
@ -206,7 +233,9 @@ function permuteHelperAST(file, metadata, id, localBindings, getDependency) {
); );
} else { } else {
exp.replaceWith( exp.replaceWith(
t.expressionStatement(t.assignmentExpression("=", id, decl.node)), t.expressionStatement(
t.assignmentExpression("=", id, decl.node as t.Expression),
),
); );
} }
} else { } else {
@ -231,8 +260,21 @@ function permuteHelperAST(file, metadata, id, localBindings, getDependency) {
traverse(file.ast, visitor, file.scope); traverse(file.ast, visitor, file.scope);
} }
const helperData = Object.create(null); interface HelperData {
function loadHelper(name) { build: (
getDependency: GetDependency,
id: t.Identifier | t.MemberExpression,
localBindings: string[],
) => {
nodes: t.Program["body"];
globals: string[];
};
minVersion: () => string;
dependencies: Map<t.Identifier, string>;
}
const helperData: Record<string, HelperData> = Object.create(null);
function loadHelper(name: string) {
if (!helperData[name]) { if (!helperData[name]) {
const helper = helpers[name]; const helper = helpers[name];
if (!helper) { if (!helper) {
@ -242,7 +284,7 @@ function loadHelper(name) {
}); });
} }
const fn = () => { const fn = (): File => {
const file = { ast: t.file(helper.ast()) }; const file = { ast: t.file(helper.ast()) };
if (fileClass) { if (fileClass) {
return new fileClass( return new fileClass(
@ -252,7 +294,7 @@ function loadHelper(name) {
file, file,
); );
} }
return file; return file as File;
}; };
const metadata = getHelperMetadata(fn()); const metadata = getHelperMetadata(fn());
@ -278,9 +320,9 @@ function loadHelper(name) {
} }
export function get( export function get(
name, name: string,
getDependency?: string => ?t.Expression, getDependency?: GetDependency,
id?, id?: t.Identifier | t.MemberExpression,
localBindings?: string[], localBindings?: string[],
) { ) {
return loadHelper(name).build(getDependency, id, localBindings); return loadHelper(name).build(getDependency, id, localBindings);
@ -290,7 +332,7 @@ export function minVersion(name: string) {
return loadHelper(name).minVersion(); return loadHelper(name).minVersion();
} }
export function getDependencies(name: string): $ReadOnlyArray<string> { export function getDependencies(name: string): ReadonlyArray<string> {
return Array.from(loadHelper(name).dependencies.values()); return Array.from(loadHelper(name).dependencies.values());
} }

View File

@ -1031,7 +1031,7 @@ export default class Scope {
* Walks the scope tree and gathers **all** bindings. * Walks the scope tree and gathers **all** bindings.
*/ */
getAllBindings(): any { getAllBindings(): Record<string, Binding> {
const ids = Object.create(null); const ids = Object.create(null);
let scope: Scope = this; let scope: Scope = this;

View File

@ -24,6 +24,7 @@
"./packages/babel-helper-transform-fixture-test-runner/src/**/*.ts", "./packages/babel-helper-transform-fixture-test-runner/src/**/*.ts",
"./packages/babel-helper-validator-identifier/src/**/*.ts", "./packages/babel-helper-validator-identifier/src/**/*.ts",
"./packages/babel-helper-validator-option/src/**/*.ts", "./packages/babel-helper-validator-option/src/**/*.ts",
"./packages/babel-helpers/src/**/*.ts",
"./packages/babel-highlight/src/**/*.ts", "./packages/babel-highlight/src/**/*.ts",
"./packages/babel-plugin-bugfix-v8-spread-parameters-in-optional-chaining/src/**/*.ts", "./packages/babel-plugin-bugfix-v8-spread-parameters-in-optional-chaining/src/**/*.ts",
"./packages/babel-plugin-proposal-async-do-expressions/src/**/*.ts", "./packages/babel-plugin-proposal-async-do-expressions/src/**/*.ts",
@ -107,6 +108,9 @@
"@babel/helper-validator-option": [ "@babel/helper-validator-option": [
"./packages/babel-helper-validator-option/src" "./packages/babel-helper-validator-option/src"
], ],
"@babel/helpers": [
"./packages/babel-helpers/src"
],
"@babel/highlight": [ "@babel/highlight": [
"./packages/babel-highlight/src" "./packages/babel-highlight/src"
], ],