convert @babel/helpers to typescript (#13679)
Co-authored-by: Federico Ciardi <fed.ciardi@gmail.com>
This commit is contained in:
parent
10640b2aad
commit
b00bd94ad8
@ -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"
|
||||
);
|
||||
}
|
||||
|
||||
@ -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<string>) {
|
||||
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<string>) {
|
||||
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<string>) {
|
||||
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<string>) {
|
||||
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<string>,
|
||||
) {
|
||||
const getHelperReference = (name: string) => {
|
||||
return namespace
|
||||
? t.memberExpression(namespace, t.identifier(name))
|
||||
: t.identifier(`_${name}`);
|
||||
@ -148,7 +152,7 @@ export default function (
|
||||
allowlist?: Array<string>,
|
||||
outputType: "global" | "module" | "umd" | "var" = "global",
|
||||
) {
|
||||
let tree;
|
||||
let tree: t.Program;
|
||||
|
||||
const build = {
|
||||
global: buildGlobal,
|
||||
|
||||
@ -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;
|
||||
});
|
||||
|
||||
|
||||
@ -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<string, Helper> = { __proto__: null, ...generated };
|
||||
export default helpers;
|
||||
|
||||
const helper = (minVersion: string) => tpl => ({
|
||||
const helper = (minVersion: string) => (tpl: TemplateStringsArray) => ({
|
||||
minVersion,
|
||||
ast: () => template.program.ast(tpl),
|
||||
});
|
||||
@ -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<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
|
||||
* 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<string>();
|
||||
const localBindingNames = new Set<string>();
|
||||
// Maps imported identifier -> helper name
|
||||
const dependencies = new Map();
|
||||
const dependencies = new Map<t.Identifier, string>();
|
||||
|
||||
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<string, t.Expression> = {};
|
||||
dependencies.forEach((name, id) => {
|
||||
dependenciesRefs[id.name] =
|
||||
(typeof getDependency === "function" && getDependency(name)) || id;
|
||||
});
|
||||
|
||||
const toRename = {};
|
||||
const toRename: Record<string, string> = {};
|
||||
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<t.ExportDefaultDeclaration> = path.get(exportPath);
|
||||
const imps: NodePath<t.ImportDeclaration>[] = importPaths.map(p =>
|
||||
path.get(p),
|
||||
);
|
||||
const impsBindingRefs: NodePath<t.Identifier>[] =
|
||||
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<t.Expression> = 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<t.Identifier, string>;
|
||||
}
|
||||
|
||||
const helperData: Record<string, HelperData> = 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<string> {
|
||||
export function getDependencies(name: string): ReadonlyArray<string> {
|
||||
return Array.from(loadHelper(name).dependencies.values());
|
||||
}
|
||||
|
||||
@ -1031,7 +1031,7 @@ export default class Scope {
|
||||
* Walks the scope tree and gathers **all** bindings.
|
||||
*/
|
||||
|
||||
getAllBindings(): any {
|
||||
getAllBindings(): Record<string, Binding> {
|
||||
const ids = Object.create(null);
|
||||
|
||||
let scope: Scope = this;
|
||||
|
||||
@ -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"
|
||||
],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user