Use named imports for babel types (#13685)

* migrate to named babel types imports

* perf: transform babel types import to destructuring

* fix merge errors

* apply plugin to itself
This commit is contained in:
Huáng Jùnliàng 2021-08-18 10:28:40 -04:00 committed by GitHub
parent fc66d4dd05
commit 614b486780
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
65 changed files with 1283 additions and 787 deletions

View File

@ -202,6 +202,7 @@ module.exports = function (api) {
{ {
test: sources.map(normalize), test: sources.map(normalize),
assumptions: sourceAssumptions, assumptions: sourceAssumptions,
plugins: [transformNamedBabelTypesImportToDestructuring],
}, },
{ {
test: unambiguousSources.map(normalize), test: unambiguousSources.map(normalize),
@ -455,6 +456,54 @@ function pluginPackageJsonMacro({ types: t }) {
}; };
} }
// transform `import { x } from "@babel/types"` to `import * as _t from "@babel/types"; const { x } = _t;
function transformNamedBabelTypesImportToDestructuring({
types: {
cloneNode,
importNamespaceSpecifier,
objectPattern,
objectProperty,
variableDeclarator,
variableDeclaration,
},
}) {
return {
name: "transform-babel-types-named-imports",
visitor: {
ImportDeclaration(path) {
const { node } = path;
if (
node.importKind === "value" &&
node.source.value === "@babel/types" &&
node.specifiers[0].type === "ImportSpecifier"
) {
const hoistedDestructuringProperties = [];
for (const { imported, local } of node.specifiers) {
hoistedDestructuringProperties.push(
objectProperty(
imported,
local,
false,
imported.name === local.name
)
);
}
const babelTypeNsImport = path.scope.generateUidIdentifier("t");
node.specifiers = [importNamespaceSpecifier(babelTypeNsImport)];
path.insertAfter([
variableDeclaration("const", [
variableDeclarator(
objectPattern(hoistedDestructuringProperties),
cloneNode(babelTypeNsImport)
),
]),
]);
}
},
},
};
}
function pluginImportMetaUrl({ types: t, template }) { function pluginImportMetaUrl({ types: t, template }) {
const isImportMeta = node => const isImportMeta = node =>
t.isMetaProperty(node) && t.isMetaProperty(node) &&

View File

@ -1,7 +1,28 @@
import * as helpers from "@babel/helpers"; import * as helpers from "@babel/helpers";
import generator from "@babel/generator"; import generator from "@babel/generator";
import template from "@babel/template"; import template from "@babel/template";
import * as t from "@babel/types"; import {
arrayExpression,
assignmentExpression,
binaryExpression,
blockStatement,
callExpression,
cloneNode,
conditionalExpression,
exportNamedDeclaration,
exportSpecifier,
expressionStatement,
functionExpression,
identifier,
memberExpression,
objectExpression,
program,
stringLiteral,
unaryExpression,
variableDeclaration,
variableDeclarator,
} from "@babel/types";
import type * as t from "@babel/types";
import File from "../transformation/file/file"; 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
@ -22,39 +43,39 @@ const buildUmdWrapper = replacements =>
`(replacements); `(replacements);
function buildGlobal(allowlist?: Array<string>) { function buildGlobal(allowlist?: Array<string>) {
const namespace = t.identifier("babelHelpers"); const namespace = identifier("babelHelpers");
const body: t.Statement[] = []; const body: t.Statement[] = [];
const container = t.functionExpression( const container = functionExpression(
null, null,
[t.identifier("global")], [identifier("global")],
t.blockStatement(body), blockStatement(body),
); );
const tree = t.program([ const tree = program([
t.expressionStatement( expressionStatement(
t.callExpression(container, [ callExpression(container, [
// typeof global === "undefined" ? self : global // typeof global === "undefined" ? self : global
t.conditionalExpression( conditionalExpression(
t.binaryExpression( binaryExpression(
"===", "===",
t.unaryExpression("typeof", t.identifier("global")), unaryExpression("typeof", identifier("global")),
t.stringLiteral("undefined"), stringLiteral("undefined"),
), ),
t.identifier("self"), identifier("self"),
t.identifier("global"), identifier("global"),
), ),
]), ]),
), ),
]); ]);
body.push( body.push(
t.variableDeclaration("var", [ variableDeclaration("var", [
t.variableDeclarator( variableDeclarator(
namespace, namespace,
t.assignmentExpression( assignmentExpression(
"=", "=",
t.memberExpression(t.identifier("global"), namespace), memberExpression(identifier("global"), namespace),
t.objectExpression([]), objectExpression([]),
), ),
), ),
]), ]),
@ -70,57 +91,57 @@ function buildModule(allowlist?: Array<string>) {
const refs = buildHelpers(body, null, allowlist); const refs = buildHelpers(body, null, allowlist);
body.unshift( body.unshift(
t.exportNamedDeclaration( exportNamedDeclaration(
null, null,
Object.keys(refs).map(name => { Object.keys(refs).map(name => {
return t.exportSpecifier(t.cloneNode(refs[name]), t.identifier(name)); return exportSpecifier(cloneNode(refs[name]), identifier(name));
}), }),
), ),
); );
return t.program(body, [], "module"); return program(body, [], "module");
} }
function buildUmd(allowlist?: Array<string>) { function buildUmd(allowlist?: Array<string>) {
const namespace = t.identifier("babelHelpers"); const namespace = identifier("babelHelpers");
const body: t.Statement[] = []; const body: t.Statement[] = [];
body.push( body.push(
t.variableDeclaration("var", [ variableDeclaration("var", [
t.variableDeclarator(namespace, t.identifier("global")), variableDeclarator(namespace, identifier("global")),
]), ]),
); );
buildHelpers(body, namespace, allowlist); buildHelpers(body, namespace, allowlist);
return t.program([ return program([
buildUmdWrapper({ buildUmdWrapper({
FACTORY_PARAMETERS: t.identifier("global"), FACTORY_PARAMETERS: identifier("global"),
BROWSER_ARGUMENTS: t.assignmentExpression( BROWSER_ARGUMENTS: assignmentExpression(
"=", "=",
t.memberExpression(t.identifier("root"), namespace), memberExpression(identifier("root"), namespace),
t.objectExpression([]), objectExpression([]),
), ),
COMMON_ARGUMENTS: t.identifier("exports"), COMMON_ARGUMENTS: identifier("exports"),
AMD_ARGUMENTS: t.arrayExpression([t.stringLiteral("exports")]), AMD_ARGUMENTS: arrayExpression([stringLiteral("exports")]),
FACTORY_BODY: body, FACTORY_BODY: body,
UMD_ROOT: t.identifier("this"), UMD_ROOT: identifier("this"),
}), }),
]); ]);
} }
function buildVar(allowlist?: Array<string>) { function buildVar(allowlist?: Array<string>) {
const namespace = t.identifier("babelHelpers"); const namespace = identifier("babelHelpers");
const body: t.Statement[] = []; const body: t.Statement[] = [];
body.push( body.push(
t.variableDeclaration("var", [ variableDeclaration("var", [
t.variableDeclarator(namespace, t.objectExpression([])), variableDeclarator(namespace, objectExpression([])),
]), ]),
); );
const tree = t.program(body); const tree = program(body);
buildHelpers(body, namespace, allowlist); buildHelpers(body, namespace, allowlist);
body.push(t.expressionStatement(namespace)); body.push(expressionStatement(namespace));
return tree; return tree;
} }
@ -131,8 +152,8 @@ function buildHelpers(
) { ) {
const getHelperReference = (name: string) => { const getHelperReference = (name: string) => {
return namespace return namespace
? t.memberExpression(namespace, t.identifier(name)) ? memberExpression(namespace, identifier(name))
: t.identifier(`_${name}`); : identifier(`_${name}`);
}; };
const refs = {}; const refs = {};

View File

@ -3,7 +3,8 @@ import { NodePath, Scope } from "@babel/traverse";
import type { HubInterface } from "@babel/traverse"; import type { HubInterface } from "@babel/traverse";
import { codeFrameColumns } from "@babel/code-frame"; import { codeFrameColumns } from "@babel/code-frame";
import traverse from "@babel/traverse"; import traverse from "@babel/traverse";
import * as t from "@babel/types"; import { cloneNode, interpreterDirective } from "@babel/types";
import type * as t from "@babel/types";
import { getModuleName } from "@babel/helper-module-transforms"; import { getModuleName } from "@babel/helper-module-transforms";
import semver from "semver"; import semver from "semver";
@ -89,7 +90,7 @@ export default class File {
} }
set shebang(value: string) { set shebang(value: string) {
if (value) { if (value) {
this.path.get("interpreter").replaceWith(t.interpreterDirective(value)); this.path.get("interpreter").replaceWith(interpreterDirective(value));
} else { } else {
this.path.get("interpreter").remove(); this.path.get("interpreter").remove();
} }
@ -176,7 +177,7 @@ export default class File {
addHelper(name: string): t.Identifier { addHelper(name: string): t.Identifier {
const declar = this.declarations[name]; const declar = this.declarations[name];
if (declar) return t.cloneNode(declar); if (declar) return cloneNode(declar);
const generator = this.get("helperGenerator"); const generator = this.get("helperGenerator");
if (generator) { if (generator) {

View File

@ -2,7 +2,8 @@ import fs from "fs";
import path from "path"; import path from "path";
import buildDebug from "debug"; import buildDebug from "debug";
import type { Handler } from "gensync"; import type { Handler } from "gensync";
import * as t from "@babel/types"; import { file, traverseFast } from "@babel/types";
import type * as t from "@babel/types";
import type { PluginPasses } from "../config"; import type { PluginPasses } from "../config";
import convertSourceMap from "convert-source-map"; import convertSourceMap from "convert-source-map";
import type { SourceMapConverter as Converter } from "convert-source-map"; import type { SourceMapConverter as Converter } from "convert-source-map";
@ -29,7 +30,7 @@ export default function* normalizeFile(
if (ast) { if (ast) {
if (ast.type === "Program") { if (ast.type === "Program") {
ast = t.file(ast, [], []); ast = file(ast, [], []);
} else if (ast.type !== "File") { } else if (ast.type !== "File") {
throw new Error("AST root must be a Program or File node"); throw new Error("AST root must be a Program or File node");
} }
@ -119,7 +120,7 @@ function extractCommentsFromList(regex, comments, lastComment) {
function extractComments(regex, ast) { function extractComments(regex, ast) {
let lastComment = null; let lastComment = null;
t.traverseFast(ast, node => { traverseFast(ast, node => {
[node.leadingComments, lastComment] = extractCommentsFromList( [node.leadingComments, lastComment] = extractCommentsFromList(
regex, regex,
node.leadingComments, node.leadingComments,

View File

@ -1,5 +1,5 @@
import type Printer from "../printer"; import type Printer from "../printer";
import * as t from "@babel/types"; import type * as t from "@babel/types";
import * as charCodes from "charcodes"; import * as charCodes from "charcodes";
export function File(this: Printer, node: t.File) { export function File(this: Printer, node: t.File) {

View File

@ -1,9 +1,11 @@
import type Printer from "../printer"; import type Printer from "../printer";
import * as t from "@babel/types"; import {
isExportDefaultDeclaration,
isExportNamedDeclaration,
} from "@babel/types";
import type * as t from "@babel/types";
import * as charCodes from "charcodes"; import * as charCodes from "charcodes";
const { isExportDefaultDeclaration, isExportNamedDeclaration } = t;
export function ClassDeclaration( export function ClassDeclaration(
this: Printer, this: Printer,
node: t.ClassDeclaration, node: t.ClassDeclaration,

View File

@ -1,8 +1,13 @@
import type Printer from "../printer"; import type Printer from "../printer";
import * as t from "@babel/types"; import {
isCallExpression,
isLiteral,
isMemberExpression,
isNewExpression,
} from "@babel/types";
import type * as t from "@babel/types";
import * as n from "../node"; import * as n from "../node";
const { isCallExpression, isLiteral, isMemberExpression, isNewExpression } = t;
export function UnaryExpression(this: Printer, node: t.UnaryExpression) { export function UnaryExpression(this: Printer, node: t.UnaryExpression) {
if ( if (
node.operator === "void" || node.operator === "void" ||

View File

@ -1,8 +1,8 @@
import type Printer from "../printer"; import type Printer from "../printer";
import * as t from "@babel/types"; import { isDeclareExportDeclaration, isStatement } from "@babel/types";
import type * as t from "@babel/types";
import { ExportAllDeclaration } from "./modules"; import { ExportAllDeclaration } from "./modules";
const { isDeclareExportDeclaration, isStatement } = t;
export function AnyTypeAnnotation(this: Printer) { export function AnyTypeAnnotation(this: Printer) {
this.word("any"); this.word("any");
} }

View File

@ -1,5 +1,5 @@
import type Printer from "../printer"; import type Printer from "../printer";
import * as t from "@babel/types"; import type * as t from "@babel/types";
export function JSXAttribute(this: Printer, node: t.JSXAttribute) { export function JSXAttribute(this: Printer, node: t.JSXAttribute) {
this.print(node.name, node); this.print(node.name, node);

View File

@ -1,7 +1,7 @@
import type Printer from "../printer"; import type Printer from "../printer";
import * as t from "@babel/types"; import { isIdentifier } from "@babel/types";
import type * as t from "@babel/types";
const { isIdentifier } = t;
export function _params(this: Printer, node: any) { export function _params(this: Printer, node: any) {
this.print(node.typeParameters, node); this.print(node.typeParameters, node);
this.token("("); this.token("(");

View File

@ -1,14 +1,13 @@
import type Printer from "../printer"; import type Printer from "../printer";
import * as t from "@babel/types"; import {
const {
isClassDeclaration, isClassDeclaration,
isExportDefaultSpecifier, isExportDefaultSpecifier,
isExportNamespaceSpecifier, isExportNamespaceSpecifier,
isImportDefaultSpecifier, isImportDefaultSpecifier,
isImportNamespaceSpecifier, isImportNamespaceSpecifier,
isStatement, isStatement,
} = t; } from "@babel/types";
import type * as t from "@babel/types";
export function ImportSpecifier(this: Printer, node: t.ImportSpecifier) { export function ImportSpecifier(this: Printer, node: t.ImportSpecifier) {
if (node.importKind === "type" || node.importKind === "typeof") { if (node.importKind === "type" || node.importKind === "typeof") {

View File

@ -1,8 +1,13 @@
import type Printer from "../printer"; import type Printer from "../printer";
import * as t from "@babel/types"; import {
isFor,
isForStatement,
isIfStatement,
isStatement,
} from "@babel/types";
import type * as t from "@babel/types";
import * as charCodes from "charcodes"; import * as charCodes from "charcodes";
const { isFor, isForStatement, isIfStatement, isStatement } = t;
export function WithStatement(this: Printer, node: t.WithStatement) { export function WithStatement(this: Printer, node: t.WithStatement) {
this.word("with"); this.word("with");
this.space(); this.space();

View File

@ -1,5 +1,5 @@
import type Printer from "../printer"; import type Printer from "../printer";
import * as t from "@babel/types"; import type * as t from "@babel/types";
export function TaggedTemplateExpression( export function TaggedTemplateExpression(
this: Printer, this: Printer,

View File

@ -1,8 +1,8 @@
import type Printer from "../printer"; import type Printer from "../printer";
import * as t from "@babel/types"; import { isAssignmentPattern, isIdentifier } from "@babel/types";
import type * as t from "@babel/types";
import jsesc from "jsesc"; import jsesc from "jsesc";
const { isAssignmentPattern, isIdentifier } = t;
export function Identifier(this: Printer, node: t.Identifier) { export function Identifier(this: Printer, node: t.Identifier) {
this.exactSource(node.loc, () => { this.exactSource(node.loc, () => {
this.word(node.name); this.word(node.name);

View File

@ -1,5 +1,5 @@
import type Printer from "../printer"; import type Printer from "../printer";
import * as t from "@babel/types"; import type * as t from "@babel/types";
export function TSTypeAnnotation(this: Printer, node: t.TSTypeAnnotation) { export function TSTypeAnnotation(this: Printer, node: t.TSTypeAnnotation) {
this.token(":"); this.token(":");

View File

@ -1,12 +1,13 @@
import * as whitespace from "./whitespace"; import * as whitespace from "./whitespace";
import * as parens from "./parentheses"; import * as parens from "./parentheses";
import * as t from "@babel/types"; import {
const { FLIPPED_ALIAS_KEYS,
isCallExpression, isCallExpression,
isExpressionStatement, isExpressionStatement,
isMemberExpression, isMemberExpression,
isNewExpression, isNewExpression,
} = t; } from "@babel/types";
function expandAliases(obj) { function expandAliases(obj) {
const newObj = {}; const newObj = {};
@ -22,7 +23,7 @@ function expandAliases(obj) {
} }
for (const type of Object.keys(obj)) { for (const type of Object.keys(obj)) {
const aliases = t.FLIPPED_ALIAS_KEYS[type]; const aliases = FLIPPED_ALIAS_KEYS[type];
if (aliases) { if (aliases) {
for (const alias of aliases) { for (const alias of aliases) {
add(alias, obj[type]); add(alias, obj[type]);

View File

@ -1,6 +1,4 @@
import * as t from "@babel/types"; import {
const {
isArrayTypeAnnotation, isArrayTypeAnnotation,
isArrowFunctionExpression, isArrowFunctionExpression,
isAssignmentExpression, isAssignmentExpression,
@ -48,7 +46,8 @@ const {
isVariableDeclarator, isVariableDeclarator,
isWhileStatement, isWhileStatement,
isYieldExpression, isYieldExpression,
} = t; } from "@babel/types";
import type * as t from "@babel/types";
const PRECEDENCE = { const PRECEDENCE = {
"||": 0, "||": 0,
"??": 0, "??": 0,

View File

@ -1,6 +1,5 @@
import * as t from "@babel/types"; import {
FLIPPED_ALIAS_KEYS,
const {
isArrayExpression, isArrayExpression,
isAssignmentExpression, isAssignmentExpression,
isBinary, isBinary,
@ -14,7 +13,9 @@ const {
isOptionalCallExpression, isOptionalCallExpression,
isOptionalMemberExpression, isOptionalMemberExpression,
isStringLiteral, isStringLiteral,
} = t; } from "@babel/types";
import type * as t from "@babel/types";
type WhitespaceObject = { type WhitespaceObject = {
before?: boolean; before?: boolean;
after?: boolean; after?: boolean;
@ -319,7 +320,7 @@ export const list = {
amounts = { after: amounts, before: amounts }; amounts = { after: amounts, before: amounts };
} }
[type as string] [type as string]
.concat(t.FLIPPED_ALIAS_KEYS[type] || []) .concat(FLIPPED_ALIAS_KEYS[type] || [])
.forEach(function (type) { .forEach(function (type) {
nodes[type] = function () { nodes[type] = function () {
return amounts; return amounts;

View File

@ -1,6 +1,7 @@
import Buffer from "./buffer"; import Buffer from "./buffer";
import * as n from "./node"; import * as n from "./node";
import * as t from "@babel/types"; import { isProgram, isFile, isEmptyStatement } from "@babel/types";
import type * as t from "@babel/types";
import * as generatorFunctions from "./generators"; import * as generatorFunctions from "./generators";
import type SourceMap from "./source-map"; import type SourceMap from "./source-map";
@ -11,7 +12,6 @@ const ZERO_DECIMAL_INTEGER = /\.0+$/;
const NON_DECIMAL_LITERAL = /^0[box]/; const NON_DECIMAL_LITERAL = /^0[box]/;
const PURE_ANNOTATION_RE = /^\s*[@#]__PURE__\s*$/; const PURE_ANNOTATION_RE = /^\s*[@#]__PURE__\s*$/;
const { isProgram, isFile, isEmptyStatement } = t;
const { needsParens, needsWhitespaceAfter, needsWhitespaceBefore } = n; const { needsParens, needsWhitespaceAfter, needsWhitespaceBefore } = n;
export type Format = { export type Format = {

View File

@ -1,4 +1,4 @@
import * as t from "@babel/types"; import { addComment } from "@babel/types";
import type { Node } from "@babel/types"; import type { Node } from "@babel/types";
const PURE_ANNOTATION = "#__PURE__"; const PURE_ANNOTATION = "#__PURE__";
@ -14,5 +14,5 @@ export default function annotateAsPure(
if (isPureAnnotated(node)) { if (isPureAnnotated(node)) {
return; return;
} }
t.addComment(node, "leading", PURE_ANNOTATION); addComment(node, "leading", PURE_ANNOTATION);
} }

View File

@ -1,5 +1,5 @@
import explode from "@babel/helper-explode-assignable-expression"; import explode from "@babel/helper-explode-assignable-expression";
import * as t from "@babel/types"; import { assignmentExpression, sequenceExpression } from "@babel/types";
export default function (opts: { build: Function, operator: string }): Object { export default function (opts: { build: Function, operator: string }): Object {
const { build, operator } = opts; const { build, operator } = opts;
@ -12,13 +12,13 @@ export default function (opts: { build: Function, operator: string }): Object {
const nodes = []; const nodes = [];
const exploded = explode(node.left, nodes, this, scope); const exploded = explode(node.left, nodes, this, scope);
nodes.push( nodes.push(
t.assignmentExpression( assignmentExpression(
"=", "=",
exploded.ref, exploded.ref,
build(exploded.uid, node.right), build(exploded.uid, node.right),
), ),
); );
path.replaceWith(t.sequenceExpression(nodes)); path.replaceWith(sequenceExpression(nodes));
}, },
BinaryExpression(path) { BinaryExpression(path) {

View File

@ -1,4 +1,28 @@
import * as t from "@babel/types"; import {
booleanLiteral,
callExpression,
identifier,
inherits,
isIdentifier,
isJSXExpressionContainer,
isJSXIdentifier,
isJSXMemberExpression,
isJSXNamespacedName,
isJSXSpreadAttribute,
isLiteral,
isObjectExpression,
isReferenced,
isStringLiteral,
isValidIdentifier,
memberExpression,
nullLiteral,
objectExpression,
objectProperty,
react,
spreadElement,
stringLiteral,
thisExpression,
} from "@babel/types";
import annotateAsPure from "@babel/helper-annotate-as-pure"; import annotateAsPure from "@babel/helper-annotate-as-pure";
type ElementState = { type ElementState = {
@ -31,7 +55,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
exit(path, file) { exit(path, file) {
const callExpr = buildElementCall(path, file); const callExpr = buildElementCall(path, file);
if (callExpr) { if (callExpr) {
path.replaceWith(t.inherits(callExpr, path.node)); path.replaceWith(inherits(callExpr, path.node));
} }
}, },
}; };
@ -45,7 +69,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
} }
const callExpr = buildFragmentCall(path, file); const callExpr = buildFragmentCall(path, file);
if (callExpr) { if (callExpr) {
path.replaceWith(t.inherits(callExpr, path.node)); path.replaceWith(inherits(callExpr, path.node));
} }
}, },
}; };
@ -53,32 +77,32 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
return visitor; return visitor;
function convertJSXIdentifier(node, parent) { function convertJSXIdentifier(node, parent) {
if (t.isJSXIdentifier(node)) { if (isJSXIdentifier(node)) {
if (node.name === "this" && t.isReferenced(node, parent)) { if (node.name === "this" && isReferenced(node, parent)) {
return t.thisExpression(); return thisExpression();
} else if (t.isValidIdentifier(node.name, false)) { } else if (isValidIdentifier(node.name, false)) {
node.type = "Identifier"; node.type = "Identifier";
} else { } else {
return t.stringLiteral(node.name); return stringLiteral(node.name);
} }
} else if (t.isJSXMemberExpression(node)) { } else if (isJSXMemberExpression(node)) {
return t.memberExpression( return memberExpression(
convertJSXIdentifier(node.object, node), convertJSXIdentifier(node.object, node),
convertJSXIdentifier(node.property, node), convertJSXIdentifier(node.property, node),
); );
} else if (t.isJSXNamespacedName(node)) { } else if (isJSXNamespacedName(node)) {
/** /**
* If there is flag "throwIfNamespace" * If there is flag "throwIfNamespace"
* print XMLNamespace like string literal * print XMLNamespace like string literal
*/ */
return t.stringLiteral(`${node.namespace.name}:${node.name.name}`); return stringLiteral(`${node.namespace.name}:${node.name.name}`);
} }
return node; return node;
} }
function convertAttributeValue(node) { function convertAttributeValue(node) {
if (t.isJSXExpressionContainer(node)) { if (isJSXExpressionContainer(node)) {
return node.expression; return node.expression;
} else { } else {
return node; return node;
@ -86,37 +110,37 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
} }
function convertAttribute(node) { function convertAttribute(node) {
const value = convertAttributeValue(node.value || t.booleanLiteral(true)); const value = convertAttributeValue(node.value || booleanLiteral(true));
if (t.isJSXSpreadAttribute(node)) { if (isJSXSpreadAttribute(node)) {
return t.spreadElement(node.argument); return spreadElement(node.argument);
} }
if (t.isStringLiteral(value) && !t.isJSXExpressionContainer(node.value)) { if (isStringLiteral(value) && !isJSXExpressionContainer(node.value)) {
value.value = value.value.replace(/\n\s+/g, " "); value.value = value.value.replace(/\n\s+/g, " ");
// "raw" JSXText should not be used from a StringLiteral because it needs to be escaped. // "raw" JSXText should not be used from a StringLiteral because it needs to be escaped.
delete value.extra?.raw; delete value.extra?.raw;
} }
if (t.isJSXNamespacedName(node.name)) { if (isJSXNamespacedName(node.name)) {
node.name = t.stringLiteral( node.name = stringLiteral(
node.name.namespace.name + ":" + node.name.name.name, node.name.namespace.name + ":" + node.name.name.name,
); );
} else if (t.isValidIdentifier(node.name.name, false)) { } else if (isValidIdentifier(node.name.name, false)) {
node.name.type = "Identifier"; node.name.type = "Identifier";
} else { } else {
node.name = t.stringLiteral(node.name.name); node.name = stringLiteral(node.name.name);
} }
return t.inherits(t.objectProperty(node.name, value), node); return inherits(objectProperty(node.name, value), node);
} }
function buildElementCall(path, file) { function buildElementCall(path, file) {
if (opts.filter && !opts.filter(path.node, file)) return; if (opts.filter && !opts.filter(path.node, file)) return;
const openingPath = path.get("openingElement"); const openingPath = path.get("openingElement");
openingPath.parent.children = t.react.buildChildren(openingPath.parent); openingPath.parent.children = react.buildChildren(openingPath.parent);
const tagExpr = convertJSXIdentifier( const tagExpr = convertJSXIdentifier(
openingPath.node.name, openingPath.node.name,
@ -125,9 +149,9 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
const args = []; const args = [];
let tagName; let tagName;
if (t.isIdentifier(tagExpr)) { if (isIdentifier(tagExpr)) {
tagName = tagExpr.name; tagName = tagExpr.name;
} else if (t.isLiteral(tagExpr)) { } else if (isLiteral(tagExpr)) {
tagName = tagExpr.value; tagName = tagExpr.value;
} }
@ -145,12 +169,12 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
let attribs = openingPath.node.attributes; let attribs = openingPath.node.attributes;
if (attribs.length) { if (attribs.length) {
if (process.env.BABEL_8_BREAKING) { if (process.env.BABEL_8_BREAKING) {
attribs = t.objectExpression(attribs.map(convertAttribute)); attribs = objectExpression(attribs.map(convertAttribute));
} else { } else {
attribs = buildOpeningElementAttributes(attribs, file); attribs = buildOpeningElementAttributes(attribs, file);
} }
} else { } else {
attribs = t.nullLiteral(); attribs = nullLiteral();
} }
args.push(attribs, ...path.node.children); args.push(attribs, ...path.node.children);
@ -159,7 +183,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
opts.post(state, file); opts.post(state, file);
} }
const call = state.call || t.callExpression(state.callee, args); const call = state.call || callExpression(state.callee, args);
if (state.pure) annotateAsPure(call); if (state.pure) annotateAsPure(call);
return call; return call;
@ -168,7 +192,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
function pushProps(_props, objs) { function pushProps(_props, objs) {
if (!_props.length) return _props; if (!_props.length) return _props;
objs.push(t.objectExpression(_props)); objs.push(objectExpression(_props));
return []; return [];
} }
@ -208,12 +232,12 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
if (useSpread) { if (useSpread) {
const props = attribs.map(convertAttribute); const props = attribs.map(convertAttribute);
return t.objectExpression(props); return objectExpression(props);
} }
while (attribs.length) { while (attribs.length) {
const prop = attribs.shift(); const prop = attribs.shift();
if (t.isJSXSpreadAttribute(prop)) { if (isJSXSpreadAttribute(prop)) {
_props = pushProps(_props, objs); _props = pushProps(_props, objs);
objs.push(prop.argument); objs.push(prop.argument);
} else { } else {
@ -228,16 +252,16 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
attribs = objs[0]; attribs = objs[0];
} else { } else {
// looks like we have multiple objects // looks like we have multiple objects
if (!t.isObjectExpression(objs[0])) { if (!isObjectExpression(objs[0])) {
objs.unshift(t.objectExpression([])); objs.unshift(objectExpression([]));
} }
const helper = useBuiltIns const helper = useBuiltIns
? t.memberExpression(t.identifier("Object"), t.identifier("assign")) ? memberExpression(identifier("Object"), identifier("assign"))
: file.addHelper("extends"); : file.addHelper("extends");
// spread it // spread it
attribs = t.callExpression(helper, objs); attribs = callExpression(helper, objs);
} }
return attribs; return attribs;
@ -247,7 +271,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
if (opts.filter && !opts.filter(path.node, file)) return; if (opts.filter && !opts.filter(path.node, file)) return;
const openingPath = path.get("openingElement"); const openingPath = path.get("openingElement");
openingPath.parent.children = t.react.buildChildren(openingPath.parent); openingPath.parent.children = react.buildChildren(openingPath.parent);
const args = []; const args = [];
const tagName = null; const tagName = null;
@ -265,7 +289,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
} }
// no attributes are allowed with <> syntax // no attributes are allowed with <> syntax
args.push(t.nullLiteral(), ...path.node.children); args.push(nullLiteral(), ...path.node.children);
if (opts.post) { if (opts.post) {
opts.post(state, file); opts.post(state, file);
@ -273,7 +297,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
file.set("usedFragment", true); file.set("usedFragment", true);
const call = state.call || t.callExpression(state.callee, args); const call = state.call || callExpression(state.callee, args);
if (state.pure) annotateAsPure(call); if (state.pure) annotateAsPure(call);
return call; return call;

View File

@ -1,8 +1,25 @@
import nameFunction from "@babel/helper-function-name"; import nameFunction from "@babel/helper-function-name";
import * as t from "@babel/types"; import {
arrayExpression,
booleanLiteral,
functionExpression,
identifier,
inheritsComments,
isClassMethod,
isFunctionExpression,
isObjectMethod,
isObjectProperty,
isProperty,
isStringLiteral,
objectExpression,
objectProperty,
removeComments,
toComputedKey,
toKeyAlias,
} from "@babel/types";
function toKind(node: Object) { function toKind(node: Object) {
if (t.isClassMethod(node) || t.isObjectMethod(node)) { if (isClassMethod(node) || isObjectMethod(node)) {
if (node.kind === "get" || node.kind === "set") { if (node.kind === "get" || node.kind === "set") {
return node.kind; return node.kind;
} }
@ -20,7 +37,7 @@ export function push(
file, file,
scope?, scope?,
): Object { ): Object {
const alias = t.toKeyAlias(node); const alias = toKeyAlias(node);
// //
@ -40,8 +57,7 @@ export function push(
} }
if (node.decorators) { if (node.decorators) {
const decorators = (map.decorators = const decorators = (map.decorators = map.decorators || arrayExpression([]));
map.decorators || t.arrayExpression([]));
decorators.elements.push( decorators.elements.push(
...node.decorators.map(dec => dec.expression).reverse(), ...node.decorators.map(dec => dec.expression).reverse(),
); );
@ -54,18 +70,14 @@ export function push(
let key, value; let key, value;
// save the key so we can possibly do function name inferences // save the key so we can possibly do function name inferences
if ( if (isObjectProperty(node) || isObjectMethod(node) || isClassMethod(node)) {
t.isObjectProperty(node) || key = toComputedKey(node, node.key);
t.isObjectMethod(node) ||
t.isClassMethod(node)
) {
key = t.toComputedKey(node, node.key);
} }
if (t.isProperty(node)) { if (isProperty(node)) {
value = node.value; value = node.value;
} else if (t.isObjectMethod(node) || t.isClassMethod(node)) { } else if (isObjectMethod(node) || isClassMethod(node)) {
value = t.functionExpression( value = functionExpression(
null, null,
node.params, node.params,
node.body, node.body,
@ -83,15 +95,15 @@ export function push(
// infer function name // infer function name
if ( if (
scope && scope &&
t.isStringLiteral(key) && isStringLiteral(key) &&
(kind === "value" || kind === "initializer") && (kind === "value" || kind === "initializer") &&
t.isFunctionExpression(value) isFunctionExpression(value)
) { ) {
value = nameFunction({ id: key, node: value, scope }); value = nameFunction({ id: key, node: value, scope });
} }
if (value) { if (value) {
t.inheritsComments(value, node); inheritsComments(value, node);
map[kind] = value; map[kind] = value;
} }
@ -108,13 +120,13 @@ export function hasComputed(mutatorMap: Object): boolean {
} }
export function toComputedObjectFromClass(obj: Object): Object { export function toComputedObjectFromClass(obj: Object): Object {
const objExpr = t.arrayExpression([]); const objExpr = arrayExpression([]);
for (let i = 0; i < obj.properties.length; i++) { for (let i = 0; i < obj.properties.length; i++) {
const prop = obj.properties[i]; const prop = obj.properties[i];
const val = prop.value; const val = prop.value;
val.properties.unshift( val.properties.unshift(
t.objectProperty(t.identifier("key"), t.toComputedKey(prop)), objectProperty(identifier("key"), toComputedKey(prop)),
); );
objExpr.elements.push(val); objExpr.elements.push(val);
} }
@ -123,21 +135,21 @@ export function toComputedObjectFromClass(obj: Object): Object {
} }
export function toClassObject(mutatorMap: Object): Object { export function toClassObject(mutatorMap: Object): Object {
const objExpr = t.objectExpression([]); const objExpr = objectExpression([]);
Object.keys(mutatorMap).forEach(function (mutatorMapKey) { Object.keys(mutatorMap).forEach(function (mutatorMapKey) {
const map = mutatorMap[mutatorMapKey]; const map = mutatorMap[mutatorMapKey];
const mapNode = t.objectExpression([]); const mapNode = objectExpression([]);
const propNode = t.objectProperty(map._key, mapNode, map._computed); const propNode = objectProperty(map._key, mapNode, map._computed);
Object.keys(map).forEach(function (key) { Object.keys(map).forEach(function (key) {
const node = map[key]; const node = map[key];
if (key[0] === "_") return; if (key[0] === "_") return;
const prop = t.objectProperty(t.identifier(key), node); const prop = objectProperty(identifier(key), node);
t.inheritsComments(prop, node); inheritsComments(prop, node);
t.removeComments(node); removeComments(node);
mapNode.properties.push(prop); mapNode.properties.push(prop);
}); });
@ -151,9 +163,9 @@ export function toClassObject(mutatorMap: Object): Object {
export function toDefineObject(mutatorMap: Object): Object { export function toDefineObject(mutatorMap: Object): Object {
Object.keys(mutatorMap).forEach(function (key) { Object.keys(mutatorMap).forEach(function (key) {
const map = mutatorMap[key]; const map = mutatorMap[key];
if (map.value) map.writable = t.booleanLiteral(true); if (map.value) map.writable = booleanLiteral(true);
map.configurable = t.booleanLiteral(true); map.configurable = booleanLiteral(true);
map.enumerable = t.booleanLiteral(true); map.enumerable = booleanLiteral(true);
}); });
return toClassObject(mutatorMap); return toClassObject(mutatorMap);

View File

@ -1,5 +1,17 @@
import type { Scope } from "@babel/traverse"; import type { Scope } from "@babel/traverse";
import * as t from "@babel/types"; import {
assignmentExpression,
cloneNode,
isIdentifier,
isLiteral,
isMemberExpression,
isPrivateName,
isPureish,
isSuper,
memberExpression,
toComputedKey,
} from "@babel/types";
import type * as t from "@babel/types";
function getObjRef( function getObjRef(
node: t.Identifier | t.MemberExpression, node: t.Identifier | t.MemberExpression,
@ -7,7 +19,7 @@ function getObjRef(
scope: Scope, scope: Scope,
): t.Identifier | t.Super { ): t.Identifier | t.Super {
let ref; let ref;
if (t.isIdentifier(node)) { if (isIdentifier(node)) {
if (scope.hasBinding(node.name)) { if (scope.hasBinding(node.name)) {
// this variable is declared in scope so we can be 100% sure // this variable is declared in scope so we can be 100% sure
// that evaluating it multiple times wont trigger a getter // that evaluating it multiple times wont trigger a getter
@ -18,10 +30,10 @@ function getObjRef(
// it once // it once
ref = node; ref = node;
} }
} else if (t.isMemberExpression(node)) { } else if (isMemberExpression(node)) {
ref = node.object; ref = node.object;
if (t.isSuper(ref) || (t.isIdentifier(ref) && scope.hasBinding(ref.name))) { if (isSuper(ref) || (isIdentifier(ref) && scope.hasBinding(ref.name))) {
// the object reference that we need to save is locally declared // the object reference that we need to save is locally declared
// so as per the previous comment we can be 100% sure evaluating // so as per the previous comment we can be 100% sure evaluating
// it multiple times will be safe // it multiple times will be safe
@ -34,7 +46,7 @@ function getObjRef(
const temp = scope.generateUidIdentifierBasedOnNode(ref); const temp = scope.generateUidIdentifierBasedOnNode(ref);
scope.push({ id: temp }); scope.push({ id: temp });
nodes.push(t.assignmentExpression("=", t.cloneNode(temp), t.cloneNode(ref))); nodes.push(assignmentExpression("=", cloneNode(temp), cloneNode(ref)));
return temp; return temp;
} }
@ -44,17 +56,17 @@ function getPropRef(
scope: Scope, scope: Scope,
): t.Identifier | t.Literal { ): t.Identifier | t.Literal {
const prop = node.property; const prop = node.property;
if (t.isPrivateName(prop)) { if (isPrivateName(prop)) {
throw new Error( throw new Error(
"We can't generate property ref for private name, please install `@babel/plugin-proposal-class-properties`", "We can't generate property ref for private name, please install `@babel/plugin-proposal-class-properties`",
); );
} }
const key = t.toComputedKey(node, prop); const key = toComputedKey(node, prop);
if (t.isLiteral(key) && t.isPureish(key)) return key; if (isLiteral(key) && isPureish(key)) return key;
const temp = scope.generateUidIdentifierBasedOnNode(prop); const temp = scope.generateUidIdentifierBasedOnNode(prop);
scope.push({ id: temp }); scope.push({ id: temp });
nodes.push(t.assignmentExpression("=", t.cloneNode(temp), t.cloneNode(prop))); nodes.push(assignmentExpression("=", cloneNode(temp), cloneNode(prop)));
return temp; return temp;
} }
@ -70,7 +82,7 @@ export default function (
ref: t.Identifier | t.MemberExpression; ref: t.Identifier | t.MemberExpression;
} { } {
let obj; let obj;
if (t.isIdentifier(node) && allowedSingleIdent) { if (isIdentifier(node) && allowedSingleIdent) {
obj = node; obj = node;
} else { } else {
obj = getObjRef(node, nodes, scope); obj = getObjRef(node, nodes, scope);
@ -78,14 +90,14 @@ export default function (
let ref, uid; let ref, uid;
if (t.isIdentifier(node)) { if (isIdentifier(node)) {
ref = t.cloneNode(node); ref = cloneNode(node);
uid = obj; uid = obj;
} else { } else {
const prop = getPropRef(node, nodes, scope); const prop = getPropRef(node, nodes, scope);
const computed = node.computed || t.isLiteral(prop); const computed = node.computed || isLiteral(prop);
uid = t.memberExpression(t.cloneNode(obj), t.cloneNode(prop), computed); uid = memberExpression(cloneNode(obj), cloneNode(prop), computed);
ref = t.memberExpression(t.cloneNode(obj), t.cloneNode(prop), computed); ref = memberExpression(cloneNode(obj), cloneNode(prop), computed);
} }
return { return {

View File

@ -1,6 +1,22 @@
import getFunctionArity from "@babel/helper-get-function-arity"; import getFunctionArity from "@babel/helper-get-function-arity";
import template from "@babel/template"; import template from "@babel/template";
import * as t from "@babel/types"; import {
NOT_LOCAL_BINDING,
cloneNode,
identifier,
isAssignmentExpression,
isFunction,
isIdentifier,
isLiteral,
isNullLiteral,
isObjectMethod,
isObjectProperty,
isRegExpLiteral,
isTemplateLiteral,
isVariableDeclarator,
toBindingIdentifierName,
} from "@babel/types";
import type * as t from "@babel/types";
const buildPropertyMethodAssignmentWrapper = template(` const buildPropertyMethodAssignmentWrapper = template(`
(function (FUNCTION_KEY) { (function (FUNCTION_KEY) {
@ -46,15 +62,15 @@ const visitor = {
}; };
function getNameFromLiteralId(id) { function getNameFromLiteralId(id) {
if (t.isNullLiteral(id)) { if (isNullLiteral(id)) {
return "null"; return "null";
} }
if (t.isRegExpLiteral(id)) { if (isRegExpLiteral(id)) {
return `_${id.pattern}_${id.flags}`; return `_${id.pattern}_${id.flags}`;
} }
if (t.isTemplateLiteral(id)) { if (isTemplateLiteral(id)) {
return id.quasis.map(quasi => quasi.value.raw).join(""); return id.quasis.map(quasi => quasi.value.raw).join("");
} }
@ -72,7 +88,7 @@ function wrap(state, method, id, scope) {
scope.rename(id.name); scope.rename(id.name);
} else { } else {
// we don't currently support wrapping class expressions // we don't currently support wrapping class expressions
if (!t.isFunction(method)) return; if (!isFunction(method)) return;
// need to add a wrapper since we can't change the references // need to add a wrapper since we can't change the references
let build = buildPropertyMethodAssignmentWrapper; let build = buildPropertyMethodAssignmentWrapper;
@ -170,18 +186,17 @@ export default function (
if (node.id) return; if (node.id) return;
if ( if (
(t.isObjectProperty(parent) || (isObjectProperty(parent) || isObjectMethod(parent, { kind: "method" })) &&
t.isObjectMethod(parent, { kind: "method" })) && (!parent.computed || isLiteral(parent.key))
(!parent.computed || t.isLiteral(parent.key))
) { ) {
// { foo() {} }; // { foo() {} };
id = parent.key; id = parent.key;
} else if (t.isVariableDeclarator(parent)) { } else if (isVariableDeclarator(parent)) {
// let foo = function () {}; // let foo = function () {};
id = parent.id; id = parent.id;
// but not "let foo = () => {};" being converted to function expression // but not "let foo = () => {};" being converted to function expression
if (t.isIdentifier(id) && !localBinding) { if (isIdentifier(id) && !localBinding) {
const binding = scope.parent.getBinding(id.name); const binding = scope.parent.getBinding(id.name);
if ( if (
binding && binding &&
@ -189,12 +204,12 @@ export default function (
scope.getBinding(id.name) === binding scope.getBinding(id.name) === binding
) { ) {
// always going to reference this method // always going to reference this method
node.id = t.cloneNode(id); node.id = cloneNode(id);
node.id[t.NOT_LOCAL_BINDING] = true; node.id[NOT_LOCAL_BINDING] = true;
return; return;
} }
} }
} else if (t.isAssignmentExpression(parent, { operator: "=" })) { } else if (isAssignmentExpression(parent, { operator: "=" })) {
// foo = function () {}; // foo = function () {};
id = parent.left; id = parent.left;
} else if (!id) { } else if (!id) {
@ -202,9 +217,9 @@ export default function (
} }
let name; let name;
if (id && t.isLiteral(id)) { if (id && isLiteral(id)) {
name = getNameFromLiteralId(id); name = getNameFromLiteralId(id);
} else if (id && t.isIdentifier(id)) { } else if (id && isIdentifier(id)) {
name = id.name; name = id.name;
} }
@ -212,13 +227,13 @@ export default function (
return; return;
} }
name = t.toBindingIdentifierName(name); name = toBindingIdentifierName(name);
id = t.identifier(name); id = identifier(name);
// The id shouldn't be considered a local binding to the function because // The id shouldn't be considered a local binding to the function because
// we are simply trying to set the function name and not actually create // we are simply trying to set the function name and not actually create
// a local binding. // a local binding.
id[t.NOT_LOCAL_BINDING] = true; id[NOT_LOCAL_BINDING] = true;
const state = visit(node, name, scope); const state = visit(node, name, scope);
return wrap(state, node, id, scope) || node; return wrap(state, node, id, scope) || node;

View File

@ -1,10 +1,11 @@
import * as t from "@babel/types"; import { isAssignmentPattern, isRestElement } from "@babel/types";
import type * as t from "@babel/types";
export default function (node: t.Function): number { export default function (node: t.Function): number {
const params = node.params; const params = node.params;
for (let i = 0; i < params.length; i++) { for (let i = 0; i < params.length; i++) {
const param = params[i]; const param = params[i];
if (t.isAssignmentPattern(param) || t.isRestElement(param)) { if (isAssignmentPattern(param) || isRestElement(param)) {
return i; return i;
} }
} }

View File

@ -1,4 +1,9 @@
import * as t from "@babel/types"; import {
assignmentExpression,
expressionStatement,
identifier,
} from "@babel/types";
import type * as t from "@babel/types";
import type { NodePath } from "@babel/traverse"; import type { NodePath } from "@babel/traverse";
export type EmitFunction = ( export type EmitFunction = (
@ -38,14 +43,14 @@ const visitor = {
if (declar.node.init) { if (declar.node.init) {
nodes.push( nodes.push(
t.expressionStatement( expressionStatement(
t.assignmentExpression("=", declar.node.id, declar.node.init), assignmentExpression("=", declar.node.id, declar.node.init),
), ),
); );
} }
for (const name of Object.keys(declar.getBindingIdentifiers())) { for (const name of Object.keys(declar.getBindingIdentifiers())) {
state.emit(t.identifier(name), name, declar.node.init !== null); state.emit(identifier(name), name, declar.node.init !== null);
} }
} }

View File

@ -1,5 +1,28 @@
import type { NodePath, Visitor } from "@babel/traverse"; import type { NodePath, Visitor } from "@babel/traverse";
import * as t from "@babel/types"; import {
LOGICAL_OPERATORS,
arrowFunctionExpression,
assignmentExpression,
binaryExpression,
booleanLiteral,
callExpression,
cloneNode,
conditionalExpression,
identifier,
isMemberExpression,
isOptionalCallExpression,
isOptionalMemberExpression,
isUpdateExpression,
logicalExpression,
memberExpression,
nullLiteral,
numericLiteral,
optionalCallExpression,
optionalMemberExpression,
sequenceExpression,
unaryExpression,
} from "@babel/types";
import type * as t from "@babel/types";
import { willPathCastToBoolean } from "./util"; import { willPathCastToBoolean } from "./util";
class AssignmentMemoiser { class AssignmentMemoiser {
@ -22,7 +45,7 @@ class AssignmentMemoiser {
if (record.count === 0) { if (record.count === 0) {
// The `count` access is the outermost function call (hopefully), so it // The `count` access is the outermost function call (hopefully), so it
// does the assignment. // does the assignment.
return t.assignmentExpression("=", value, key); return assignmentExpression("=", value, key);
} }
return value; return value;
} }
@ -37,8 +60,8 @@ function toNonOptional(
base: t.Expression, base: t.Expression,
): t.Expression { ): t.Expression {
const { node } = path; const { node } = path;
if (t.isOptionalMemberExpression(node)) { if (isOptionalMemberExpression(node)) {
return t.memberExpression(base, node.property, node.computed); return memberExpression(base, node.property, node.computed);
} }
if (path.isOptionalCallExpression()) { if (path.isOptionalCallExpression()) {
@ -48,15 +71,15 @@ function toNonOptional(
const context = path.scope.maybeGenerateMemoised(object) || object; const context = path.scope.maybeGenerateMemoised(object) || object;
callee callee
.get("object") .get("object")
.replaceWith(t.assignmentExpression("=", context as t.LVal, object)); .replaceWith(assignmentExpression("=", context as t.LVal, object));
return t.callExpression(t.memberExpression(base, t.identifier("call")), [ return callExpression(memberExpression(base, identifier("call")), [
context, context,
...path.node.arguments, ...path.node.arguments,
]); ]);
} }
return t.callExpression(base, path.node.arguments); return callExpression(base, path.node.arguments);
} }
return path.node; return path.node;
@ -108,13 +131,13 @@ const handle = {
// actually, we can consider the `baz` access to be the end. So we're // actually, we can consider the `baz` access to be the end. So we're
// looking for the nearest optional chain that is `optional: true`. // looking for the nearest optional chain that is `optional: true`.
const endPath = member.find(({ node, parent }) => { const endPath = member.find(({ node, parent }) => {
if (t.isOptionalMemberExpression(parent)) { if (isOptionalMemberExpression(parent)) {
// We need to check `parent.object` since we could be inside the // We need to check `parent.object` since we could be inside the
// computed expression of a `bad?.[FOO?.BAR]`. In this case, the // computed expression of a `bad?.[FOO?.BAR]`. In this case, the
// endPath is the `FOO?.BAR` member itself. // endPath is the `FOO?.BAR` member itself.
return parent.optional || parent.object !== node; return parent.optional || parent.object !== node;
} }
if (t.isOptionalCallExpression(parent)) { if (isOptionalCallExpression(parent)) {
// Checking `parent.callee` since we could be in the arguments, eg // Checking `parent.callee` since we could be in the arguments, eg
// `bad?.(FOO?.BAR)`. // `bad?.(FOO?.BAR)`.
// Also skip `FOO?.BAR` in `FOO?.BAR?.()` since we need to transform the optional call to ensure proper this // Also skip `FOO?.BAR` in `FOO?.BAR?.()` since we need to transform the optional call to ensure proper this
@ -132,7 +155,7 @@ const handle = {
if (scope.path.isPattern()) { if (scope.path.isPattern()) {
endPath.replaceWith( endPath.replaceWith(
// The injected member will be queued and eventually transformed when visited // The injected member will be queued and eventually transformed when visited
t.callExpression(t.arrowFunctionExpression([], endPath.node), []), callExpression(arrowFunctionExpression([], endPath.node), []),
); );
return; return;
} }
@ -239,7 +262,7 @@ const handle = {
let context: t.Identifier; let context: t.Identifier;
const endParentPath = endPath.parentPath as NodePath<t.Expression>; const endParentPath = endPath.parentPath as NodePath<t.Expression>;
if ( if (
t.isMemberExpression(regular) && isMemberExpression(regular) &&
endParentPath.isOptionalCallExpression({ endParentPath.isOptionalCallExpression({
callee: endPath.node, callee: endPath.node,
optional: true, optional: true,
@ -248,7 +271,7 @@ const handle = {
const { object } = regular; const { object } = regular;
context = member.scope.maybeGenerateMemoised(object); context = member.scope.maybeGenerateMemoised(object);
if (context) { if (context) {
regular.object = t.assignmentExpression("=", context, object); regular.object = assignmentExpression("=", context, object);
} }
} }
@ -259,60 +282,48 @@ const handle = {
} }
const baseMemoised = baseNeedsMemoised const baseMemoised = baseNeedsMemoised
? t.assignmentExpression( ? assignmentExpression("=", cloneNode(baseRef), cloneNode(startingNode))
"=", : cloneNode(baseRef);
t.cloneNode(baseRef),
t.cloneNode(startingNode),
)
: t.cloneNode(baseRef);
if (willEndPathCastToBoolean) { if (willEndPathCastToBoolean) {
let nonNullishCheck; let nonNullishCheck;
if (noDocumentAll) { if (noDocumentAll) {
nonNullishCheck = t.binaryExpression( nonNullishCheck = binaryExpression("!=", baseMemoised, nullLiteral());
"!=",
baseMemoised,
t.nullLiteral(),
);
} else { } else {
nonNullishCheck = t.logicalExpression( nonNullishCheck = logicalExpression(
"&&", "&&",
t.binaryExpression("!==", baseMemoised, t.nullLiteral()), binaryExpression("!==", baseMemoised, nullLiteral()),
t.binaryExpression( binaryExpression(
"!==", "!==",
t.cloneNode(baseRef), cloneNode(baseRef),
scope.buildUndefinedNode(), scope.buildUndefinedNode(),
), ),
); );
} }
replacementPath.replaceWith( replacementPath.replaceWith(
t.logicalExpression("&&", nonNullishCheck, regular), logicalExpression("&&", nonNullishCheck, regular),
); );
} else { } else {
let nullishCheck; let nullishCheck;
if (noDocumentAll) { if (noDocumentAll) {
nullishCheck = t.binaryExpression( nullishCheck = binaryExpression("==", baseMemoised, nullLiteral());
"==",
baseMemoised,
t.nullLiteral(),
);
} else { } else {
nullishCheck = t.logicalExpression( nullishCheck = logicalExpression(
"||", "||",
t.binaryExpression("===", baseMemoised, t.nullLiteral()), binaryExpression("===", baseMemoised, nullLiteral()),
t.binaryExpression( binaryExpression(
"===", "===",
t.cloneNode(baseRef), cloneNode(baseRef),
scope.buildUndefinedNode(), scope.buildUndefinedNode(),
), ),
); );
} }
replacementPath.replaceWith( replacementPath.replaceWith(
t.conditionalExpression( conditionalExpression(
nullishCheck, nullishCheck,
isDeleteOperation isDeleteOperation
? t.booleanLiteral(true) ? booleanLiteral(true)
: scope.buildUndefinedNode(), : scope.buildUndefinedNode(),
regular, regular,
), ),
@ -323,14 +334,14 @@ const handle = {
if (context) { if (context) {
const endParent = endParentPath.node as t.OptionalCallExpression; const endParent = endParentPath.node as t.OptionalCallExpression;
endParentPath.replaceWith( endParentPath.replaceWith(
t.optionalCallExpression( optionalCallExpression(
t.optionalMemberExpression( optionalMemberExpression(
endParent.callee, endParent.callee,
t.identifier("call"), identifier("call"),
false, false,
true, true,
), ),
[t.cloneNode(context), ...endParent.arguments], [cloneNode(context), ...endParent.arguments],
false, false,
), ),
); );
@ -341,7 +352,7 @@ const handle = {
// MEMBER++ -> _set(MEMBER, (_ref = (+_get(MEMBER))) + 1), _ref // MEMBER++ -> _set(MEMBER, (_ref = (+_get(MEMBER))) + 1), _ref
// ++MEMBER -> _set(MEMBER, (+_get(MEMBER)) + 1) // ++MEMBER -> _set(MEMBER, (+_get(MEMBER)) + 1)
if (t.isUpdateExpression(parent, { argument: node })) { if (isUpdateExpression(parent, { argument: node })) {
if (this.simpleSet) { if (this.simpleSet) {
member.replaceWith(this.simpleSet(member)); member.replaceWith(this.simpleSet(member));
return; return;
@ -354,10 +365,10 @@ const handle = {
// assignment. // assignment.
this.memoise(member, 2); this.memoise(member, 2);
const value = t.binaryExpression( const value = binaryExpression(
operator[0] as "+" | "-", operator[0] as "+" | "-",
t.unaryExpression("+", this.get(member)), unaryExpression("+", this.get(member)),
t.numericLiteral(1), numericLiteral(1),
); );
if (prefix) { if (prefix) {
@ -367,15 +378,15 @@ const handle = {
const ref = scope.generateUidIdentifierBasedOnNode(node); const ref = scope.generateUidIdentifierBasedOnNode(node);
scope.push({ id: ref }); scope.push({ id: ref });
value.left = t.assignmentExpression( value.left = assignmentExpression(
"=", "=",
t.cloneNode(ref), cloneNode(ref),
// @ts-expect-error todo(flow->ts) value.left is possibly PrivateName, which is not usable here // @ts-expect-error todo(flow->ts) value.left is possibly PrivateName, which is not usable here
value.left, value.left,
); );
parentPath.replaceWith( parentPath.replaceWith(
t.sequenceExpression([this.set(member, value), t.cloneNode(ref)]), sequenceExpression([this.set(member, value), cloneNode(ref)]),
); );
} }
return; return;
@ -396,13 +407,13 @@ const handle = {
parentPath.replaceWith(this.set(member, value)); parentPath.replaceWith(this.set(member, value));
} else { } else {
const operatorTrunc = operator.slice(0, -1); const operatorTrunc = operator.slice(0, -1);
if (t.LOGICAL_OPERATORS.includes(operatorTrunc)) { if (LOGICAL_OPERATORS.includes(operatorTrunc)) {
// Give the state handler a chance to memoise the member, since we'll // Give the state handler a chance to memoise the member, since we'll
// reference it twice. The first access (the get) should do the memo // reference it twice. The first access (the get) should do the memo
// assignment. // assignment.
this.memoise(member, 1); this.memoise(member, 1);
parentPath.replaceWith( parentPath.replaceWith(
t.logicalExpression( logicalExpression(
operatorTrunc as t.LogicalExpression["operator"], operatorTrunc as t.LogicalExpression["operator"],
this.get(member), this.get(member),
this.set(member, value), this.set(member, value),
@ -414,7 +425,7 @@ const handle = {
parentPath.replaceWith( parentPath.replaceWith(
this.set( this.set(
member, member,
t.binaryExpression( binaryExpression(
operatorTrunc as t.BinaryExpression["operator"], operatorTrunc as t.BinaryExpression["operator"],
this.get(member), this.get(member),
value, value,
@ -440,7 +451,7 @@ const handle = {
if (scope.path.isPattern()) { if (scope.path.isPattern()) {
parentPath.replaceWith( parentPath.replaceWith(
// The injected member will be queued and eventually transformed when visited // The injected member will be queued and eventually transformed when visited
t.callExpression(t.arrowFunctionExpression([], parentPath.node), []), callExpression(arrowFunctionExpression([], parentPath.node), []),
); );
return; return;
} }

View File

@ -1,5 +1,18 @@
import assert from "assert"; import assert from "assert";
import * as t from "@babel/types"; import {
callExpression,
cloneNode,
expressionStatement,
identifier,
importDeclaration,
importDefaultSpecifier,
importNamespaceSpecifier,
importSpecifier,
memberExpression,
stringLiteral,
variableDeclaration,
variableDeclarator,
} from "@babel/types";
/** /**
* A class to track and accumulate mutations to the AST that will eventually * A class to track and accumulate mutations to the AST that will eventually
@ -28,16 +41,16 @@ export default class ImportBuilder {
import() { import() {
this._statements.push( this._statements.push(
t.importDeclaration([], t.stringLiteral(this._importedSource)), importDeclaration([], stringLiteral(this._importedSource)),
); );
return this; return this;
} }
require() { require() {
this._statements.push( this._statements.push(
t.expressionStatement( expressionStatement(
t.callExpression(t.identifier("require"), [ callExpression(identifier("require"), [
t.stringLiteral(this._importedSource), stringLiteral(this._importedSource),
]), ]),
), ),
); );
@ -50,8 +63,8 @@ export default class ImportBuilder {
const statement = this._statements[this._statements.length - 1]; const statement = this._statements[this._statements.length - 1];
assert(statement.type === "ImportDeclaration"); assert(statement.type === "ImportDeclaration");
assert(statement.specifiers.length === 0); assert(statement.specifiers.length === 0);
statement.specifiers = [t.importNamespaceSpecifier(local)]; statement.specifiers = [importNamespaceSpecifier(local)];
this._resultName = t.cloneNode(local); this._resultName = cloneNode(local);
return this; return this;
} }
default(name) { default(name) {
@ -59,8 +72,8 @@ export default class ImportBuilder {
const statement = this._statements[this._statements.length - 1]; const statement = this._statements[this._statements.length - 1];
assert(statement.type === "ImportDeclaration"); assert(statement.type === "ImportDeclaration");
assert(statement.specifiers.length === 0); assert(statement.specifiers.length === 0);
statement.specifiers = [t.importDefaultSpecifier(name)]; statement.specifiers = [importDefaultSpecifier(name)];
this._resultName = t.cloneNode(name); this._resultName = cloneNode(name);
return this; return this;
} }
named(name, importName) { named(name, importName) {
@ -70,8 +83,8 @@ export default class ImportBuilder {
const statement = this._statements[this._statements.length - 1]; const statement = this._statements[this._statements.length - 1];
assert(statement.type === "ImportDeclaration"); assert(statement.type === "ImportDeclaration");
assert(statement.specifiers.length === 0); assert(statement.specifiers.length === 0);
statement.specifiers = [t.importSpecifier(name, t.identifier(importName))]; statement.specifiers = [importSpecifier(name, identifier(importName))];
this._resultName = t.cloneNode(name); this._resultName = cloneNode(name);
return this; return this;
} }
@ -80,14 +93,13 @@ export default class ImportBuilder {
let statement = this._statements[this._statements.length - 1]; let statement = this._statements[this._statements.length - 1];
if (statement.type !== "ExpressionStatement") { if (statement.type !== "ExpressionStatement") {
assert(this._resultName); assert(this._resultName);
statement = t.expressionStatement(this._resultName); statement = expressionStatement(this._resultName);
this._statements.push(statement); this._statements.push(statement);
} }
this._statements[this._statements.length - 1] = t.variableDeclaration( this._statements[this._statements.length - 1] = variableDeclaration("var", [
"var", variableDeclarator(name, statement.expression),
[t.variableDeclarator(name, statement.expression)], ]);
); this._resultName = cloneNode(name);
this._resultName = t.cloneNode(name);
return this; return this;
} }
@ -101,10 +113,10 @@ export default class ImportBuilder {
_interop(callee) { _interop(callee) {
const statement = this._statements[this._statements.length - 1]; const statement = this._statements[this._statements.length - 1];
if (statement.type === "ExpressionStatement") { if (statement.type === "ExpressionStatement") {
statement.expression = t.callExpression(callee, [statement.expression]); statement.expression = callExpression(callee, [statement.expression]);
} else if (statement.type === "VariableDeclaration") { } else if (statement.type === "VariableDeclaration") {
assert(statement.declarations.length === 1); assert(statement.declarations.length === 1);
statement.declarations[0].init = t.callExpression(callee, [ statement.declarations[0].init = callExpression(callee, [
statement.declarations[0].init, statement.declarations[0].init,
]); ]);
} else { } else {
@ -116,15 +128,15 @@ export default class ImportBuilder {
prop(name) { prop(name) {
const statement = this._statements[this._statements.length - 1]; const statement = this._statements[this._statements.length - 1];
if (statement.type === "ExpressionStatement") { if (statement.type === "ExpressionStatement") {
statement.expression = t.memberExpression( statement.expression = memberExpression(
statement.expression, statement.expression,
t.identifier(name), identifier(name),
); );
} else if (statement.type === "VariableDeclaration") { } else if (statement.type === "VariableDeclaration") {
assert(statement.declarations.length === 1); assert(statement.declarations.length === 1);
statement.declarations[0].init = t.memberExpression( statement.declarations[0].init = memberExpression(
statement.declarations[0].init, statement.declarations[0].init,
t.identifier(name), identifier(name),
); );
} else { } else {
assert.fail("Unexpected type:" + statement.type); assert.fail("Unexpected type:" + statement.type);
@ -133,6 +145,6 @@ export default class ImportBuilder {
} }
read(name) { read(name) {
this._resultName = t.memberExpression(this._resultName, t.identifier(name)); this._resultName = memberExpression(this._resultName, identifier(name));
} }
} }

View File

@ -1,5 +1,6 @@
import assert from "assert"; import assert from "assert";
import * as t from "@babel/types"; import { numericLiteral, sequenceExpression } from "@babel/types";
import type * as t from "@babel/types";
import type { NodePath, Scope, HubInterface } from "@babel/traverse"; import type { NodePath, Scope, HubInterface } from "@babel/traverse";
import ImportBuilder from "./import-builder"; import ImportBuilder from "./import-builder";
@ -416,7 +417,7 @@ export default class ImportInjector {
ensureNoContext && ensureNoContext &&
resultName.type !== "Identifier" resultName.type !== "Identifier"
) { ) {
return t.sequenceExpression([t.numericLiteral(0), resultName]); return sequenceExpression([numericLiteral(0), resultName]);
} }
return resultName; return resultName;
} }

View File

@ -1,5 +1,20 @@
import assert from "assert"; import assert from "assert";
import * as t from "@babel/types"; import {
booleanLiteral,
callExpression,
cloneNode,
directive,
directiveLiteral,
expressionStatement,
identifier,
isIdentifier,
memberExpression,
stringLiteral,
valueToNode,
variableDeclaration,
variableDeclarator,
} from "@babel/types";
import type * as t from "@babel/types";
import template from "@babel/template"; import template from "@babel/template";
import { isModule } from "@babel/helper-module-imports"; import { isModule } from "@babel/helper-module-imports";
@ -85,7 +100,7 @@ export function rewriteModuleStatementsAndPrepareHeader(
if (!hasStrict) { if (!hasStrict) {
path.unshiftContainer( path.unshiftContainer(
"directives", "directives",
t.directive(t.directiveLiteral("use strict")), directive(directiveLiteral("use strict")),
); );
} }
} }
@ -140,10 +155,10 @@ export function wrapInterop(
} }
if (type === "node-namespace") { if (type === "node-namespace") {
return t.callExpression( return callExpression(programPath.hub.addHelper("interopRequireWildcard"), [
programPath.hub.addHelper("interopRequireWildcard"), expr,
[expr, t.booleanLiteral(true)], booleanLiteral(true),
); ]);
} else if (type === "node-default") { } else if (type === "node-default") {
return null; return null;
} }
@ -157,7 +172,7 @@ export function wrapInterop(
throw new Error(`Unknown interop: ${type}`); throw new Error(`Unknown interop: ${type}`);
} }
return t.callExpression(programPath.hub.addHelper(helper), [expr]); return callExpression(programPath.hub.addHelper(helper), [expr]);
} }
/** /**
@ -173,8 +188,8 @@ export function buildNamespaceInitStatements(
) { ) {
const statements = []; const statements = [];
let srcNamespace: t.Node = t.identifier(sourceMetadata.name); let srcNamespace: t.Node = identifier(sourceMetadata.name);
if (sourceMetadata.lazy) srcNamespace = t.callExpression(srcNamespace, []); if (sourceMetadata.lazy) srcNamespace = callExpression(srcNamespace, []);
for (const localName of sourceMetadata.importsNamespace) { for (const localName of sourceMetadata.importsNamespace) {
if (localName === sourceMetadata.name) continue; if (localName === sourceMetadata.name) continue;
@ -183,7 +198,7 @@ export function buildNamespaceInitStatements(
statements.push( statements.push(
template.statement`var NAME = SOURCE;`({ template.statement`var NAME = SOURCE;`({
NAME: localName, NAME: localName,
SOURCE: t.cloneNode(srcNamespace), SOURCE: cloneNode(srcNamespace),
}), }),
); );
} }
@ -205,14 +220,14 @@ export function buildNamespaceInitStatements(
: template.statement`EXPORTS.NAME = NAMESPACE;`)({ : template.statement`EXPORTS.NAME = NAMESPACE;`)({
EXPORTS: metadata.exportName, EXPORTS: metadata.exportName,
NAME: exportName, NAME: exportName,
NAMESPACE: t.cloneNode(srcNamespace), NAMESPACE: cloneNode(srcNamespace),
}), }),
); );
} }
if (sourceMetadata.reexportAll) { if (sourceMetadata.reexportAll) {
const statement = buildNamespaceReexport( const statement = buildNamespaceReexport(
metadata, metadata,
t.cloneNode(srcNamespace), cloneNode(srcNamespace),
constantReexports, constantReexports,
); );
statement.loc = sourceMetadata.reexportAll.loc; statement.loc = sourceMetadata.reexportAll.loc;
@ -242,24 +257,24 @@ const buildReexportsFromMeta = (
constantReexports: boolean, constantReexports: boolean,
) => { ) => {
const namespace = metadata.lazy const namespace = metadata.lazy
? t.callExpression(t.identifier(metadata.name), []) ? callExpression(identifier(metadata.name), [])
: t.identifier(metadata.name); : identifier(metadata.name);
const { stringSpecifiers } = meta; const { stringSpecifiers } = meta;
return Array.from(metadata.reexports, ([exportName, importName]) => { return Array.from(metadata.reexports, ([exportName, importName]) => {
let NAMESPACE_IMPORT: t.Expression = t.cloneNode(namespace); let NAMESPACE_IMPORT: t.Expression = cloneNode(namespace);
if (importName === "default" && metadata.interop === "node-default") { if (importName === "default" && metadata.interop === "node-default") {
// Nothing, it's ok as-is // Nothing, it's ok as-is
} else if (stringSpecifiers.has(importName)) { } else if (stringSpecifiers.has(importName)) {
NAMESPACE_IMPORT = t.memberExpression( NAMESPACE_IMPORT = memberExpression(
NAMESPACE_IMPORT, NAMESPACE_IMPORT,
t.stringLiteral(importName), stringLiteral(importName),
true, true,
); );
} else { } else {
NAMESPACE_IMPORT = t.memberExpression( NAMESPACE_IMPORT = memberExpression(
NAMESPACE_IMPORT, NAMESPACE_IMPORT,
t.identifier(importName), identifier(importName),
); );
} }
const astNodes = { const astNodes = {
@ -267,7 +282,7 @@ const buildReexportsFromMeta = (
EXPORT_NAME: exportName, EXPORT_NAME: exportName,
NAMESPACE_IMPORT, NAMESPACE_IMPORT,
}; };
if (constantReexports || t.isIdentifier(NAMESPACE_IMPORT)) { if (constantReexports || isIdentifier(NAMESPACE_IMPORT)) {
if (stringSpecifiers.has(exportName)) { if (stringSpecifiers.has(exportName)) {
return ReexportTemplate.constantComputed(astNodes); return ReexportTemplate.constantComputed(astNodes);
} else { } else {
@ -381,8 +396,8 @@ function buildExportNameListDeclaration(
return { return {
name: name.name, name: name.name,
statement: t.variableDeclaration("var", [ statement: variableDeclaration("var", [
t.variableDeclarator(name, t.valueToNode(exportedVars)), variableDeclarator(name, valueToNode(exportedVars)),
]), ]),
}; };
} }
@ -405,7 +420,7 @@ function buildExportInitializationStatements(
// No-open since these are explicitly set with the "reexports" block. // No-open since these are explicitly set with the "reexports" block.
} else if (data.kind === "hoisted") { } else if (data.kind === "hoisted") {
initStatements.push( initStatements.push(
buildInitStatement(metadata, data.names, t.identifier(localName)), buildInitStatement(metadata, data.names, identifier(localName)),
); );
} else { } else {
exportNames.push(...data.names); exportNames.push(...data.names);
@ -447,7 +462,7 @@ const InitTemplate = {
function buildInitStatement(metadata: ModuleMetadata, exportNames, initExpr) { function buildInitStatement(metadata: ModuleMetadata, exportNames, initExpr) {
const { stringSpecifiers, exportName: EXPORTS } = metadata; const { stringSpecifiers, exportName: EXPORTS } = metadata;
return t.expressionStatement( return expressionStatement(
exportNames.reduce((acc, exportName) => { exportNames.reduce((acc, exportName) => {
const params = { const params = {
EXPORTS, EXPORTS,

View File

@ -1,5 +1,23 @@
import assert from "assert"; import assert from "assert";
import * as t from "@babel/types"; import {
assignmentExpression,
callExpression,
cloneNode,
expressionStatement,
getOuterBindingIdentifiers,
identifier,
isMemberExpression,
isVariableDeclaration,
jsxIdentifier,
jsxMemberExpression,
memberExpression,
numericLiteral,
sequenceExpression,
stringLiteral,
variableDeclaration,
variableDeclarator,
} from "@babel/types";
import type * as t from "@babel/types";
import template from "@babel/template"; import template from "@babel/template";
import type { NodePath, Visitor, Scope } from "@babel/traverse"; import type { NodePath, Visitor, Scope } from "@babel/traverse";
import simplifyAccess from "@babel/helper-simple-access"; import simplifyAccess from "@babel/helper-simple-access";
@ -89,12 +107,12 @@ export default function rewriteLiveReferences(
const meta = metadata.source.get(source); const meta = metadata.source.get(source);
if (localName) { if (localName) {
if (meta.lazy) identNode = t.callExpression(identNode, []); if (meta.lazy) identNode = callExpression(identNode, []);
return identNode; return identNode;
} }
let namespace: t.Expression = t.identifier(meta.name); let namespace: t.Expression = identifier(meta.name);
if (meta.lazy) namespace = t.callExpression(namespace, []); if (meta.lazy) namespace = callExpression(namespace, []);
if (importName === "default" && meta.interop === "node-default") { if (importName === "default" && meta.interop === "node-default") {
return namespace; return namespace;
@ -102,9 +120,9 @@ export default function rewriteLiveReferences(
const computed = metadata.stringSpecifiers.has(importName); const computed = metadata.stringSpecifiers.has(importName);
return t.memberExpression( return memberExpression(
namespace, namespace,
computed ? t.stringLiteral(importName) : t.identifier(importName), computed ? stringLiteral(importName) : identifier(importName),
computed, computed,
); );
}, },
@ -128,11 +146,11 @@ const rewriteBindingInitVisitor: Visitor<RewriteBindingInitVisitorState> = {
const exportNames = exported.get(localName) || []; const exportNames = exported.get(localName) || [];
if (exportNames.length > 0) { if (exportNames.length > 0) {
const statement = t.expressionStatement( const statement = expressionStatement(
buildBindingExportAssignmentExpression( buildBindingExportAssignmentExpression(
metadata, metadata,
exportNames, exportNames,
t.identifier(localName), identifier(localName),
), ),
); );
// @ts-expect-error todo(flow->ts): avoid mutations // @ts-expect-error todo(flow->ts): avoid mutations
@ -148,11 +166,11 @@ const rewriteBindingInitVisitor: Visitor<RewriteBindingInitVisitorState> = {
const exportNames = exported.get(localName) || []; const exportNames = exported.get(localName) || [];
if (exportNames.length > 0) { if (exportNames.length > 0) {
const statement = t.expressionStatement( const statement = expressionStatement(
buildBindingExportAssignmentExpression( buildBindingExportAssignmentExpression(
metadata, metadata,
exportNames, exportNames,
t.identifier(localName), identifier(localName),
), ),
); );
// @ts-expect-error todo(flow->ts): avoid mutations // @ts-expect-error todo(flow->ts): avoid mutations
@ -175,11 +193,11 @@ const buildBindingExportAssignmentExpression = (
// class Foo {} exports.Foo = exports.Bar = Foo; // class Foo {} exports.Foo = exports.Bar = Foo;
const { stringSpecifiers } = metadata; const { stringSpecifiers } = metadata;
const computed = stringSpecifiers.has(exportName); const computed = stringSpecifiers.has(exportName);
return t.assignmentExpression( return assignmentExpression(
"=", "=",
t.memberExpression( memberExpression(
t.identifier(metadata.exportName), identifier(metadata.exportName),
computed ? t.stringLiteral(exportName) : t.identifier(exportName), computed ? stringLiteral(exportName) : identifier(exportName),
/* computed */ computed, /* computed */ computed,
), ),
expr, expr,
@ -221,17 +239,17 @@ const rewriteReferencesVisitor: Visitor<RewriteReferencesVisitorState> = {
(path.parentPath.isCallExpression({ callee: path.node }) || (path.parentPath.isCallExpression({ callee: path.node }) ||
path.parentPath.isOptionalCallExpression({ callee: path.node }) || path.parentPath.isOptionalCallExpression({ callee: path.node }) ||
path.parentPath.isTaggedTemplateExpression({ tag: path.node })) && path.parentPath.isTaggedTemplateExpression({ tag: path.node })) &&
t.isMemberExpression(ref) isMemberExpression(ref)
) { ) {
path.replaceWith(t.sequenceExpression([t.numericLiteral(0), ref])); path.replaceWith(sequenceExpression([numericLiteral(0), ref]));
} else if (path.isJSXIdentifier() && t.isMemberExpression(ref)) { } else if (path.isJSXIdentifier() && isMemberExpression(ref)) {
const { object, property } = ref; const { object, property } = ref;
path.replaceWith( path.replaceWith(
t.jsxMemberExpression( jsxMemberExpression(
// @ts-expect-error todo(flow->ts): possible bug `object` might not have a name // @ts-expect-error todo(flow->ts): possible bug `object` might not have a name
t.jsxIdentifier(object.name), jsxIdentifier(object.name),
// @ts-expect-error todo(flow->ts): possible bug `property` might not have a name // @ts-expect-error todo(flow->ts): possible bug `property` might not have a name
t.jsxIdentifier(property.name), jsxIdentifier(property.name),
), ),
); );
} else { } else {
@ -285,7 +303,7 @@ const rewriteReferencesVisitor: Visitor<RewriteReferencesVisitorState> = {
if (importData) { if (importData) {
assignment.left = buildImportReference(importData, assignment.left); assignment.left = buildImportReference(importData, assignment.left);
assignment.right = t.sequenceExpression([ assignment.right = sequenceExpression([
assignment.right, assignment.right,
buildImportThrow(localName), buildImportThrow(localName),
]); ]);
@ -309,7 +327,7 @@ const rewriteReferencesVisitor: Visitor<RewriteReferencesVisitorState> = {
const id = programScopeIds.find(localName => imported.has(localName)); const id = programScopeIds.find(localName => imported.has(localName));
if (id) { if (id) {
path.node.right = t.sequenceExpression([ path.node.right = sequenceExpression([
path.node.right, path.node.right,
buildImportThrow(id), buildImportThrow(id),
]); ]);
@ -325,16 +343,16 @@ const rewriteReferencesVisitor: Visitor<RewriteReferencesVisitorState> = {
buildBindingExportAssignmentExpression( buildBindingExportAssignmentExpression(
this.metadata, this.metadata,
exportedNames, exportedNames,
t.identifier(localName), identifier(localName),
), ),
); );
} }
}); });
if (items.length > 0) { if (items.length > 0) {
let node: t.Node = t.sequenceExpression(items); let node: t.Node = sequenceExpression(items);
if (path.parentPath.isExpressionStatement()) { if (path.parentPath.isExpressionStatement()) {
node = t.expressionStatement(node); node = expressionStatement(node);
// @ts-expect-error todo(flow->ts): avoid mutations // @ts-expect-error todo(flow->ts): avoid mutations
node._blockHoist = path.parentPath.node._blockHoist; node._blockHoist = path.parentPath.node._blockHoist;
} }
@ -352,11 +370,11 @@ const rewriteReferencesVisitor: Visitor<RewriteReferencesVisitorState> = {
const { left } = node; const { left } = node;
const { exported, imported, scope: programScope } = this; const { exported, imported, scope: programScope } = this;
if (!t.isVariableDeclaration(left)) { if (!isVariableDeclaration(left)) {
let didTransformExport = false, let didTransformExport = false,
importConstViolationName; importConstViolationName;
const loopBodyScope = path.get("body").scope; const loopBodyScope = path.get("body").scope;
for (const name of Object.keys(t.getOuterBindingIdentifiers(left))) { for (const name of Object.keys(getOuterBindingIdentifiers(left))) {
if (programScope.getBinding(name) === scope.getBinding(name)) { if (programScope.getBinding(name) === scope.getBinding(name)) {
if (exported.has(name)) { if (exported.has(name)) {
didTransformExport = true; didTransformExport = true;
@ -380,8 +398,8 @@ const rewriteReferencesVisitor: Visitor<RewriteReferencesVisitorState> = {
path path
.get("left") .get("left")
.replaceWith( .replaceWith(
t.variableDeclaration("let", [ variableDeclaration("let", [
t.variableDeclarator(t.cloneNode(newLoopId)), variableDeclarator(cloneNode(newLoopId)),
]), ]),
); );
scope.registerDeclaration(path.get("left")); scope.registerDeclaration(path.get("left"));
@ -389,13 +407,13 @@ const rewriteReferencesVisitor: Visitor<RewriteReferencesVisitorState> = {
if (didTransformExport) { if (didTransformExport) {
bodyPath.unshiftContainer( bodyPath.unshiftContainer(
"body", "body",
t.expressionStatement(t.assignmentExpression("=", left, newLoopId)), expressionStatement(assignmentExpression("=", left, newLoopId)),
); );
} }
if (importConstViolationName) { if (importConstViolationName) {
bodyPath.unshiftContainer( bodyPath.unshiftContainer(
"body", "body",
t.expressionStatement(buildImportThrow(importConstViolationName)), expressionStatement(buildImportThrow(importConstViolationName)),
); );
} }
} }

View File

@ -1,6 +1,7 @@
import { environmentVisitor } from "@babel/helper-replace-supers"; import { environmentVisitor } from "@babel/helper-replace-supers";
import traverse from "@babel/traverse"; import traverse from "@babel/traverse";
import * as t from "@babel/types"; import { numericLiteral, unaryExpression } from "@babel/types";
import type * as t from "@babel/types";
import type { NodePath, Visitor } from "@babel/traverse"; import type { NodePath, Visitor } from "@babel/traverse";
export default function rewriteThis(programPath: NodePath) { export default function rewriteThis(programPath: NodePath) {
@ -16,7 +17,7 @@ const rewriteThisVisitor: Visitor = traverse.visitors.merge([
environmentVisitor, environmentVisitor,
{ {
ThisExpression(path: NodePath<t.ThisExpression>) { ThisExpression(path: NodePath<t.ThisExpression>) {
path.replaceWith(t.unaryExpression("void", t.numericLiteral(0), true)); path.replaceWith(unaryExpression("void", numericLiteral(0), true));
}, },
}, },
]); ]);

View File

@ -1,4 +1,12 @@
import * as t from "@babel/types"; import {
callExpression,
identifier,
isIdentifier,
isSpreadElement,
memberExpression,
optionalCallExpression,
optionalMemberExpression,
} from "@babel/types";
import type { import type {
CallExpression, CallExpression,
Expression, Expression,
@ -25,33 +33,33 @@ export default function optimiseCallExpression(
): CallExpression | OptionalCallExpression { ): CallExpression | OptionalCallExpression {
if ( if (
args.length === 1 && args.length === 1 &&
t.isSpreadElement(args[0]) && isSpreadElement(args[0]) &&
t.isIdentifier(args[0].argument, { name: "arguments" }) isIdentifier(args[0].argument, { name: "arguments" })
) { ) {
// a.b?.(...arguments); // a.b?.(...arguments);
if (optional) { if (optional) {
return t.optionalCallExpression( return optionalCallExpression(
t.optionalMemberExpression(callee, t.identifier("apply"), false, true), optionalMemberExpression(callee, identifier("apply"), false, true),
[thisNode, args[0].argument], [thisNode, args[0].argument],
false, false,
); );
} }
// a.b(...arguments); // a.b(...arguments);
return t.callExpression(t.memberExpression(callee, t.identifier("apply")), [ return callExpression(memberExpression(callee, identifier("apply")), [
thisNode, thisNode,
args[0].argument, args[0].argument,
]); ]);
} else { } else {
// a.b?.(arg1, arg2) // a.b?.(arg1, arg2)
if (optional) { if (optional) {
return t.optionalCallExpression( return optionalCallExpression(
t.optionalMemberExpression(callee, t.identifier("call"), false, true), optionalMemberExpression(callee, identifier("call"), false, true),
[thisNode, ...args], [thisNode, ...args],
false, false,
); );
} }
// a.b(arg1, arg2) // a.b(arg1, arg2)
return t.callExpression(t.memberExpression(callee, t.identifier("call")), [ return callExpression(memberExpression(callee, identifier("call")), [
thisNode, thisNode,
...args, ...args,
]); ]);

View File

@ -3,7 +3,13 @@
import type { NodePath } from "@babel/traverse"; import type { NodePath } from "@babel/traverse";
import wrapFunction from "@babel/helper-wrap-function"; import wrapFunction from "@babel/helper-wrap-function";
import annotateAsPure from "@babel/helper-annotate-as-pure"; import annotateAsPure from "@babel/helper-annotate-as-pure";
import * as t from "@babel/types"; import {
callExpression,
cloneNode,
isIdentifier,
isThisExpression,
yieldExpression,
} from "@babel/types";
const awaitVisitor = { const awaitVisitor = {
Function(path) { Function(path) {
@ -19,9 +25,9 @@ const awaitVisitor = {
} }
path.replaceWith( path.replaceWith(
t.yieldExpression( yieldExpression(
wrapAwait wrapAwait
? t.callExpression(t.cloneNode(wrapAwait), [argument.node]) ? callExpression(cloneNode(wrapAwait), [argument.node])
: argument.node, : argument.node,
), ),
); );
@ -42,7 +48,7 @@ export default function (
path.node.async = false; path.node.async = false;
path.node.generator = true; path.node.generator = true;
wrapFunction(path, t.cloneNode(helpers.wrapAsync), noNewArrows); wrapFunction(path, cloneNode(helpers.wrapAsync), noNewArrows);
const isProperty = const isProperty =
path.isObjectMethod() || path.isObjectMethod() ||
@ -64,7 +70,7 @@ export default function (
const { parentPath } = path; const { parentPath } = path;
if ( if (
parentPath.isMemberExpression() && parentPath.isMemberExpression() &&
t.isIdentifier(parentPath.node.property, { name: "bind" }) isIdentifier(parentPath.node.property, { name: "bind" })
) { ) {
const { parentPath: bindCall } = parentPath; const { parentPath: bindCall } = parentPath;
@ -75,7 +81,7 @@ export default function (
bindCall.isCallExpression() && bindCall.isCallExpression() &&
// and whether its sole argument is 'this' // and whether its sole argument is 'this'
bindCall.node.arguments.length === 1 && bindCall.node.arguments.length === 1 &&
t.isThisExpression(bindCall.node.arguments[0]) && isThisExpression(bindCall.node.arguments[0]) &&
// and whether the result of the .bind(this) is being called // and whether the result of the .bind(this) is being called
bindCall.parentPath.isCallExpression({ callee: bindCall.node }) bindCall.parentPath.isCallExpression({ callee: bindCall.node })
); );

View File

@ -2,7 +2,20 @@ import type { HubInterface, NodePath, Scope } from "@babel/traverse";
import traverse from "@babel/traverse"; import traverse from "@babel/traverse";
import memberExpressionToFunctions from "@babel/helper-member-expression-to-functions"; import memberExpressionToFunctions from "@babel/helper-member-expression-to-functions";
import optimiseCall from "@babel/helper-optimise-call-expression"; import optimiseCall from "@babel/helper-optimise-call-expression";
import * as t from "@babel/types"; import {
VISITOR_KEYS,
assignmentExpression,
booleanLiteral,
callExpression,
cloneNode,
identifier,
memberExpression,
sequenceExpression,
staticBlock,
stringLiteral,
thisExpression,
} from "@babel/types";
import type * as t from "@babel/types";
/** /**
* Creates an expression which result is the proto of objectRef. * Creates an expression which result is the proto of objectRef.
@ -16,13 +29,13 @@ import * as t from "@babel/types";
* helpers.getPrototypeOf(CLASS.prototype) * helpers.getPrototypeOf(CLASS.prototype)
*/ */
function getPrototypeOfExpression(objectRef, isStatic, file, isPrivateMethod) { function getPrototypeOfExpression(objectRef, isStatic, file, isPrivateMethod) {
objectRef = t.cloneNode(objectRef); objectRef = cloneNode(objectRef);
const targetRef = const targetRef =
isStatic || isPrivateMethod isStatic || isPrivateMethod
? objectRef ? objectRef
: t.memberExpression(objectRef, t.identifier("prototype")); : memberExpression(objectRef, identifier("prototype"));
return t.callExpression(file.addHelper("getPrototypeOf"), [targetRef]); return callExpression(file.addHelper("getPrototypeOf"), [targetRef]);
} }
export function skipAllButComputedKey( export function skipAllButComputedKey(
@ -37,7 +50,7 @@ export function skipAllButComputedKey(
// So it's got a computed key. Make sure to skip every other key the // So it's got a computed key. Make sure to skip every other key the
// traversal would visit. // traversal would visit.
const keys = t.VISITOR_KEYS[path.type]; const keys = VISITOR_KEYS[path.type];
for (const key of keys) { for (const key of keys) {
if (key !== "key") path.skipKey(key); if (key !== "key") path.skipKey(key);
} }
@ -48,7 +61,7 @@ export function skipAllButComputedKey(
// Avoid using `path.scope` here // Avoid using `path.scope` here
export const environmentVisitor = { export const environmentVisitor = {
// todo (Babel 8): remove StaticBlock brand checks // todo (Babel 8): remove StaticBlock brand checks
[`${t.staticBlock ? "StaticBlock|" : ""}ClassPrivateProperty|TypeAnnotation`]( [`${staticBlock ? "StaticBlock|" : ""}ClassPrivateProperty|TypeAnnotation`](
path: NodePath, path: NodePath,
) { ) {
path.skip(); path.skip();
@ -110,14 +123,14 @@ const specHandlers = {
prop(superMember) { prop(superMember) {
const { computed, property } = superMember.node; const { computed, property } = superMember.node;
if (this.memoiser.has(property)) { if (this.memoiser.has(property)) {
return t.cloneNode(this.memoiser.get(property)); return cloneNode(this.memoiser.get(property));
} }
if (computed) { if (computed) {
return t.cloneNode(property); return cloneNode(property);
} }
return t.stringLiteral(property.name); return stringLiteral(property.name);
}, },
get(superMember) { get(superMember) {
@ -131,8 +144,8 @@ const specHandlers = {
this.file, this.file,
this.isPrivateMethod, this.isPrivateMethod,
); );
return t.callExpression(this.file.addHelper("get"), [ return callExpression(this.file.addHelper("get"), [
thisRefs.memo ? t.sequenceExpression([thisRefs.memo, proto]) : proto, thisRefs.memo ? sequenceExpression([thisRefs.memo, proto]) : proto,
this.prop(superMember), this.prop(superMember),
thisRefs.this, thisRefs.this,
]); ]);
@ -140,12 +153,12 @@ const specHandlers = {
_getThisRefs() { _getThisRefs() {
if (!this.isDerivedConstructor) { if (!this.isDerivedConstructor) {
return { this: t.thisExpression() }; return { this: thisExpression() };
} }
const thisRef = this.scope.generateDeclaredUidIdentifier("thisSuper"); const thisRef = this.scope.generateDeclaredUidIdentifier("thisSuper");
return { return {
memo: t.assignmentExpression("=", thisRef, t.thisExpression()), memo: assignmentExpression("=", thisRef, thisExpression()),
this: t.cloneNode(thisRef), this: cloneNode(thisRef),
}; };
}, },
@ -157,12 +170,12 @@ const specHandlers = {
this.file, this.file,
this.isPrivateMethod, this.isPrivateMethod,
); );
return t.callExpression(this.file.addHelper("set"), [ return callExpression(this.file.addHelper("set"), [
thisRefs.memo ? t.sequenceExpression([thisRefs.memo, proto]) : proto, thisRefs.memo ? sequenceExpression([thisRefs.memo, proto]) : proto,
this.prop(superMember), this.prop(superMember),
value, value,
thisRefs.this, thisRefs.this,
t.booleanLiteral(superMember.isInStrictMode()), booleanLiteral(superMember.isInStrictMode()),
]); ]);
}, },
@ -176,7 +189,7 @@ const specHandlers = {
const thisRefs = this._getThisRefs(); const thisRefs = this._getThisRefs();
return optimiseCall( return optimiseCall(
this._get(superMember, thisRefs), this._get(superMember, thisRefs),
t.cloneNode(thisRefs.this), cloneNode(thisRefs.this),
args, args,
false, false,
); );
@ -186,7 +199,7 @@ const specHandlers = {
const thisRefs = this._getThisRefs(); const thisRefs = this._getThisRefs();
return optimiseCall( return optimiseCall(
this._get(superMember, thisRefs), this._get(superMember, thisRefs),
t.cloneNode(thisRefs.this), cloneNode(thisRefs.this),
args, args,
true, true,
); );
@ -199,10 +212,10 @@ const looseHandlers = {
prop(superMember) { prop(superMember) {
const { property } = superMember.node; const { property } = superMember.node;
if (this.memoiser.has(property)) { if (this.memoiser.has(property)) {
return t.cloneNode(this.memoiser.get(property)); return cloneNode(this.memoiser.get(property));
} }
return t.cloneNode(property); return cloneNode(property);
}, },
get(superMember) { get(superMember) {
@ -214,24 +227,24 @@ const looseHandlers = {
if (isStatic) { if (isStatic) {
object = object =
getSuperRef() ?? getSuperRef() ??
t.memberExpression(t.identifier("Function"), t.identifier("prototype")); memberExpression(identifier("Function"), identifier("prototype"));
} else { } else {
object = t.memberExpression( object = memberExpression(
getSuperRef() ?? t.identifier("Object"), getSuperRef() ?? identifier("Object"),
t.identifier("prototype"), identifier("prototype"),
); );
} }
return t.memberExpression(object, prop, computed); return memberExpression(object, prop, computed);
}, },
set(superMember, value) { set(superMember, value) {
const { computed } = superMember.node; const { computed } = superMember.node;
const prop = this.prop(superMember); const prop = this.prop(superMember);
return t.assignmentExpression( return assignmentExpression(
"=", "=",
t.memberExpression(t.thisExpression(), prop, computed), memberExpression(thisExpression(), prop, computed),
value, value,
); );
}, },
@ -240,15 +253,15 @@ const looseHandlers = {
const { computed } = superMember.node; const { computed } = superMember.node;
const prop = this.prop(superMember); const prop = this.prop(superMember);
return t.memberExpression(t.thisExpression(), prop, computed); return memberExpression(thisExpression(), prop, computed);
}, },
call(superMember, args) { call(superMember, args) {
return optimiseCall(this.get(superMember), t.thisExpression(), args, false); return optimiseCall(this.get(superMember), thisExpression(), args, false);
}, },
optionalCall(superMember, args) { optionalCall(superMember, args) {
return optimiseCall(this.get(superMember), t.thisExpression(), args, true); return optimiseCall(this.get(superMember), thisExpression(), args, true);
}, },
}; };
@ -309,12 +322,12 @@ export default class ReplaceSupers {
declare opts: ReplaceSupersOptions; declare opts: ReplaceSupersOptions;
getObjectRef() { getObjectRef() {
return t.cloneNode(this.opts.objectRef || this.opts.getObjectRef()); return cloneNode(this.opts.objectRef || this.opts.getObjectRef());
} }
getSuperRef() { getSuperRef() {
if (this.opts.superRef) return t.cloneNode(this.opts.superRef); if (this.opts.superRef) return cloneNode(this.opts.superRef);
if (this.opts.getSuperRef) return t.cloneNode(this.opts.getSuperRef()); if (this.opts.getSuperRef) return cloneNode(this.opts.getSuperRef());
} }
replace() { replace() {

View File

@ -1,4 +1,14 @@
import * as t from "@babel/types"; import {
LOGICAL_OPERATORS,
assignmentExpression,
binaryExpression,
cloneNode,
identifier,
logicalExpression,
numericLiteral,
sequenceExpression,
unaryExpression,
} from "@babel/types";
import type { NodePath } from "@babel/traverse"; import type { NodePath } from "@babel/traverse";
export default function simplifyAccess(path: NodePath, bindingNames) { export default function simplifyAccess(path: NodePath, bindingNames) {
@ -32,18 +42,18 @@ const simpleAssignmentVisitor = {
// ++i => (i += 1); // ++i => (i += 1);
const operator = path.node.operator == "++" ? "+=" : "-="; const operator = path.node.operator == "++" ? "+=" : "-=";
path.replaceWith( path.replaceWith(
t.assignmentExpression(operator, arg.node, t.numericLiteral(1)), assignmentExpression(operator, arg.node, numericLiteral(1)),
); );
} else if (path.node.prefix) { } else if (path.node.prefix) {
// ++i => (i = (+i) + 1); // ++i => (i = (+i) + 1);
path.replaceWith( path.replaceWith(
t.assignmentExpression( assignmentExpression(
"=", "=",
t.identifier(localName), identifier(localName),
t.binaryExpression( binaryExpression(
path.node.operator[0], path.node.operator[0],
t.unaryExpression("+", arg.node), unaryExpression("+", arg.node),
t.numericLiteral(1), numericLiteral(1),
), ),
), ),
); );
@ -55,22 +65,22 @@ const simpleAssignmentVisitor = {
const varName = old.name; const varName = old.name;
path.scope.push({ id: old }); path.scope.push({ id: old });
const binary = t.binaryExpression( const binary = binaryExpression(
path.node.operator[0], path.node.operator[0],
t.identifier(varName), identifier(varName),
t.numericLiteral(1), numericLiteral(1),
); );
// i++ => (_old = (+i), i = _old + 1, _old) // i++ => (_old = (+i), i = _old + 1, _old)
path.replaceWith( path.replaceWith(
t.sequenceExpression([ sequenceExpression([
t.assignmentExpression( assignmentExpression(
"=", "=",
t.identifier(varName), identifier(varName),
t.unaryExpression("+", arg.node), unaryExpression("+", arg.node),
), ),
t.assignmentExpression("=", t.cloneNode(arg.node), binary), assignmentExpression("=", cloneNode(arg.node), binary),
t.identifier(varName), identifier(varName),
]), ]),
); );
} }
@ -101,25 +111,25 @@ const simpleAssignmentVisitor = {
} }
const operator = path.node.operator.slice(0, -1); const operator = path.node.operator.slice(0, -1);
if (t.LOGICAL_OPERATORS.includes(operator)) { if (LOGICAL_OPERATORS.includes(operator)) {
// &&, ||, ?? // &&, ||, ??
// (foo &&= bar) => (foo && foo = bar) // (foo &&= bar) => (foo && foo = bar)
path.replaceWith( path.replaceWith(
t.logicalExpression( logicalExpression(
operator, operator,
path.node.left, path.node.left,
t.assignmentExpression( assignmentExpression(
"=", "=",
t.cloneNode(path.node.left), cloneNode(path.node.left),
path.node.right, path.node.right,
), ),
), ),
); );
} else { } else {
// (foo += bar) => (foo = foo + bar) // (foo += bar) => (foo = foo + bar)
path.node.right = t.binaryExpression( path.node.right = binaryExpression(
operator, operator,
t.cloneNode(path.node.left), cloneNode(path.node.left),
path.node.right, path.node.right,
); );
path.node.operator = "="; path.node.operator = "=";

View File

@ -1,6 +1,12 @@
// @flow // @flow
import * as t from "@babel/types"; import {
isParenthesizedExpression,
isTSAsExpression,
isTSNonNullExpression,
isTSTypeAssertion,
isTypeCastExpression,
} from "@babel/types";
import type { NodePath } from "@babel/traverse"; import type { NodePath } from "@babel/traverse";
// A transparent expression wrapper is an AST node that most plugins will wish // A transparent expression wrapper is an AST node that most plugins will wish
@ -10,11 +16,11 @@ import type { NodePath } from "@babel/traverse";
// determining the callee. // determining the callee.
export function isTransparentExprWrapper(node: Node) { export function isTransparentExprWrapper(node: Node) {
return ( return (
t.isTSAsExpression(node) || isTSAsExpression(node) ||
t.isTSTypeAssertion(node) || isTSTypeAssertion(node) ||
t.isTSNonNullExpression(node) || isTSNonNullExpression(node) ||
t.isTypeCastExpression(node) || isTypeCastExpression(node) ||
t.isParenthesizedExpression(node) isParenthesizedExpression(node)
); );
} }

View File

@ -1,4 +1,11 @@
import * as t from "@babel/types"; import {
cloneNode,
exportNamedDeclaration,
exportSpecifier,
identifier,
variableDeclaration,
variableDeclarator,
} from "@babel/types";
export default function splitExportDeclaration(exportDeclaration) { export default function splitExportDeclaration(exportDeclaration) {
if (!exportDeclaration.isExportDeclaration()) { if (!exportDeclaration.isExportDeclaration()) {
@ -31,18 +38,18 @@ export default function splitExportDeclaration(exportDeclaration) {
declaration.isFunctionExpression() || declaration.isFunctionExpression() ||
declaration.isClassExpression() declaration.isClassExpression()
) { ) {
declaration.node.id = t.cloneNode(id); declaration.node.id = cloneNode(id);
} }
} }
const updatedDeclaration = standaloneDeclaration const updatedDeclaration = standaloneDeclaration
? declaration ? declaration
: t.variableDeclaration("var", [ : variableDeclaration("var", [
t.variableDeclarator(t.cloneNode(id), declaration.node), variableDeclarator(cloneNode(id), declaration.node),
]); ]);
const updatedExportDeclaration = t.exportNamedDeclaration(null, [ const updatedExportDeclaration = exportNamedDeclaration(null, [
t.exportSpecifier(t.cloneNode(id), t.identifier("default")), exportSpecifier(cloneNode(id), identifier("default")),
]); ]);
exportDeclaration.insertAfter(updatedExportDeclaration); exportDeclaration.insertAfter(updatedExportDeclaration);
@ -62,10 +69,10 @@ export default function splitExportDeclaration(exportDeclaration) {
const bindingIdentifiers = declaration.getOuterBindingIdentifiers(); const bindingIdentifiers = declaration.getOuterBindingIdentifiers();
const specifiers = Object.keys(bindingIdentifiers).map(name => { const specifiers = Object.keys(bindingIdentifiers).map(name => {
return t.exportSpecifier(t.identifier(name), t.identifier(name)); return exportSpecifier(identifier(name), identifier(name));
}); });
const aliasDeclar = t.exportNamedDeclaration(null, specifiers); const aliasDeclar = exportNamedDeclaration(null, specifiers);
exportDeclaration.insertAfter(aliasDeclar); exportDeclaration.insertAfter(aliasDeclar);
exportDeclaration.replaceWith(declaration.node); exportDeclaration.replaceWith(declaration.node);

View File

@ -1,7 +1,14 @@
import type { NodePath } from "@babel/traverse"; import type { NodePath } from "@babel/traverse";
import nameFunction from "@babel/helper-function-name"; import nameFunction from "@babel/helper-function-name";
import template from "@babel/template"; import template from "@babel/template";
import * as t from "@babel/types"; import {
blockStatement,
callExpression,
functionExpression,
isAssignmentPattern,
isRestElement,
returnStatement,
} from "@babel/types";
const buildAnonymousExpressionWrapper = template.expression(` const buildAnonymousExpressionWrapper = template.expression(`
(function () { (function () {
@ -34,16 +41,14 @@ function classOrObjectMethod(path: NodePath, callId: Object) {
const node = path.node; const node = path.node;
const body = node.body; const body = node.body;
const container = t.functionExpression( const container = functionExpression(
null, null,
[], [],
t.blockStatement(body.body), blockStatement(body.body),
true, true,
); );
body.body = [ body.body = [
t.returnStatement( returnStatement(callExpression(callExpression(callId, [container]), [])),
t.callExpression(t.callExpression(callId, [container]), []),
),
]; ];
// Regardless of whether or not the wrapped function is a an async method // Regardless of whether or not the wrapped function is a an async method
@ -77,7 +82,7 @@ function plainFunction(path: NodePath, callId: Object, noNewArrows: boolean) {
node.type = "FunctionExpression"; node.type = "FunctionExpression";
} }
const built = t.callExpression(callId, [node]); const built = callExpression(callId, [node]);
const container = wrapper({ const container = wrapper({
NAME: functionId || null, NAME: functionId || null,
REF: path.scope.generateUidIdentifier(functionId ? functionId.name : "ref"), REF: path.scope.generateUidIdentifier(functionId ? functionId.name : "ref"),
@ -85,7 +90,7 @@ function plainFunction(path: NodePath, callId: Object, noNewArrows: boolean) {
PARAMS: node.params.reduce( PARAMS: node.params.reduce(
(acc, param) => { (acc, param) => {
acc.done = acc.done =
acc.done || t.isAssignmentPattern(param) || t.isRestElement(param); acc.done || isAssignmentPattern(param) || isRestElement(param);
if (!acc.done) { if (!acc.done) {
acc.params.push(path.scope.generateUidIdentifier("x")); acc.params.push(path.scope.generateUidIdentifier("x"));

View File

@ -1,7 +1,16 @@
import type { File } from "@babel/core"; import type { File } from "@babel/core";
import type { NodePath, Visitor } from "@babel/traverse"; import type { NodePath, Visitor } from "@babel/traverse";
import traverse from "@babel/traverse"; import traverse from "@babel/traverse";
import * as t from "@babel/types"; import {
assignmentExpression,
cloneNode,
expressionStatement,
file as t_file,
identifier,
variableDeclaration,
variableDeclarator,
} from "@babel/types";
import type * as t from "@babel/types";
import helpers from "./helpers"; import helpers from "./helpers";
function makePath(path: NodePath) { function makePath(path: NodePath) {
@ -213,8 +222,8 @@ function permuteHelperAST(
exp.replaceWith(decl); exp.replaceWith(decl);
} else { } else {
exp.replaceWith( exp.replaceWith(
t.variableDeclaration("var", [ variableDeclaration("var", [
t.variableDeclarator(id, decl.node as t.Expression), variableDeclarator(id, decl.node as t.Expression),
]), ]),
); );
} }
@ -222,19 +231,19 @@ function permuteHelperAST(
if (decl.isFunctionDeclaration()) { if (decl.isFunctionDeclaration()) {
exportBindingAssignments.forEach(assignPath => { exportBindingAssignments.forEach(assignPath => {
const assign: NodePath<t.Expression> = path.get(assignPath); const assign: NodePath<t.Expression> = path.get(assignPath);
assign.replaceWith(t.assignmentExpression("=", id, assign.node)); assign.replaceWith(assignmentExpression("=", id, assign.node));
}); });
exp.replaceWith(decl); exp.replaceWith(decl);
path.pushContainer( path.pushContainer(
"body", "body",
t.expressionStatement( expressionStatement(
t.assignmentExpression("=", id, t.identifier(exportName)), assignmentExpression("=", id, identifier(exportName)),
), ),
); );
} else { } else {
exp.replaceWith( exp.replaceWith(
t.expressionStatement( expressionStatement(
t.assignmentExpression("=", id, decl.node as t.Expression), assignmentExpression("=", id, decl.node as t.Expression),
), ),
); );
} }
@ -248,7 +257,7 @@ function permuteHelperAST(
for (const path of imps) path.remove(); for (const path of imps) path.remove();
for (const path of impsBindingRefs) { for (const path of impsBindingRefs) {
const node = t.cloneNode(dependenciesRefs[path.node.name]); const node = cloneNode(dependenciesRefs[path.node.name]);
path.replaceWith(node); path.replaceWith(node);
} }
@ -285,7 +294,7 @@ function loadHelper(name: string) {
} }
const fn = (): File => { 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(
{ {

View File

@ -1,7 +1,7 @@
import { getImportSource, getRequireSource, isPolyfillSource } from "./utils"; import { getImportSource, getRequireSource, isPolyfillSource } from "./utils";
import type { NodePath } from "@babel/traverse"; import type { NodePath } from "@babel/traverse";
import * as t from "@babel/types"; import type * as t from "@babel/types";
const BABEL_POLYFILL_DEPRECATION = ` const BABEL_POLYFILL_DEPRECATION = `
\`@babel/polyfill\` is deprecated. Please, use required parts of \`core-js\` \`@babel/polyfill\` is deprecated. Please, use required parts of \`core-js\`

View File

@ -1,4 +1,10 @@
import * as t from "@babel/types"; import {
isCallExpression,
isExpressionStatement,
isIdentifier,
isStringLiteral,
} from "@babel/types";
import type * as t from "@babel/types";
import type { NodePath } from "@babel/traverse"; import type { NodePath } from "@babel/traverse";
export function getImportSource({ node }: NodePath<t.ImportDeclaration>) { export function getImportSource({ node }: NodePath<t.ImportDeclaration>) {
@ -6,14 +12,14 @@ export function getImportSource({ node }: NodePath<t.ImportDeclaration>) {
} }
export function getRequireSource({ node }: NodePath) { export function getRequireSource({ node }: NodePath) {
if (!t.isExpressionStatement(node)) return; if (!isExpressionStatement(node)) return;
const { expression } = node; const { expression } = node;
if ( if (
t.isCallExpression(expression) && isCallExpression(expression) &&
t.isIdentifier(expression.callee) && isIdentifier(expression.callee) &&
expression.callee.name === "require" && expression.callee.name === "require" &&
expression.arguments.length === 1 && expression.arguments.length === 1 &&
t.isStringLiteral(expression.arguments[0]) isStringLiteral(expression.arguments[0])
) { ) {
return expression.arguments[0].value; return expression.arguments[0].value;
} }

View File

@ -1,4 +1,5 @@
import * as t from "@babel/types"; import { assertExpressionStatement } from "@babel/types";
import type * as t from "@babel/types";
export type Formatter<T> = { export type Formatter<T> = {
code: (source: string) => string; code: (source: string) => string;
@ -58,7 +59,7 @@ export const expression: Formatter<t.Expression> = {
}, },
unwrap: ({ program }) => { unwrap: ({ program }) => {
const [stmt] = program.body; const [stmt] = program.body;
t.assertExpressionStatement(stmt); assertExpressionStatement(stmt);
return stmt.expression; return stmt.expression;
}, },
}; };

View File

@ -1,4 +1,17 @@
import * as t from "@babel/types"; import {
isCallExpression,
isExpressionStatement,
isFunction,
isIdentifier,
isJSXIdentifier,
isNewExpression,
isPlaceholder,
isStatement,
isStringLiteral,
removePropertiesDeep,
traverse,
} from "@babel/types";
import type * as t from "@babel/types";
import type { TraversalAncestors, TraversalHandler } from "@babel/types"; import type { TraversalAncestors, TraversalHandler } from "@babel/types";
import { parse } from "@babel/parser"; import { parse } from "@babel/parser";
import { codeFrameColumns } from "@babel/code-frame"; import { codeFrameColumns } from "@babel/code-frame";
@ -35,7 +48,7 @@ export default function parseAndBuildMetadata<T>(
const ast = parseWithCodeFrame(code, opts.parser, syntacticPlaceholders); const ast = parseWithCodeFrame(code, opts.parser, syntacticPlaceholders);
t.removePropertiesDeep(ast, { removePropertiesDeep(ast, {
preserveComments, preserveComments,
}); });
@ -51,7 +64,7 @@ export default function parseAndBuildMetadata<T>(
}; };
const isLegacyRef = { value: undefined }; const isLegacyRef = { value: undefined };
t.traverse(ast, placeholderVisitorHandler as TraversalHandler<any>, { traverse(ast, placeholderVisitorHandler as TraversalHandler<any>, {
syntactic, syntactic,
legacy, legacy,
isLegacyRef, isLegacyRef,
@ -73,7 +86,7 @@ function placeholderVisitorHandler(
) { ) {
let name: string; let name: string;
if (t.isPlaceholder(node)) { if (isPlaceholder(node)) {
if (state.syntacticPlaceholders === false) { if (state.syntacticPlaceholders === false) {
throw new Error( throw new Error(
"%%foo%%-style placeholders can't be used when " + "%%foo%%-style placeholders can't be used when " +
@ -85,10 +98,10 @@ function placeholderVisitorHandler(
} }
} else if (state.isLegacyRef.value === false || state.syntacticPlaceholders) { } else if (state.isLegacyRef.value === false || state.syntacticPlaceholders) {
return; return;
} else if (t.isIdentifier(node) || t.isJSXIdentifier(node)) { } else if (isIdentifier(node) || isJSXIdentifier(node)) {
name = node.name; name = node.name;
state.isLegacyRef.value = true; state.isLegacyRef.value = true;
} else if (t.isStringLiteral(node)) { } else if (isStringLiteral(node)) {
name = node.value; name = node.value;
state.isLegacyRef.value = true; state.isLegacyRef.value = true;
} else { } else {
@ -123,20 +136,20 @@ function placeholderVisitorHandler(
let type: PlaceholderType; let type: PlaceholderType;
if ( if (
t.isStringLiteral(node) || isStringLiteral(node) ||
t.isPlaceholder(node, { expectedNode: "StringLiteral" }) isPlaceholder(node, { expectedNode: "StringLiteral" })
) { ) {
type = "string"; type = "string";
} else if ( } else if (
(t.isNewExpression(parent) && key === "arguments") || (isNewExpression(parent) && key === "arguments") ||
(t.isCallExpression(parent) && key === "arguments") || (isCallExpression(parent) && key === "arguments") ||
(t.isFunction(parent) && key === "params") (isFunction(parent) && key === "params")
) { ) {
type = "param"; type = "param";
} else if (t.isExpressionStatement(parent) && !t.isPlaceholder(node)) { } else if (isExpressionStatement(parent) && !isPlaceholder(node)) {
type = "statement"; type = "statement";
ancestors = ancestors.slice(0, -1); ancestors = ancestors.slice(0, -1);
} else if (t.isStatement(node) && t.isPlaceholder(node)) { } else if (isStatement(node) && isPlaceholder(node)) {
type = "statement"; type = "statement";
} else { } else {
type = "other"; type = "other";

View File

@ -1,4 +1,15 @@
import * as t from "@babel/types"; import {
blockStatement,
cloneNode,
emptyStatement,
expressionStatement,
identifier,
isStatement,
isStringLiteral,
stringLiteral,
validate,
} from "@babel/types";
import type * as t from "@babel/types";
import type { TemplateReplacements } from "./options"; import type { TemplateReplacements } from "./options";
import type { Metadata, Placeholder } from "./parse"; import type { Metadata, Placeholder } from "./parse";
@ -7,7 +18,7 @@ export default function populatePlaceholders(
metadata: Metadata, metadata: Metadata,
replacements: TemplateReplacements, replacements: TemplateReplacements,
): t.File { ): t.File {
const ast = t.cloneNode(metadata.ast); const ast = cloneNode(metadata.ast);
if (replacements) { if (replacements) {
metadata.placeholders.forEach(placeholder => { metadata.placeholders.forEach(placeholder => {
@ -61,9 +72,9 @@ function applyReplacement(
// once to avoid injecting the same node multiple times. // once to avoid injecting the same node multiple times.
if (placeholder.isDuplicate) { if (placeholder.isDuplicate) {
if (Array.isArray(replacement)) { if (Array.isArray(replacement)) {
replacement = replacement.map(node => t.cloneNode(node)); replacement = replacement.map(node => cloneNode(node));
} else if (typeof replacement === "object") { } else if (typeof replacement === "object") {
replacement = t.cloneNode(replacement); replacement = cloneNode(replacement);
} }
} }
@ -71,41 +82,41 @@ function applyReplacement(
if (placeholder.type === "string") { if (placeholder.type === "string") {
if (typeof replacement === "string") { if (typeof replacement === "string") {
replacement = t.stringLiteral(replacement); replacement = stringLiteral(replacement);
} }
if (!replacement || !t.isStringLiteral(replacement)) { if (!replacement || !isStringLiteral(replacement)) {
throw new Error("Expected string substitution"); throw new Error("Expected string substitution");
} }
} else if (placeholder.type === "statement") { } else if (placeholder.type === "statement") {
if (index === undefined) { if (index === undefined) {
if (!replacement) { if (!replacement) {
replacement = t.emptyStatement(); replacement = emptyStatement();
} else if (Array.isArray(replacement)) { } else if (Array.isArray(replacement)) {
replacement = t.blockStatement(replacement); replacement = blockStatement(replacement);
} else if (typeof replacement === "string") { } else if (typeof replacement === "string") {
replacement = t.expressionStatement(t.identifier(replacement)); replacement = expressionStatement(identifier(replacement));
} else if (!t.isStatement(replacement)) { } else if (!isStatement(replacement)) {
replacement = t.expressionStatement(replacement as any); replacement = expressionStatement(replacement as any);
} }
} else { } else {
if (replacement && !Array.isArray(replacement)) { if (replacement && !Array.isArray(replacement)) {
if (typeof replacement === "string") { if (typeof replacement === "string") {
replacement = t.identifier(replacement); replacement = identifier(replacement);
} }
if (!t.isStatement(replacement)) { if (!isStatement(replacement)) {
replacement = t.expressionStatement(replacement as any); replacement = expressionStatement(replacement as any);
} }
} }
} }
} else if (placeholder.type === "param") { } else if (placeholder.type === "param") {
if (typeof replacement === "string") { if (typeof replacement === "string") {
replacement = t.identifier(replacement); replacement = identifier(replacement);
} }
if (index === undefined) throw new Error("Assertion failure."); if (index === undefined) throw new Error("Assertion failure.");
} else { } else {
if (typeof replacement === "string") { if (typeof replacement === "string") {
replacement = t.identifier(replacement); replacement = identifier(replacement);
} }
if (Array.isArray(replacement)) { if (Array.isArray(replacement)) {
throw new Error("Cannot replace single expression with an array."); throw new Error("Cannot replace single expression with an array.");
@ -113,7 +124,7 @@ function applyReplacement(
} }
if (index === undefined) { if (index === undefined) {
t.validate(parent, key, replacement); validate(parent, key, replacement);
(parent as any)[key] = replacement; (parent as any)[key] = replacement;
} else { } else {
@ -131,7 +142,7 @@ function applyReplacement(
items[index] = replacement; items[index] = replacement;
} }
t.validate(parent, key, items); validate(parent, key, items);
(parent as any)[key] = items; (parent as any)[key] = items;
} }
} }

View File

@ -1,5 +1,5 @@
import NodePath from "./path"; import NodePath from "./path";
import * as t from "@babel/types"; import { VISITOR_KEYS } from "@babel/types";
import type Scope from "./scope"; import type Scope from "./scope";
export default class TraversalContext { export default class TraversalContext {
@ -30,7 +30,7 @@ export default class TraversalContext {
if (opts[node.type]) return true; if (opts[node.type]) return true;
// check if we're going to traverse into this node // check if we're going to traverse into this node
const keys: Array<string> | undefined = t.VISITOR_KEYS[node.type]; const keys: Array<string> | undefined = VISITOR_KEYS[node.type];
if (!keys?.length) return false; if (!keys?.length) return false;
// we need to traverse into this node so ensure that it has children to traverse into! // we need to traverse into this node so ensure that it has children to traverse into!

View File

@ -1,6 +1,7 @@
import TraversalContext from "./context"; import TraversalContext from "./context";
import * as visitors from "./visitors"; import * as visitors from "./visitors";
import * as t from "@babel/types"; import { VISITOR_KEYS, removeProperties, traverseFast } from "@babel/types";
import type * as t from "@babel/types";
import * as cache from "./cache"; import * as cache from "./cache";
import type NodePath from "./path"; import type NodePath from "./path";
import type { default as Scope, Binding } from "./scope"; import type { default as Scope, Binding } from "./scope";
@ -57,7 +58,7 @@ function traverse(
} }
} }
if (!t.VISITOR_KEYS[parent.type]) { if (!VISITOR_KEYS[parent.type]) {
return; return;
} }
@ -73,7 +74,7 @@ traverse.verify = visitors.verify;
traverse.explode = visitors.explode; traverse.explode = visitors.explode;
traverse.cheap = function (node, enter) { traverse.cheap = function (node, enter) {
return t.traverseFast(node, enter); return traverseFast(node, enter);
}; };
traverse.node = function ( traverse.node = function (
@ -84,7 +85,7 @@ traverse.node = function (
parentPath?: NodePath, parentPath?: NodePath,
skipKeys?, skipKeys?,
) { ) {
const keys = t.VISITOR_KEYS[node.type]; const keys = VISITOR_KEYS[node.type];
if (!keys) return; if (!keys) return;
const context = new TraversalContext(scope, opts, state, parentPath); const context = new TraversalContext(scope, opts, state, parentPath);
@ -95,13 +96,13 @@ traverse.node = function (
}; };
traverse.clearNode = function (node: t.Node, opts?) { traverse.clearNode = function (node: t.Node, opts?) {
t.removeProperties(node, opts); removeProperties(node, opts);
cache.path.delete(node); cache.path.delete(node);
}; };
traverse.removeProperties = function (tree, opts?) { traverse.removeProperties = function (tree, opts?) {
t.traverseFast(tree, traverse.clearNode, opts); traverseFast(tree, traverse.clearNode, opts);
return tree; return tree;
}; };

View File

@ -1,6 +1,7 @@
// This file contains that retrieve or validate anything related to the current paths ancestry. // This file contains that retrieve or validate anything related to the current paths ancestry.
import * as t from "@babel/types"; import { VISITOR_KEYS } from "@babel/types";
import type * as t from "@babel/types";
import NodePath from "./index"; import NodePath from "./index";
/** /**
@ -88,7 +89,7 @@ export function getEarliestCommonAncestorFrom(
paths, paths,
function (deepest, i, ancestries) { function (deepest, i, ancestries) {
let earliest; let earliest;
const keys = t.VISITOR_KEYS[deepest.type]; const keys = VISITOR_KEYS[deepest.type];
for (const ancestry of ancestries) { for (const ancestry of ancestries) {
const path = ancestry[i + 1]; const path = ancestry[i + 1];

View File

@ -1,6 +1,10 @@
// This file contains methods responsible for dealing with comments. // This file contains methods responsible for dealing with comments.
import * as t from "@babel/types"; import type * as t from "@babel/types";
import type NodePath from "./index"; import type NodePath from "./index";
import {
addComment as _addComment,
addComments as _addComments,
} from "@babel/types";
/** /**
* Share comments amongst siblings. * Share comments amongst siblings.
@ -34,7 +38,7 @@ export function addComment(
content: string, content: string,
line?: boolean, line?: boolean,
) { ) {
t.addComment(this.node, type, content, line); _addComment(this.node, type, content, line);
} }
/** /**
@ -46,5 +50,5 @@ export function addComments(
type: t.CommentTypeShorthand, type: t.CommentTypeShorthand,
comments: readonly t.Comment[], comments: readonly t.Comment[],
) { ) {
t.addComments(this.node, type, comments); _addComments(this.node, type, comments);
} }

View File

@ -1,6 +1,30 @@
// This file contains methods that convert the path node into another node or some other type of data. // This file contains methods that convert the path node into another node or some other type of data.
import * as t from "@babel/types"; import {
arrowFunctionExpression,
assignmentExpression,
binaryExpression,
blockStatement,
callExpression,
conditionalExpression,
expressionStatement,
identifier,
isIdentifier,
jsxIdentifier,
memberExpression,
metaProperty,
numericLiteral,
objectExpression,
restElement,
returnStatement,
sequenceExpression,
spreadElement,
stringLiteral,
super as _super,
thisExpression,
unaryExpression,
} from "@babel/types";
import type * as t from "@babel/types";
import nameFunction from "@babel/helper-function-name"; import nameFunction from "@babel/helper-function-name";
import type NodePath from "./index"; import type NodePath from "./index";
@ -16,7 +40,7 @@ export function toComputedKey(this: NodePath) {
// @ts-expect-error todo(flow->ts) computed does not exist in ClassPrivateProperty // @ts-expect-error todo(flow->ts) computed does not exist in ClassPrivateProperty
if (!this.node.computed) { if (!this.node.computed) {
if (t.isIdentifier(key)) key = t.stringLiteral(key.name); if (isIdentifier(key)) key = stringLiteral(key.name);
} }
return key; return key;
@ -54,14 +78,14 @@ export function ensureBlock(
stringPath += ".body.0"; stringPath += ".body.0";
if (this.isFunction()) { if (this.isFunction()) {
key = "argument"; key = "argument";
statements.push(t.returnStatement(body.node as t.Expression)); statements.push(returnStatement(body.node as t.Expression));
} else { } else {
key = "expression"; key = "expression";
statements.push(t.expressionStatement(body.node as t.Expression)); statements.push(expressionStatement(body.node as t.Expression));
} }
} }
this.node.body = t.blockStatement(statements); this.node.body = blockStatement(statements);
const parentPath = this.get(stringPath) as NodePath; const parentPath = this.get(stringPath) as NodePath;
body.setup( body.setup(
parentPath, parentPath,
@ -138,29 +162,29 @@ export function arrowFunctionToExpression(
if (checkBinding) { if (checkBinding) {
this.parentPath.scope.push({ this.parentPath.scope.push({
id: checkBinding, id: checkBinding,
init: t.objectExpression([]), init: objectExpression([]),
}); });
} }
this.get("body").unshiftContainer( this.get("body").unshiftContainer(
"body", "body",
t.expressionStatement( expressionStatement(
t.callExpression(this.hub.addHelper("newArrowCheck"), [ callExpression(this.hub.addHelper("newArrowCheck"), [
t.thisExpression(), thisExpression(),
checkBinding checkBinding
? t.identifier(checkBinding.name) ? identifier(checkBinding.name)
: t.identifier(thisBinding), : identifier(thisBinding),
]), ]),
), ),
); );
this.replaceWith( this.replaceWith(
t.callExpression( callExpression(
t.memberExpression( memberExpression(
nameFunction(this, true) || this.node, nameFunction(this, true) || this.node,
t.identifier("bind"), identifier("bind"),
), ),
[checkBinding ? t.identifier(checkBinding.name) : t.thisExpression()], [checkBinding ? identifier(checkBinding.name) : thisExpression()],
), ),
); );
} }
@ -217,7 +241,7 @@ function hoistFunctionEnvironment(
}); });
const superBinding = getSuperBinding(thisEnvFn); const superBinding = getSuperBinding(thisEnvFn);
allSuperCalls.forEach(superCall => { allSuperCalls.forEach(superCall => {
const callee = t.identifier(superBinding); const callee = identifier(superBinding);
callee.loc = superCall.node.callee.loc; callee.loc = superCall.node.callee.loc;
superCall.get("callee").replaceWith(callee); superCall.get("callee").replaceWith(callee);
@ -227,13 +251,13 @@ function hoistFunctionEnvironment(
// Convert all "arguments" references in the arrow to point at the alias. // Convert all "arguments" references in the arrow to point at the alias.
if (argumentsPaths.length > 0) { if (argumentsPaths.length > 0) {
const argumentsBinding = getBinding(thisEnvFn, "arguments", () => { const argumentsBinding = getBinding(thisEnvFn, "arguments", () => {
const args = () => t.identifier("arguments"); const args = () => identifier("arguments");
if (thisEnvFn.scope.path.isProgram()) { if (thisEnvFn.scope.path.isProgram()) {
return t.conditionalExpression( return conditionalExpression(
t.binaryExpression( binaryExpression(
"===", "===",
t.unaryExpression("typeof", args()), unaryExpression("typeof", args()),
t.stringLiteral("undefined"), stringLiteral("undefined"),
), ),
thisEnvFn.scope.buildUndefinedNode(), thisEnvFn.scope.buildUndefinedNode(),
args(), args(),
@ -244,7 +268,7 @@ function hoistFunctionEnvironment(
}); });
argumentsPaths.forEach(argumentsChild => { argumentsPaths.forEach(argumentsChild => {
const argsRef = t.identifier(argumentsBinding); const argsRef = identifier(argumentsBinding);
argsRef.loc = argumentsChild.node.loc; argsRef.loc = argumentsChild.node.loc;
argumentsChild.replaceWith(argsRef); argumentsChild.replaceWith(argsRef);
@ -254,11 +278,11 @@ function hoistFunctionEnvironment(
// Convert all "new.target" references in the arrow to point at the alias. // Convert all "new.target" references in the arrow to point at the alias.
if (newTargetPaths.length > 0) { if (newTargetPaths.length > 0) {
const newTargetBinding = getBinding(thisEnvFn, "newtarget", () => const newTargetBinding = getBinding(thisEnvFn, "newtarget", () =>
t.metaProperty(t.identifier("new"), t.identifier("target")), metaProperty(identifier("new"), identifier("target")),
); );
newTargetPaths.forEach(targetChild => { newTargetPaths.forEach(targetChild => {
const targetRef = t.identifier(newTargetBinding); const targetRef = identifier(newTargetBinding);
targetRef.loc = targetChild.node.loc; targetRef.loc = targetChild.node.loc;
targetChild.replaceWith(targetRef); targetChild.replaceWith(targetRef);
@ -301,11 +325,11 @@ function hoistFunctionEnvironment(
args.push(value); args.push(value);
} }
const call = t.callExpression(t.identifier(superBinding), args); const call = callExpression(identifier(superBinding), args);
if (isCall) { if (isCall) {
superProp.parentPath.unshiftContainer("arguments", t.thisExpression()); superProp.parentPath.unshiftContainer("arguments", thisExpression());
superProp.replaceWith(t.memberExpression(call, t.identifier("call"))); superProp.replaceWith(memberExpression(call, identifier("call")));
thisPaths.push(superProp.parentPath.get("arguments.0")); thisPaths.push(superProp.parentPath.get("arguments.0"));
} else if (isAssignment) { } else if (isAssignment) {
@ -330,8 +354,8 @@ function hoistFunctionEnvironment(
) { ) {
thisPaths.forEach(thisChild => { thisPaths.forEach(thisChild => {
const thisRef = thisChild.isJSX() const thisRef = thisChild.isJSX()
? t.jsxIdentifier(thisBinding) ? jsxIdentifier(thisBinding)
: t.identifier(thisBinding); : identifier(thisBinding);
thisRef.loc = thisChild.node.loc; thisRef.loc = thisChild.node.loc;
thisChild.replaceWith(thisRef); thisChild.replaceWith(thisRef);
@ -361,9 +385,9 @@ function standardizeSuperProperty(superProp) {
assignmentPath assignmentPath
.get("left") .get("left")
.replaceWith( .replaceWith(
t.memberExpression( memberExpression(
superProp.node.object, superProp.node.object,
t.assignmentExpression("=", tmp, superProp.node.property), assignmentExpression("=", tmp, superProp.node.property),
true /* computed */, true /* computed */,
), ),
); );
@ -371,11 +395,11 @@ function standardizeSuperProperty(superProp) {
assignmentPath assignmentPath
.get("right") .get("right")
.replaceWith( .replaceWith(
t.binaryExpression( binaryExpression(
op, op,
t.memberExpression( memberExpression(
superProp.node.object, superProp.node.object,
t.identifier(tmp.name), identifier(tmp.name),
true /* computed */, true /* computed */,
), ),
value, value,
@ -385,17 +409,17 @@ function standardizeSuperProperty(superProp) {
assignmentPath assignmentPath
.get("left") .get("left")
.replaceWith( .replaceWith(
t.memberExpression(superProp.node.object, superProp.node.property), memberExpression(superProp.node.object, superProp.node.property),
); );
assignmentPath assignmentPath
.get("right") .get("right")
.replaceWith( .replaceWith(
t.binaryExpression( binaryExpression(
op, op,
t.memberExpression( memberExpression(
superProp.node.object, superProp.node.object,
t.identifier(superProp.node.property.name), identifier(superProp.node.property.name),
), ),
value, value,
), ),
@ -414,35 +438,33 @@ function standardizeSuperProperty(superProp) {
: null; : null;
const parts: t.Expression[] = [ const parts: t.Expression[] = [
t.assignmentExpression( assignmentExpression(
"=", "=",
tmp, tmp,
t.memberExpression( memberExpression(
superProp.node.object, superProp.node.object,
computedKey computedKey
? t.assignmentExpression("=", computedKey, superProp.node.property) ? assignmentExpression("=", computedKey, superProp.node.property)
: superProp.node.property, : superProp.node.property,
superProp.node.computed, superProp.node.computed,
), ),
), ),
t.assignmentExpression( assignmentExpression(
"=", "=",
t.memberExpression( memberExpression(
superProp.node.object, superProp.node.object,
computedKey computedKey ? identifier(computedKey.name) : superProp.node.property,
? t.identifier(computedKey.name)
: superProp.node.property,
superProp.node.computed, superProp.node.computed,
), ),
t.binaryExpression("+", t.identifier(tmp.name), t.numericLiteral(1)), binaryExpression("+", identifier(tmp.name), numericLiteral(1)),
), ),
]; ];
if (!superProp.parentPath.node.prefix) { if (!superProp.parentPath.node.prefix) {
parts.push(t.identifier(tmp.name)); parts.push(identifier(tmp.name));
} }
updateExpr.replaceWith(t.sequenceExpression(parts)); updateExpr.replaceWith(sequenceExpression(parts));
const left = updateExpr.get("expressions.0.right"); const left = updateExpr.get("expressions.0.right");
const right = updateExpr.get("expressions.1.left"); const right = updateExpr.get("expressions.1.left");
@ -462,7 +484,7 @@ function hasSuperClass(thisEnvFn) {
// Create a binding that evaluates to the "this" of the given function. // Create a binding that evaluates to the "this" of the given function.
function getThisBinding(thisEnvFn, inConstructor) { function getThisBinding(thisEnvFn, inConstructor) {
return getBinding(thisEnvFn, "this", thisBinding => { return getBinding(thisEnvFn, "this", thisBinding => {
if (!inConstructor || !hasSuperClass(thisEnvFn)) return t.thisExpression(); if (!inConstructor || !hasSuperClass(thisEnvFn)) return thisExpression();
const supers = new WeakSet(); const supers = new WeakSet();
thisEnvFn.traverse({ thisEnvFn.traverse({
@ -480,10 +502,10 @@ function getThisBinding(thisEnvFn, inConstructor) {
child.replaceWithMultiple([ child.replaceWithMultiple([
child.node, child.node,
t.assignmentExpression( assignmentExpression(
"=", "=",
t.identifier(thisBinding), identifier(thisBinding),
t.identifier("this"), identifier("this"),
), ),
]); ]);
}, },
@ -495,11 +517,9 @@ function getThisBinding(thisEnvFn, inConstructor) {
function getSuperBinding(thisEnvFn) { function getSuperBinding(thisEnvFn) {
return getBinding(thisEnvFn, "supercall", () => { return getBinding(thisEnvFn, "supercall", () => {
const argsBinding = thisEnvFn.scope.generateUidIdentifier("args"); const argsBinding = thisEnvFn.scope.generateUidIdentifier("args");
return t.arrowFunctionExpression( return arrowFunctionExpression(
[t.restElement(argsBinding)], [restElement(argsBinding)],
t.callExpression(t.super(), [ callExpression(_super(), [spreadElement(identifier(argsBinding.name))]),
t.spreadElement(t.identifier(argsBinding.name)),
]),
); );
}); });
} }
@ -514,14 +534,14 @@ function getSuperPropBinding(thisEnvFn, isAssignment, propName) {
let fnBody; let fnBody;
if (propName) { if (propName) {
// () => super.foo // () => super.foo
fnBody = t.memberExpression(t.super(), t.identifier(propName)); fnBody = memberExpression(_super(), identifier(propName));
} else { } else {
const method = thisEnvFn.scope.generateUidIdentifier("prop"); const method = thisEnvFn.scope.generateUidIdentifier("prop");
// (method) => super[method] // (method) => super[method]
argsList.unshift(method); argsList.unshift(method);
fnBody = t.memberExpression( fnBody = memberExpression(
t.super(), _super(),
t.identifier(method.name), identifier(method.name),
true /* computed */, true /* computed */,
); );
} }
@ -530,14 +550,10 @@ function getSuperPropBinding(thisEnvFn, isAssignment, propName) {
const valueIdent = thisEnvFn.scope.generateUidIdentifier("value"); const valueIdent = thisEnvFn.scope.generateUidIdentifier("value");
argsList.push(valueIdent); argsList.push(valueIdent);
fnBody = t.assignmentExpression( fnBody = assignmentExpression("=", fnBody, identifier(valueIdent.name));
"=",
fnBody,
t.identifier(valueIdent.name),
);
} }
return t.arrowFunctionExpression(argsList, fnBody); return arrowFunctionExpression(argsList, fnBody);
}); });
} }

View File

@ -2,7 +2,14 @@
import type TraversalContext from "../context"; import type TraversalContext from "../context";
import NodePath from "./index"; import NodePath from "./index";
import * as t from "@babel/types"; import {
getBindingIdentifiers as _getBindingIdentifiers,
getOuterBindingIdentifiers as _getOuterBindingIdentifiers,
isDeclaration,
numericLiteral,
unaryExpression,
} from "@babel/types";
import type * as t from "@babel/types";
const NORMAL_COMPLETION = 0; const NORMAL_COMPLETION = 0;
const BREAK_COMPLETION = 1; const BREAK_COMPLETION = 1;
@ -107,7 +114,7 @@ function replaceBreakStatementInBreakCompletion(
completions.forEach(c => { completions.forEach(c => {
if (c.path.isBreakStatement({ label: null })) { if (c.path.isBreakStatement({ label: null })) {
if (reachable) { if (reachable) {
c.path.replaceWith(t.unaryExpression("void", t.numericLiteral(0))); c.path.replaceWith(unaryExpression("void", numericLiteral(0)));
} else { } else {
c.path.remove(); c.path.remove();
} }
@ -463,7 +470,7 @@ function getBindingIdentifiers(
function getBindingIdentifiers( function getBindingIdentifiers(
duplicates?: boolean, duplicates?: boolean,
): Record<string, t.Identifier[] | t.Identifier> { ): Record<string, t.Identifier[] | t.Identifier> {
return t.getBindingIdentifiers(this.node, duplicates); return _getBindingIdentifiers(this.node, duplicates);
} }
export { getBindingIdentifiers }; export { getBindingIdentifiers };
@ -481,7 +488,7 @@ function getOuterBindingIdentifiers(
function getOuterBindingIdentifiers( function getOuterBindingIdentifiers(
duplicates?: boolean, duplicates?: boolean,
): Record<string, t.Identifier[] | t.Identifier> { ): Record<string, t.Identifier[] | t.Identifier> {
return t.getOuterBindingIdentifiers(this.node, duplicates); return _getOuterBindingIdentifiers(this.node, duplicates);
} }
export { getOuterBindingIdentifiers }; export { getOuterBindingIdentifiers };
@ -505,7 +512,7 @@ export function getBindingIdentifierPaths(
if (!id) continue; if (!id) continue;
if (!id.node) continue; if (!id.node) continue;
const keys = t.getBindingIdentifiers.keys[id.node.type]; const keys = _getBindingIdentifiers.keys[id.node.type];
if (id.isIdentifier()) { if (id.isIdentifier()) {
if (duplicates) { if (duplicates) {
@ -519,7 +526,7 @@ export function getBindingIdentifierPaths(
if (id.isExportDeclaration()) { if (id.isExportDeclaration()) {
const declaration = id.get("declaration"); const declaration = id.get("declaration");
if (t.isDeclaration(declaration)) { if (isDeclaration(declaration)) {
search.push(declaration); search.push(declaration);
} }
continue; continue;

View File

@ -5,6 +5,7 @@ import buildDebug from "debug";
import traverse from "../index"; import traverse from "../index";
import type { Visitor } from "../types"; import type { Visitor } from "../types";
import Scope from "../scope"; import Scope from "../scope";
import { validate } from "@babel/types";
import * as t from "@babel/types"; import * as t from "@babel/types";
import { path as pathCache } from "../cache"; import { path as pathCache } from "../cache";
import generator from "@babel/generator"; import generator from "@babel/generator";
@ -135,7 +136,7 @@ class NodePath<T extends t.Node = t.Node> {
} }
set(key: string, node: any) { set(key: string, node: any) {
t.validate(this.node, key, node); validate(this.node, key, node);
this.node[key] = node; this.node[key] = node;
} }
@ -225,6 +226,10 @@ Object.assign(
NodePath_comments, NodePath_comments,
); );
// we can not use `import { TYPES } from "@babel/types"` here
// because the transformNamedBabelTypesImportToDestructuring plugin in babel.config.js
// does not offer live bindings for `TYPES`
// we can change to `import { TYPES }` when we are publishing ES modules only
for (const type of t.TYPES) { for (const type of t.TYPES) {
const typeKey = `is${type}`; const typeKey = `is${type}`;
const fn = t[typeKey]; const fn = t[typeKey];

View File

@ -1,6 +1,23 @@
import type NodePath from "../index"; import type NodePath from "../index";
import * as inferers from "./inferers"; import * as inferers from "./inferers";
import * as t from "@babel/types"; import {
anyTypeAnnotation,
isAnyTypeAnnotation,
isBooleanTypeAnnotation,
isEmptyTypeAnnotation,
isFlowBaseAnnotation,
isGenericTypeAnnotation,
isIdentifier,
isMixedTypeAnnotation,
isNumberTypeAnnotation,
isStringTypeAnnotation,
isTypeAnnotation,
isUnionTypeAnnotation,
isVoidTypeAnnotation,
stringTypeAnnotation,
voidTypeAnnotation,
} from "@babel/types";
import type * as t from "@babel/types";
/** /**
* Infer the type of the current `NodePath`. * Infer the type of the current `NodePath`.
@ -9,8 +26,8 @@ import * as t from "@babel/types";
export function getTypeAnnotation(): t.FlowType { export function getTypeAnnotation(): t.FlowType {
if (this.typeAnnotation) return this.typeAnnotation; if (this.typeAnnotation) return this.typeAnnotation;
let type = this._getTypeAnnotation() || t.anyTypeAnnotation(); let type = this._getTypeAnnotation() || anyTypeAnnotation();
if (t.isTypeAnnotation(type)) type = type.typeAnnotation; if (isTypeAnnotation(type)) type = type.typeAnnotation;
return (this.typeAnnotation = type); return (this.typeAnnotation = type);
} }
@ -34,15 +51,15 @@ export function _getTypeAnnotation(): any {
// for (let NODE in bar) {} // for (let NODE in bar) {}
if (declar.key === "left" && declarParent.isForInStatement()) { if (declar.key === "left" && declarParent.isForInStatement()) {
return t.stringTypeAnnotation(); return stringTypeAnnotation();
} }
// for (let NODE of bar) {} // for (let NODE of bar) {}
if (declar.key === "left" && declarParent.isForOfStatement()) { if (declar.key === "left" && declarParent.isForOfStatement()) {
return t.anyTypeAnnotation(); return anyTypeAnnotation();
} }
return t.voidTypeAnnotation(); return voidTypeAnnotation();
} else { } else {
return; return;
} }
@ -79,19 +96,19 @@ export function isBaseType(baseName: string, soft?: boolean): boolean {
function _isBaseType(baseName: string, type?, soft?): boolean { function _isBaseType(baseName: string, type?, soft?): boolean {
if (baseName === "string") { if (baseName === "string") {
return t.isStringTypeAnnotation(type); return isStringTypeAnnotation(type);
} else if (baseName === "number") { } else if (baseName === "number") {
return t.isNumberTypeAnnotation(type); return isNumberTypeAnnotation(type);
} else if (baseName === "boolean") { } else if (baseName === "boolean") {
return t.isBooleanTypeAnnotation(type); return isBooleanTypeAnnotation(type);
} else if (baseName === "any") { } else if (baseName === "any") {
return t.isAnyTypeAnnotation(type); return isAnyTypeAnnotation(type);
} else if (baseName === "mixed") { } else if (baseName === "mixed") {
return t.isMixedTypeAnnotation(type); return isMixedTypeAnnotation(type);
} else if (baseName === "empty") { } else if (baseName === "empty") {
return t.isEmptyTypeAnnotation(type); return isEmptyTypeAnnotation(type);
} else if (baseName === "void") { } else if (baseName === "void") {
return t.isVoidTypeAnnotation(type); return isVoidTypeAnnotation(type);
} else { } else {
if (soft) { if (soft) {
return false; return false;
@ -103,11 +120,11 @@ function _isBaseType(baseName: string, type?, soft?): boolean {
export function couldBeBaseType(name: string): boolean { export function couldBeBaseType(name: string): boolean {
const type = this.getTypeAnnotation(); const type = this.getTypeAnnotation();
if (t.isAnyTypeAnnotation(type)) return true; if (isAnyTypeAnnotation(type)) return true;
if (t.isUnionTypeAnnotation(type)) { if (isUnionTypeAnnotation(type)) {
for (const type2 of type.types) { for (const type2 of type.types) {
if (t.isAnyTypeAnnotation(type2) || _isBaseType(name, type2, true)) { if (isAnyTypeAnnotation(type2) || _isBaseType(name, type2, true)) {
return true; return true;
} }
} }
@ -121,7 +138,7 @@ export function baseTypeStrictlyMatches(rightArg: NodePath): boolean {
const left = this.getTypeAnnotation(); const left = this.getTypeAnnotation();
const right = rightArg.getTypeAnnotation(); const right = rightArg.getTypeAnnotation();
if (!t.isAnyTypeAnnotation(left) && t.isFlowBaseAnnotation(left)) { if (!isAnyTypeAnnotation(left) && isFlowBaseAnnotation(left)) {
return right.type === left.type; return right.type === left.type;
} }
return false; return false;
@ -130,7 +147,7 @@ export function baseTypeStrictlyMatches(rightArg: NodePath): boolean {
export function isGenericType(genericName: string): boolean { export function isGenericType(genericName: string): boolean {
const type = this.getTypeAnnotation(); const type = this.getTypeAnnotation();
return ( return (
t.isGenericTypeAnnotation(type) && isGenericTypeAnnotation(type) &&
t.isIdentifier(type.id, { name: genericName }) isIdentifier(type.id, { name: genericName })
); );
} }

View File

@ -1,5 +1,15 @@
import type NodePath from "../index"; import type NodePath from "../index";
import * as t from "@babel/types"; import {
BOOLEAN_NUMBER_BINARY_OPERATORS,
createFlowUnionType,
createTSUnionType,
createTypeAnnotationBasedOnTypeof,
createUnionTypeAnnotation,
isTSTypeAnnotation,
numberTypeAnnotation,
voidTypeAnnotation,
} from "@babel/types";
import type * as t from "@babel/types";
import type Binding from "../../scope/binding"; import type Binding from "../../scope/binding";
export default function (node: any) { export default function (node: any) {
@ -22,9 +32,9 @@ export default function (node: any) {
// built-in values // built-in values
if (node.name === "undefined") { if (node.name === "undefined") {
return t.voidTypeAnnotation(); return voidTypeAnnotation();
} else if (node.name === "NaN" || node.name === "Infinity") { } else if (node.name === "NaN" || node.name === "Infinity") {
return t.numberTypeAnnotation(); return numberTypeAnnotation();
} else if (node.name === "arguments") { } else if (node.name === "arguments") {
// todo // todo
} }
@ -96,15 +106,15 @@ function getTypeAnnotationBindingConstantViolations(binding, path, name) {
return; return;
} }
if (t.isTSTypeAnnotation(types[0]) && t.createTSUnionType) { if (isTSTypeAnnotation(types[0]) && createTSUnionType) {
return t.createTSUnionType(types); return createTSUnionType(types);
} }
if (t.createFlowUnionType) { if (createFlowUnionType) {
return t.createFlowUnionType(types); return createFlowUnionType(types);
} }
return t.createUnionTypeAnnotation(types); return createUnionTypeAnnotation(types);
} }
function getConstantViolationsBefore(binding: Binding, path, functions?) { function getConstantViolationsBefore(binding: Binding, path, functions?) {
@ -138,8 +148,8 @@ function inferAnnotationFromBinaryExpression(
if (operator === "===") { if (operator === "===") {
return target.getTypeAnnotation(); return target.getTypeAnnotation();
} }
if (t.BOOLEAN_NUMBER_BINARY_OPERATORS.indexOf(operator) >= 0) { if (BOOLEAN_NUMBER_BINARY_OPERATORS.indexOf(operator) >= 0) {
return t.numberTypeAnnotation(); return numberTypeAnnotation();
} }
return; return;
@ -173,7 +183,7 @@ function inferAnnotationFromBinaryExpression(
// turn type value into a type annotation // 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 // @ts-expect-error todo(flow->ts): move validation from helper or relax type constraint to just a string
return t.createTypeAnnotationBasedOnTypeof(typeValue); return createTypeAnnotationBasedOnTypeof(typeValue);
} }
function getParentConditionalPath(binding, path, name) { function getParentConditionalPath(binding, path, name) {
@ -221,22 +231,22 @@ function getConditionalAnnotation<T extends t.Node>(
} }
if (types.length) { if (types.length) {
if (t.isTSTypeAnnotation(types[0]) && t.createTSUnionType) { if (isTSTypeAnnotation(types[0]) && createTSUnionType) {
return { return {
typeAnnotation: t.createTSUnionType(types), typeAnnotation: createTSUnionType(types),
ifStatement, ifStatement,
}; };
} }
if (t.createFlowUnionType) { if (createFlowUnionType) {
return { return {
typeAnnotation: t.createFlowUnionType(types), typeAnnotation: createFlowUnionType(types),
ifStatement, ifStatement,
}; };
} }
return { return {
typeAnnotation: t.createUnionTypeAnnotation(types), typeAnnotation: createUnionTypeAnnotation(types),
ifStatement, ifStatement,
}; };
} }

View File

@ -1,4 +1,26 @@
import * as t from "@babel/types"; import {
BOOLEAN_BINARY_OPERATORS,
BOOLEAN_UNARY_OPERATORS,
NUMBER_BINARY_OPERATORS,
NUMBER_UNARY_OPERATORS,
STRING_UNARY_OPERATORS,
anyTypeAnnotation,
arrayTypeAnnotation,
booleanTypeAnnotation,
buildMatchMemberExpression,
createFlowUnionType,
createTSUnionType,
createUnionTypeAnnotation,
genericTypeAnnotation,
identifier,
isTSTypeAnnotation,
nullLiteralTypeAnnotation,
numberTypeAnnotation,
stringTypeAnnotation,
tupleTypeAnnotation,
unionTypeAnnotation,
voidTypeAnnotation,
} from "@babel/types";
export { default as Identifier } from "./inferer-reference"; export { default as Identifier } from "./inferer-reference";
@ -33,51 +55,51 @@ TypeCastExpression.validParent = true;
export function NewExpression(node) { export function NewExpression(node) {
if (this.get("callee").isIdentifier()) { if (this.get("callee").isIdentifier()) {
// only resolve identifier callee // only resolve identifier callee
return t.genericTypeAnnotation(node.callee); return genericTypeAnnotation(node.callee);
} }
} }
export function TemplateLiteral() { export function TemplateLiteral() {
return t.stringTypeAnnotation(); return stringTypeAnnotation();
} }
export function UnaryExpression(node) { export function UnaryExpression(node) {
const operator = node.operator; const operator = node.operator;
if (operator === "void") { if (operator === "void") {
return t.voidTypeAnnotation(); return voidTypeAnnotation();
} else if (t.NUMBER_UNARY_OPERATORS.indexOf(operator) >= 0) { } else if (NUMBER_UNARY_OPERATORS.indexOf(operator) >= 0) {
return t.numberTypeAnnotation(); return numberTypeAnnotation();
} else if (t.STRING_UNARY_OPERATORS.indexOf(operator) >= 0) { } else if (STRING_UNARY_OPERATORS.indexOf(operator) >= 0) {
return t.stringTypeAnnotation(); return stringTypeAnnotation();
} else if (t.BOOLEAN_UNARY_OPERATORS.indexOf(operator) >= 0) { } else if (BOOLEAN_UNARY_OPERATORS.indexOf(operator) >= 0) {
return t.booleanTypeAnnotation(); return booleanTypeAnnotation();
} }
} }
export function BinaryExpression(node) { export function BinaryExpression(node) {
const operator = node.operator; const operator = node.operator;
if (t.NUMBER_BINARY_OPERATORS.indexOf(operator) >= 0) { if (NUMBER_BINARY_OPERATORS.indexOf(operator) >= 0) {
return t.numberTypeAnnotation(); return numberTypeAnnotation();
} else if (t.BOOLEAN_BINARY_OPERATORS.indexOf(operator) >= 0) { } else if (BOOLEAN_BINARY_OPERATORS.indexOf(operator) >= 0) {
return t.booleanTypeAnnotation(); return booleanTypeAnnotation();
} else if (operator === "+") { } else if (operator === "+") {
const right = this.get("right"); const right = this.get("right");
const left = this.get("left"); const left = this.get("left");
if (left.isBaseType("number") && right.isBaseType("number")) { if (left.isBaseType("number") && right.isBaseType("number")) {
// both numbers so this will be a number // both numbers so this will be a number
return t.numberTypeAnnotation(); return numberTypeAnnotation();
} else if (left.isBaseType("string") || right.isBaseType("string")) { } else if (left.isBaseType("string") || right.isBaseType("string")) {
// one is a string so the result will be a string // one is a string so the result will be a string
return t.stringTypeAnnotation(); return stringTypeAnnotation();
} }
// unsure if left and right are strings or numbers so stay on the safe side // unsure if left and right are strings or numbers so stay on the safe side
return t.unionTypeAnnotation([ return unionTypeAnnotation([
t.stringTypeAnnotation(), stringTypeAnnotation(),
t.numberTypeAnnotation(), numberTypeAnnotation(),
]); ]);
} }
} }
@ -88,15 +110,15 @@ export function LogicalExpression() {
this.get("right").getTypeAnnotation(), this.get("right").getTypeAnnotation(),
]; ];
if (t.isTSTypeAnnotation(argumentTypes[0]) && t.createTSUnionType) { if (isTSTypeAnnotation(argumentTypes[0]) && createTSUnionType) {
return t.createTSUnionType(argumentTypes); return createTSUnionType(argumentTypes);
} }
if (t.createFlowUnionType) { if (createFlowUnionType) {
return t.createFlowUnionType(argumentTypes); return createFlowUnionType(argumentTypes);
} }
return t.createUnionTypeAnnotation(argumentTypes); return createUnionTypeAnnotation(argumentTypes);
} }
export function ConditionalExpression() { export function ConditionalExpression() {
@ -105,15 +127,15 @@ export function ConditionalExpression() {
this.get("alternate").getTypeAnnotation(), this.get("alternate").getTypeAnnotation(),
]; ];
if (t.isTSTypeAnnotation(argumentTypes[0]) && t.createTSUnionType) { if (isTSTypeAnnotation(argumentTypes[0]) && createTSUnionType) {
return t.createTSUnionType(argumentTypes); return createTSUnionType(argumentTypes);
} }
if (t.createFlowUnionType) { if (createFlowUnionType) {
return t.createFlowUnionType(argumentTypes); return createFlowUnionType(argumentTypes);
} }
return t.createUnionTypeAnnotation(argumentTypes); return createUnionTypeAnnotation(argumentTypes);
} }
export function SequenceExpression() { export function SequenceExpression() {
@ -131,36 +153,36 @@ export function AssignmentExpression() {
export function UpdateExpression(node) { export function UpdateExpression(node) {
const operator = node.operator; const operator = node.operator;
if (operator === "++" || operator === "--") { if (operator === "++" || operator === "--") {
return t.numberTypeAnnotation(); return numberTypeAnnotation();
} }
} }
export function StringLiteral() { export function StringLiteral() {
return t.stringTypeAnnotation(); return stringTypeAnnotation();
} }
export function NumericLiteral() { export function NumericLiteral() {
return t.numberTypeAnnotation(); return numberTypeAnnotation();
} }
export function BooleanLiteral() { export function BooleanLiteral() {
return t.booleanTypeAnnotation(); return booleanTypeAnnotation();
} }
export function NullLiteral() { export function NullLiteral() {
return t.nullLiteralTypeAnnotation(); return nullLiteralTypeAnnotation();
} }
export function RegExpLiteral() { export function RegExpLiteral() {
return t.genericTypeAnnotation(t.identifier("RegExp")); return genericTypeAnnotation(identifier("RegExp"));
} }
export function ObjectExpression() { export function ObjectExpression() {
return t.genericTypeAnnotation(t.identifier("Object")); return genericTypeAnnotation(identifier("Object"));
} }
export function ArrayExpression() { export function ArrayExpression() {
return t.genericTypeAnnotation(t.identifier("Array")); return genericTypeAnnotation(identifier("Array"));
} }
export function RestElement() { export function RestElement() {
@ -170,7 +192,7 @@ export function RestElement() {
RestElement.validParent = true; RestElement.validParent = true;
function Func() { function Func() {
return t.genericTypeAnnotation(t.identifier("Function")); return genericTypeAnnotation(identifier("Function"));
} }
export { export {
@ -181,19 +203,19 @@ export {
Func as ClassDeclaration, Func as ClassDeclaration,
}; };
const isArrayFrom = t.buildMatchMemberExpression("Array.from"); const isArrayFrom = buildMatchMemberExpression("Array.from");
const isObjectKeys = t.buildMatchMemberExpression("Object.keys"); const isObjectKeys = buildMatchMemberExpression("Object.keys");
const isObjectValues = t.buildMatchMemberExpression("Object.values"); const isObjectValues = buildMatchMemberExpression("Object.values");
const isObjectEntries = t.buildMatchMemberExpression("Object.entries"); const isObjectEntries = buildMatchMemberExpression("Object.entries");
export function CallExpression() { export function CallExpression() {
const { callee } = this.node; const { callee } = this.node;
if (isObjectKeys(callee)) { if (isObjectKeys(callee)) {
return t.arrayTypeAnnotation(t.stringTypeAnnotation()); return arrayTypeAnnotation(stringTypeAnnotation());
} else if (isArrayFrom(callee) || isObjectValues(callee)) { } else if (isArrayFrom(callee) || isObjectValues(callee)) {
return t.arrayTypeAnnotation(t.anyTypeAnnotation()); return arrayTypeAnnotation(anyTypeAnnotation());
} else if (isObjectEntries(callee)) { } else if (isObjectEntries(callee)) {
return t.arrayTypeAnnotation( return arrayTypeAnnotation(
t.tupleTypeAnnotation([t.stringTypeAnnotation(), t.anyTypeAnnotation()]), tupleTypeAnnotation([stringTypeAnnotation(), anyTypeAnnotation()]),
); );
} }
@ -210,9 +232,9 @@ function resolveCall(callee) {
if (callee.isFunction()) { if (callee.isFunction()) {
if (callee.is("async")) { if (callee.is("async")) {
if (callee.is("generator")) { if (callee.is("generator")) {
return t.genericTypeAnnotation(t.identifier("AsyncIterator")); return genericTypeAnnotation(identifier("AsyncIterator"));
} else { } else {
return t.genericTypeAnnotation(t.identifier("Promise")); return genericTypeAnnotation(identifier("Promise"));
} }
} else { } else {
if (callee.node.returnType) { if (callee.node.returnType) {

View File

@ -1,7 +1,18 @@
// This file contains methods responsible for introspecting the current path for certain values. // This file contains methods responsible for introspecting the current path for certain values.
import type NodePath from "./index"; import type NodePath from "./index";
import * as t from "@babel/types"; import {
STATEMENT_OR_BLOCK_KEYS,
VISITOR_KEYS,
isBlockStatement,
isExpression,
isIdentifier,
isLiteral,
isStringLiteral,
isType,
matchesPattern as _matchesPattern,
} from "@babel/types";
import type * as t from "@babel/types";
/** /**
* Match the current node if it matches the provided `pattern`. * Match the current node if it matches the provided `pattern`.
@ -15,7 +26,7 @@ export function matchesPattern(
pattern: string, pattern: string,
allowPartial?: boolean, allowPartial?: boolean,
): boolean { ): boolean {
return t.matchesPattern(this.node, pattern, allowPartial); return _matchesPattern(this.node, pattern, allowPartial);
} }
/** /**
@ -68,7 +79,7 @@ export function equals(this: NodePath, key: string, value): boolean {
*/ */
export function isNodeType(this: NodePath, type: string): boolean { export function isNodeType(this: NodePath, type: string): boolean {
return t.isType(this.type, type); return isType(this.type, type);
} }
/** /**
@ -104,9 +115,9 @@ export function canSwapBetweenExpressionAndStatement(
} }
if (this.isExpression()) { if (this.isExpression()) {
return t.isBlockStatement(replacement); return isBlockStatement(replacement);
} else if (this.isBlockStatement()) { } else if (this.isBlockStatement()) {
return t.isExpression(replacement); return isExpression(replacement);
} }
return false; return false;
@ -151,11 +162,11 @@ export function isCompletionRecord(
export function isStatementOrBlock(this: NodePath): boolean { export function isStatementOrBlock(this: NodePath): boolean {
if ( if (
this.parentPath.isLabeledStatement() || this.parentPath.isLabeledStatement() ||
t.isBlockStatement(this.container) isBlockStatement(this.container)
) { ) {
return false; return false;
} else { } else {
return t.STATEMENT_OR_BLOCK_KEYS.includes(this.key as string); return STATEMENT_OR_BLOCK_KEYS.includes(this.key as string);
} }
} }
@ -172,7 +183,7 @@ export function referencesImport(
if ( if (
(this.isMemberExpression() || this.isOptionalMemberExpression()) && (this.isMemberExpression() || this.isOptionalMemberExpression()) &&
(this.node.computed (this.node.computed
? t.isStringLiteral(this.node.property, { value: importName }) ? isStringLiteral(this.node.property, { value: importName })
: (this.node.property as t.Identifier).name === importName) : (this.node.property as t.Identifier).name === importName)
) { ) {
const object = ( const object = (
@ -211,7 +222,7 @@ export function referencesImport(
if ( if (
path.isImportSpecifier() && path.isImportSpecifier() &&
t.isIdentifier(path.node.imported, { name: importName }) isIdentifier(path.node.imported, { name: importName })
) { ) {
return true; return true;
} }
@ -384,7 +395,7 @@ export function _guessExecutionStatusRelativeTo(
} }
// otherwise we're associated by a parent node, check which key comes before the other // otherwise we're associated by a parent node, check which key comes before the other
const keys = t.VISITOR_KEYS[commonPath.type]; const keys = VISITOR_KEYS[commonPath.type];
const keyPosition = { const keyPosition = {
this: keys.indexOf(divergence.this.parentKey as string), this: keys.indexOf(divergence.this.parentKey as string),
target: keys.indexOf(divergence.target.parentKey as string), target: keys.indexOf(divergence.target.parentKey as string),
@ -503,7 +514,7 @@ export function _resolve(
// making this resolution inaccurate // making this resolution inaccurate
const targetKey = this.toComputedKey(); const targetKey = this.toComputedKey();
if (!t.isLiteral(targetKey)) return; if (!isLiteral(targetKey)) return;
// @ts-expect-error todo(flow->ts): NullLiteral // @ts-expect-error todo(flow->ts): NullLiteral
const targetName = targetKey.value; const targetName = targetKey.value;

View File

@ -1,5 +1,11 @@
import { react } from "@babel/types"; import { react } from "@babel/types";
import * as t from "@babel/types"; import {
cloneNode,
jsxExpressionContainer,
variableDeclaration,
variableDeclarator,
} from "@babel/types";
import type * as t from "@babel/types";
import type Scope from "../../scope"; import type Scope from "../../scope";
import type NodePath from "../index"; import type NodePath from "../index";
import type Binding from "../../scope/binding"; import type Binding from "../../scope/binding";
@ -235,23 +241,23 @@ export default class PathHoister<T extends t.Node = t.Node> {
let uid = attachTo.scope.generateUidIdentifier("ref"); let uid = attachTo.scope.generateUidIdentifier("ref");
// @ts-expect-error todo(flow->ts): more specific type for this.path // @ts-expect-error todo(flow->ts): more specific type for this.path
const declarator = t.variableDeclarator(uid, this.path.node); const declarator = variableDeclarator(uid, this.path.node);
const insertFn = this.attachAfter ? "insertAfter" : "insertBefore"; const insertFn = this.attachAfter ? "insertAfter" : "insertBefore";
const [attached] = attachTo[insertFn]([ const [attached] = attachTo[insertFn]([
attachTo.isVariableDeclarator() attachTo.isVariableDeclarator()
? declarator ? declarator
: t.variableDeclaration("var", [declarator]), : variableDeclaration("var", [declarator]),
]); ]);
const parent = this.path.parentPath; const parent = this.path.parentPath;
if (parent.isJSXElement() && this.path.container === parent.node.children) { if (parent.isJSXElement() && this.path.container === parent.node.children) {
// turning the `span` in `<div><span /></div>` to an expression so we need to wrap it with // turning the `span` in `<div><span /></div>` to an expression so we need to wrap it with
// an expression container // an expression container
uid = t.jsxExpressionContainer(uid); uid = jsxExpressionContainer(uid);
} }
this.path.replaceWith(t.cloneNode(uid)); this.path.replaceWith(cloneNode(uid));
return attachTo.isVariableDeclarator() return attachTo.isVariableDeclarator()
? attached.get("init") ? attached.get("init")

View File

@ -1,14 +1,35 @@
import type NodePath from "../index"; import type NodePath from "../index";
import { react } from "@babel/types"; import {
import * as t from "@babel/types"; isBinding,
isBlockScoped,
isExportDeclaration,
isExpression,
isFlow,
isForStatement,
isForXStatement,
isIdentifier,
isImportDeclaration,
isImportSpecifier,
isJSXIdentifier,
isJSXMemberExpression,
isMemberExpression,
isReferenced,
isScope,
isStatement,
isVar,
isVariableDeclaration,
react,
} from "@babel/types";
import type * as t from "@babel/types";
const { isCompatTag } = react;
export const ReferencedIdentifier = { export const ReferencedIdentifier = {
types: ["Identifier", "JSXIdentifier"], types: ["Identifier", "JSXIdentifier"],
checkPath(path: NodePath, opts?: any): boolean { checkPath(path: NodePath, opts?: any): boolean {
const { node, parent } = path; const { node, parent } = path;
if (!t.isIdentifier(node, opts) && !t.isJSXMemberExpression(parent, opts)) { if (!isIdentifier(node, opts) && !isJSXMemberExpression(parent, opts)) {
if (t.isJSXIdentifier(node, opts)) { if (isJSXIdentifier(node, opts)) {
if (react.isCompatTag(node.name)) return false; if (isCompatTag(node.name)) return false;
} else { } else {
// not a JSXIdentifier or an Identifier // not a JSXIdentifier or an Identifier
return false; return false;
@ -16,14 +37,14 @@ export const ReferencedIdentifier = {
} }
// check if node is referenced // check if node is referenced
return t.isReferenced(node, parent, path.parentPath.parent); return isReferenced(node, parent, path.parentPath.parent);
}, },
}; };
export const ReferencedMemberExpression = { export const ReferencedMemberExpression = {
types: ["MemberExpression"], types: ["MemberExpression"],
checkPath({ node, parent }) { checkPath({ node, parent }) {
return t.isMemberExpression(node) && t.isReferenced(node, parent); return isMemberExpression(node) && isReferenced(node, parent);
}, },
}; };
@ -32,17 +53,17 @@ export const BindingIdentifier = {
checkPath(path: NodePath): boolean { checkPath(path: NodePath): boolean {
const { node, parent } = path; const { node, parent } = path;
const grandparent = path.parentPath.parent; const grandparent = path.parentPath.parent;
return t.isIdentifier(node) && t.isBinding(node, parent, grandparent); return isIdentifier(node) && isBinding(node, parent, grandparent);
}, },
}; };
export const Statement = { export const Statement = {
types: ["Statement"], types: ["Statement"],
checkPath({ node, parent }: NodePath): boolean { checkPath({ node, parent }: NodePath): boolean {
if (t.isStatement(node)) { if (isStatement(node)) {
if (t.isVariableDeclaration(node)) { if (isVariableDeclaration(node)) {
if (t.isForXStatement(parent, { left: node })) return false; if (isForXStatement(parent, { left: node })) return false;
if (t.isForStatement(parent, { init: node })) return false; if (isForStatement(parent, { init: node })) return false;
} }
return true; return true;
@ -58,7 +79,7 @@ export const Expression = {
if (path.isIdentifier()) { if (path.isIdentifier()) {
return path.isReferencedIdentifier(); return path.isReferencedIdentifier();
} else { } else {
return t.isExpression(path.node); return isExpression(path.node);
} }
}, },
}; };
@ -67,26 +88,26 @@ export const Scope = {
// When pattern is inside the function params, it is a scope // When pattern is inside the function params, it is a scope
types: ["Scopable", "Pattern"], types: ["Scopable", "Pattern"],
checkPath(path) { checkPath(path) {
return t.isScope(path.node, path.parent); return isScope(path.node, path.parent);
}, },
}; };
export const Referenced = { export const Referenced = {
checkPath(path: NodePath): boolean { checkPath(path: NodePath): boolean {
return t.isReferenced(path.node, path.parent); return isReferenced(path.node, path.parent);
}, },
}; };
export const BlockScoped = { export const BlockScoped = {
checkPath(path: NodePath): boolean { checkPath(path: NodePath): boolean {
return t.isBlockScoped(path.node); return isBlockScoped(path.node);
}, },
}; };
export const Var = { export const Var = {
types: ["VariableDeclaration"], types: ["VariableDeclaration"],
checkPath(path: NodePath): boolean { checkPath(path: NodePath): boolean {
return t.isVar(path.node); return isVar(path.node);
}, },
}; };
@ -111,13 +132,13 @@ export const Pure = {
export const Flow = { export const Flow = {
types: ["Flow", "ImportDeclaration", "ExportDeclaration", "ImportSpecifier"], types: ["Flow", "ImportDeclaration", "ExportDeclaration", "ImportSpecifier"],
checkPath({ node }: NodePath): boolean { checkPath({ node }: NodePath): boolean {
if (t.isFlow(node)) { if (isFlow(node)) {
return true; return true;
} else if (t.isImportDeclaration(node)) { } else if (isImportDeclaration(node)) {
return node.importKind === "type" || node.importKind === "typeof"; return node.importKind === "type" || node.importKind === "typeof";
} else if (t.isExportDeclaration(node)) { } else if (isExportDeclaration(node)) {
return node.exportKind === "type"; return node.exportKind === "type";
} else if (t.isImportSpecifier(node)) { } else if (isImportSpecifier(node)) {
return node.importKind === "type" || node.importKind === "typeof"; return node.importKind === "type" || node.importKind === "typeof";
} else { } else {
return false; return false;

View File

@ -3,7 +3,17 @@
import { path as pathCache } from "../cache"; import { path as pathCache } from "../cache";
import PathHoister from "./lib/hoister"; import PathHoister from "./lib/hoister";
import NodePath from "./index"; import NodePath from "./index";
import * as t from "@babel/types"; import {
arrowFunctionExpression,
assertExpression,
assignmentExpression,
blockStatement,
callExpression,
cloneNode,
expressionStatement,
isExpression,
} from "@babel/types";
import type * as t from "@babel/types";
import type Scope from "../scope"; import type Scope from "../scope";
/** /**
@ -40,7 +50,7 @@ export function insertBefore(this: NodePath, nodes_: t.Node | t.Node[]) {
(!this.isExpressionStatement() || (!this.isExpressionStatement() ||
(node as t.ExpressionStatement).expression != null); (node as t.ExpressionStatement).expression != null);
this.replaceWith(t.blockStatement(shouldInsertCurrentNode ? [node] : [])); this.replaceWith(blockStatement(shouldInsertCurrentNode ? [node] : []));
return this.unshiftContainer("body", nodes); return this.unshiftContainer("body", nodes);
} else { } else {
throw new Error( throw new Error(
@ -118,7 +128,7 @@ export function insertAfter(
// If A is an expression statement, it isn't safe anymore so we need to // If A is an expression statement, it isn't safe anymore so we need to
// convert B to an expression statement // convert B to an expression statement
// A; -> A; B // No semicolon! It could break if followed by [! // A; -> A; B // No semicolon! It could break if followed by [!
return t.isExpression(node) ? t.expressionStatement(node) : node; return isExpression(node) ? expressionStatement(node) : node;
}), }),
); );
} else if ( } else if (
@ -132,11 +142,9 @@ export function insertAfter(
let { scope } = this; let { scope } = this;
if (scope.path.isPattern()) { if (scope.path.isPattern()) {
t.assertExpression(node); assertExpression(node);
this.replaceWith( this.replaceWith(callExpression(arrowFunctionExpression([], node), []));
t.callExpression(t.arrowFunctionExpression([], node), []),
);
(this.get("callee.body") as NodePath).insertAfter(nodes); (this.get("callee.body") as NodePath).insertAfter(nodes);
return [this]; return [this];
} }
@ -148,14 +156,14 @@ export function insertAfter(
} }
const temp = scope.generateDeclaredUidIdentifier(); const temp = scope.generateDeclaredUidIdentifier();
nodes.unshift( nodes.unshift(
t.expressionStatement( expressionStatement(
// @ts-expect-error todo(flow->ts): This can be a variable // @ts-expect-error todo(flow->ts): This can be a variable
// declaraion in the "init" of a for statement, but that's // declaraion in the "init" of a for statement, but that's
// invalid here. // invalid here.
t.assignmentExpression("=", t.cloneNode(temp), node), assignmentExpression("=", cloneNode(temp), node),
), ),
); );
nodes.push(t.expressionStatement(t.cloneNode(temp))); nodes.push(expressionStatement(cloneNode(temp)));
} }
// @ts-expect-error todo(flow->ts): check that nodes is an array of statements // @ts-expect-error todo(flow->ts): check that nodes is an array of statements
return this.replaceExpressionWithStatements(nodes); return this.replaceExpressionWithStatements(nodes);
@ -168,7 +176,7 @@ export function insertAfter(
(!this.isExpressionStatement() || (!this.isExpressionStatement() ||
(node as t.ExpressionStatement).expression != null); (node as t.ExpressionStatement).expression != null);
this.replaceWith(t.blockStatement(shouldInsertCurrentNode ? [node] : [])); this.replaceWith(blockStatement(shouldInsertCurrentNode ? [node] : []));
return this.pushContainer("body", nodes); return this.pushContainer("body", nodes);
} else { } else {
throw new Error( throw new Error(

View File

@ -5,7 +5,29 @@ import traverse from "../index";
import NodePath from "./index"; import NodePath from "./index";
import { path as pathCache } from "../cache"; import { path as pathCache } from "../cache";
import { parse } from "@babel/parser"; import { parse } from "@babel/parser";
import * as t from "@babel/types"; import {
FUNCTION_TYPES,
arrowFunctionExpression,
assignmentExpression,
awaitExpression,
blockStatement,
callExpression,
cloneNode,
expressionStatement,
identifier,
inheritLeadingComments,
inheritTrailingComments,
inheritsComments,
isExpression,
isProgram,
isStatement,
removeComments,
returnStatement,
toSequenceExpression,
validate,
yieldExpression,
} from "@babel/types";
import type * as t from "@babel/types";
import hoistVariables from "@babel/helper-hoist-variables"; import hoistVariables from "@babel/helper-hoist-variables";
/** /**
@ -23,8 +45,8 @@ export function replaceWithMultiple<Nodes extends Array<t.Node>>(
this.resync(); this.resync();
nodes = this._verifyNodeList(nodes); nodes = this._verifyNodeList(nodes);
t.inheritLeadingComments(nodes[0], this.node); inheritLeadingComments(nodes[0], this.node);
t.inheritTrailingComments(nodes[nodes.length - 1], this.node); inheritTrailingComments(nodes[nodes.length - 1], this.node);
pathCache.get(this.parent)?.delete(this.node); pathCache.get(this.parent)?.delete(this.node);
this.node = this.container[this.key] = null; this.node = this.container[this.key] = null;
const paths = this.insertAfter(nodes); const paths = this.insertAfter(nodes);
@ -97,7 +119,7 @@ export function replaceWith(this: NodePath, replacement: t.Node | NodePath) {
return [this]; return [this];
} }
if (this.isProgram() && !t.isProgram(replacement)) { if (this.isProgram() && !isProgram(replacement)) {
throw new Error( throw new Error(
"You can only replace a Program root node with another Program node", "You can only replace a Program root node with another Program node",
); );
@ -117,19 +139,19 @@ export function replaceWith(this: NodePath, replacement: t.Node | NodePath) {
let nodePath = ""; let nodePath = "";
if (this.isNodeType("Statement") && t.isExpression(replacement)) { if (this.isNodeType("Statement") && isExpression(replacement)) {
if ( if (
!this.canHaveVariableDeclarationOrExpression() && !this.canHaveVariableDeclarationOrExpression() &&
!this.canSwapBetweenExpressionAndStatement(replacement) && !this.canSwapBetweenExpressionAndStatement(replacement) &&
!this.parentPath.isExportDefaultDeclaration() !this.parentPath.isExportDefaultDeclaration()
) { ) {
// replacing a statement with an expression so wrap it in an expression statement // replacing a statement with an expression so wrap it in an expression statement
replacement = t.expressionStatement(replacement); replacement = expressionStatement(replacement);
nodePath = "expression"; nodePath = "expression";
} }
} }
if (this.isNodeType("Expression") && t.isStatement(replacement)) { if (this.isNodeType("Expression") && isStatement(replacement)) {
if ( if (
!this.canHaveVariableDeclarationOrExpression() && !this.canHaveVariableDeclarationOrExpression() &&
!this.canSwapBetweenExpressionAndStatement(replacement) !this.canSwapBetweenExpressionAndStatement(replacement)
@ -141,8 +163,8 @@ export function replaceWith(this: NodePath, replacement: t.Node | NodePath) {
const oldNode = this.node; const oldNode = this.node;
if (oldNode) { if (oldNode) {
t.inheritsComments(replacement, oldNode); inheritsComments(replacement, oldNode);
t.removeComments(oldNode); removeComments(oldNode);
} }
// replace the node // replace the node
@ -168,10 +190,10 @@ export function _replaceWith(this: NodePath, node) {
} }
if (this.inList) { if (this.inList) {
// @ts-expect-error todo(flow->ts): check if t.validate accepts a numeric key // @ts-expect-error todo(flow->ts): check if validate accepts a numeric key
t.validate(this.parent, this.key, [node]); validate(this.parent, this.key, [node]);
} else { } else {
t.validate(this.parent, this.key as string, node); validate(this.parent, this.key as string, node);
} }
this.debug(`Replace with ${node?.type}`); this.debug(`Replace with ${node?.type}`);
@ -192,19 +214,19 @@ export function replaceExpressionWithStatements(
) { ) {
this.resync(); this.resync();
const toSequenceExpression = t.toSequenceExpression(nodes, this.scope); const nodesAsSequenceExpression = toSequenceExpression(nodes, this.scope);
if (toSequenceExpression) { if (nodesAsSequenceExpression) {
return this.replaceWith(toSequenceExpression)[0].get("expressions"); return this.replaceWith(nodesAsSequenceExpression)[0].get("expressions");
} }
const functionParent = this.getFunctionParent(); const functionParent = this.getFunctionParent();
const isParentAsync = functionParent?.is("async"); const isParentAsync = functionParent?.is("async");
const isParentGenerator = functionParent?.is("generator"); const isParentGenerator = functionParent?.is("generator");
const container = t.arrowFunctionExpression([], t.blockStatement(nodes)); const container = arrowFunctionExpression([], blockStatement(nodes));
this.replaceWith(t.callExpression(container, [])); this.replaceWith(callExpression(container, []));
// replaceWith changes the type of "this", but it isn't trackable by TS // replaceWith changes the type of "this", but it isn't trackable by TS
type ThisType = NodePath< type ThisType = NodePath<
t.CallExpression & { callee: t.ArrowFunctionExpression } t.CallExpression & { callee: t.ArrowFunctionExpression }
@ -236,19 +258,19 @@ export function replaceExpressionWithStatements(
uid = callee.scope.generateDeclaredUidIdentifier("ret"); uid = callee.scope.generateDeclaredUidIdentifier("ret");
callee callee
.get("body") .get("body")
.pushContainer("body", t.returnStatement(t.cloneNode(uid))); .pushContainer("body", returnStatement(cloneNode(uid)));
loop.setData("expressionReplacementReturnUid", uid); loop.setData("expressionReplacementReturnUid", uid);
} else { } else {
uid = t.identifier(uid.name); uid = identifier(uid.name);
} }
path path
.get("expression") .get("expression")
.replaceWith( .replaceWith(
t.assignmentExpression("=", t.cloneNode(uid), path.node.expression), assignmentExpression("=", cloneNode(uid), path.node.expression),
); );
} else { } else {
path.replaceWith(t.returnStatement(path.node.expression)); path.replaceWith(returnStatement(path.node.expression));
} }
} }
@ -264,25 +286,25 @@ export function replaceExpressionWithStatements(
traverse.hasType( traverse.hasType(
(this.get("callee.body") as NodePath<t.BlockStatement>).node, (this.get("callee.body") as NodePath<t.BlockStatement>).node,
"AwaitExpression", "AwaitExpression",
t.FUNCTION_TYPES, FUNCTION_TYPES,
); );
const needToYieldFunction = const needToYieldFunction =
isParentGenerator && isParentGenerator &&
traverse.hasType( traverse.hasType(
(this.get("callee.body") as NodePath<t.BlockStatement>).node, (this.get("callee.body") as NodePath<t.BlockStatement>).node,
"YieldExpression", "YieldExpression",
t.FUNCTION_TYPES, FUNCTION_TYPES,
); );
if (needToAwaitFunction) { if (needToAwaitFunction) {
newCallee.set("async", true); newCallee.set("async", true);
// yield* will await the generator return result // yield* will await the generator return result
if (!needToYieldFunction) { if (!needToYieldFunction) {
this.replaceWith(t.awaitExpression((this as ThisType).node)); this.replaceWith(awaitExpression((this as ThisType).node));
} }
} }
if (needToYieldFunction) { if (needToYieldFunction) {
newCallee.set("generator", true); newCallee.set("generator", true);
this.replaceWith(t.yieldExpression((this as ThisType).node, true)); this.replaceWith(yieldExpression((this as ThisType).node, true));
} }
return newCallee.get("body.body"); return newCallee.get("body.body");

View File

@ -4,7 +4,46 @@ import traverse from "../index";
import type { TraverseOptions } from "../index"; import type { TraverseOptions } from "../index";
import Binding from "./binding"; import Binding from "./binding";
import globals from "globals"; import globals from "globals";
import * as t from "@babel/types"; import {
FOR_INIT_KEYS,
NOT_LOCAL_BINDING,
callExpression,
cloneNode,
getBindingIdentifiers,
identifier,
isArrayExpression,
isBinary,
isClass,
isClassBody,
isClassDeclaration,
isExportAllDeclaration,
isExportDefaultDeclaration,
isExportNamedDeclaration,
isFunctionDeclaration,
isIdentifier,
isImportDeclaration,
isLiteral,
isMethod,
isModuleDeclaration,
isModuleSpecifier,
isObjectExpression,
isProperty,
isPureish,
isSuper,
isTaggedTemplateExpression,
isTemplateLiteral,
isThisExpression,
isUnaryExpression,
isVariableDeclaration,
matchesPattern,
memberExpression,
numericLiteral,
toIdentifier,
unaryExpression,
variableDeclaration,
variableDeclarator,
} from "@babel/types";
import type * as t from "@babel/types";
import { scope as scopeCache } from "../cache"; import { scope as scopeCache } from "../cache";
import type { Visitor } from "../types"; import type { Visitor } from "../types";
@ -12,28 +51,28 @@ import type { Visitor } from "../types";
function gatherNodeParts(node: t.Node, parts: any[]) { function gatherNodeParts(node: t.Node, parts: any[]) {
switch (node?.type) { switch (node?.type) {
default: default:
if (t.isModuleDeclaration(node)) { if (isModuleDeclaration(node)) {
if ( if (
(t.isExportAllDeclaration(node) || (isExportAllDeclaration(node) ||
t.isExportNamedDeclaration(node) || isExportNamedDeclaration(node) ||
t.isImportDeclaration(node)) && isImportDeclaration(node)) &&
node.source node.source
) { ) {
gatherNodeParts(node.source, parts); gatherNodeParts(node.source, parts);
} else if ( } else if (
(t.isExportNamedDeclaration(node) || t.isImportDeclaration(node)) && (isExportNamedDeclaration(node) || isImportDeclaration(node)) &&
node.specifiers && node.specifiers &&
node.specifiers.length node.specifiers.length
) { ) {
for (const e of node.specifiers) gatherNodeParts(e, parts); for (const e of node.specifiers) gatherNodeParts(e, parts);
} else if ( } else if (
(t.isExportDefaultDeclaration(node) || (isExportDefaultDeclaration(node) ||
t.isExportNamedDeclaration(node)) && isExportNamedDeclaration(node)) &&
node.declaration node.declaration
) { ) {
gatherNodeParts(node.declaration, parts); gatherNodeParts(node.declaration, parts);
} }
} else if (t.isModuleSpecifier(node)) { } else if (isModuleSpecifier(node)) {
// todo(flow->ts): should condition instead be: // todo(flow->ts): should condition instead be:
// ``` // ```
// t.isExportSpecifier(node) || // t.isExportSpecifier(node) ||
@ -44,12 +83,12 @@ function gatherNodeParts(node: t.Node, parts: any[]) {
// allowing only nodes with `.local`? // allowing only nodes with `.local`?
// @ts-expect-error todo(flow->ts) // @ts-expect-error todo(flow->ts)
gatherNodeParts(node.local, parts); gatherNodeParts(node.local, parts);
} else if (t.isLiteral(node)) { } else if (isLiteral(node)) {
// todo(flow->ts): should condition be stricter to ensure value is there // todo(flow->ts): should condition be stricter to ensure value is there
// ``` // ```
// !t.isNullLiteral(node) && // !t.isNullLiteral(node) &&
// !t.isRegExpLiteral(node) && // !t.isRegExpLiteral(node) &&
// !t.isTemplateLiteral(node) // !isTemplateLiteral(node)
// ``` // ```
// @ts-expect-error todo(flow->ts) // @ts-expect-error todo(flow->ts)
parts.push(node.value); parts.push(node.value);
@ -186,7 +225,7 @@ interface CollectVisitorState {
const collectorVisitor: Visitor<CollectVisitorState> = { const collectorVisitor: Visitor<CollectVisitorState> = {
For(path) { For(path) {
for (const key of t.FOR_INIT_KEYS) { for (const key of FOR_INIT_KEYS) {
// todo: might be not needed with improvement to babel-types // todo: might be not needed with improvement to babel-types
const declar = path.get(key) as NodePath; const declar = path.get(key) as NodePath;
// delegate block scope handling to the `BlockScoped` method // delegate block scope handling to the `BlockScoped` method
@ -236,17 +275,17 @@ const collectorVisitor: Visitor<CollectVisitorState> = {
exit(path) { exit(path) {
const { node, scope } = path; const { node, scope } = path;
// ExportAllDeclaration does not have `declaration` // ExportAllDeclaration does not have `declaration`
if (t.isExportAllDeclaration(node)) return; if (isExportAllDeclaration(node)) return;
const declar = node.declaration; const declar = node.declaration;
if (t.isClassDeclaration(declar) || t.isFunctionDeclaration(declar)) { if (isClassDeclaration(declar) || isFunctionDeclaration(declar)) {
const id = declar.id; const id = declar.id;
if (!id) return; if (!id) return;
const binding = scope.getBinding(id.name); const binding = scope.getBinding(id.name);
if (binding) binding.reference(path); if (binding) binding.reference(path);
} else if (t.isVariableDeclaration(declar)) { } else if (isVariableDeclaration(declar)) {
for (const decl of declar.declarations) { for (const decl of declar.declarations) {
for (const name of Object.keys(t.getBindingIdentifiers(decl))) { for (const name of Object.keys(getBindingIdentifiers(decl))) {
const binding = scope.getBinding(name); const binding = scope.getBinding(name);
if (binding) binding.reference(path); if (binding) binding.reference(path);
} }
@ -297,7 +336,7 @@ const collectorVisitor: Visitor<CollectVisitorState> = {
if ( if (
path.isFunctionExpression() && path.isFunctionExpression() &&
path.has("id") && path.has("id") &&
!path.get("id").node[t.NOT_LOCAL_BINDING] !path.get("id").node[NOT_LOCAL_BINDING]
) { ) {
path.scope.registerBinding("local", path.get("id"), path); path.scope.registerBinding("local", path.get("id"), path);
} }
@ -309,7 +348,7 @@ const collectorVisitor: Visitor<CollectVisitorState> = {
}, },
ClassExpression(path) { ClassExpression(path) {
if (path.has("id") && !path.get("id").node[t.NOT_LOCAL_BINDING]) { if (path.has("id") && !path.get("id").node[NOT_LOCAL_BINDING]) {
path.scope.registerBinding("local", path); path.scope.registerBinding("local", path);
} }
}, },
@ -412,7 +451,7 @@ export default class Scope {
generateDeclaredUidIdentifier(name?: string) { generateDeclaredUidIdentifier(name?: string) {
const id = this.generateUidIdentifier(name); const id = this.generateUidIdentifier(name);
this.push({ id }); this.push({ id });
return t.cloneNode(id); return cloneNode(id);
} }
/** /**
@ -420,7 +459,7 @@ export default class Scope {
*/ */
generateUidIdentifier(name?: string) { generateUidIdentifier(name?: string) {
return t.identifier(this.generateUid(name)); return identifier(this.generateUid(name));
} }
/** /**
@ -428,8 +467,7 @@ export default class Scope {
*/ */
generateUid(name: string = "temp"): string { generateUid(name: string = "temp"): string {
name = t name = toIdentifier(name)
.toIdentifier(name)
.replace(/^_+/, "") .replace(/^_+/, "")
.replace(/[0-9]+$/g, ""); .replace(/[0-9]+$/g, "");
@ -477,7 +515,7 @@ export default class Scope {
*/ */
generateUidIdentifierBasedOnNode(node: t.Node, defaultName?: string) { generateUidIdentifierBasedOnNode(node: t.Node, defaultName?: string) {
return t.identifier(this.generateUidBasedOnNode(node, defaultName)); return identifier(this.generateUidBasedOnNode(node, defaultName));
} }
/** /**
@ -491,11 +529,11 @@ export default class Scope {
*/ */
isStatic(node: t.Node): boolean { isStatic(node: t.Node): boolean {
if (t.isThisExpression(node) || t.isSuper(node)) { if (isThisExpression(node) || isSuper(node)) {
return true; return true;
} }
if (t.isIdentifier(node)) { if (isIdentifier(node)) {
const binding = this.getBinding(node.name); const binding = this.getBinding(node.name);
if (binding) { if (binding) {
return binding.constant; return binding.constant;
@ -518,7 +556,7 @@ export default class Scope {
const id = this.generateUidIdentifierBasedOnNode(node); const id = this.generateUidIdentifierBasedOnNode(node);
if (!dontPush) { if (!dontPush) {
this.push({ id }); this.push({ id });
return t.cloneNode(id); return cloneNode(id);
} }
return id; return id;
} }
@ -591,28 +629,25 @@ export default class Scope {
// TODO: (Babel 8) Split i in two parameters, and use an object of flags // TODO: (Babel 8) Split i in two parameters, and use an object of flags
toArray(node: t.Node, i?: number | boolean, arrayLikeIsIterable?: boolean) { toArray(node: t.Node, i?: number | boolean, arrayLikeIsIterable?: boolean) {
if (t.isIdentifier(node)) { if (isIdentifier(node)) {
const binding = this.getBinding(node.name); const binding = this.getBinding(node.name);
if (binding?.constant && binding.path.isGenericType("Array")) { if (binding?.constant && binding.path.isGenericType("Array")) {
return node; return node;
} }
} }
if (t.isArrayExpression(node)) { if (isArrayExpression(node)) {
return node; return node;
} }
if (t.isIdentifier(node, { name: "arguments" })) { if (isIdentifier(node, { name: "arguments" })) {
return t.callExpression( return callExpression(
t.memberExpression( memberExpression(
t.memberExpression( memberExpression(
t.memberExpression( memberExpression(identifier("Array"), identifier("prototype")),
t.identifier("Array"), identifier("slice"),
t.identifier("prototype"),
), ),
t.identifier("slice"), identifier("call"),
),
t.identifier("call"),
), ),
[node], [node],
); );
@ -624,7 +659,7 @@ export default class Scope {
// Used in array-spread to create an array. // Used in array-spread to create an array.
helperName = "toConsumableArray"; helperName = "toConsumableArray";
} else if (i) { } else if (i) {
args.push(t.numericLiteral(i)); args.push(numericLiteral(i));
// Used in array-rest to create an array from a subset of an iterable. // Used in array-rest to create an array from a subset of an iterable.
helperName = "slicedToArray"; helperName = "slicedToArray";
@ -640,7 +675,7 @@ export default class Scope {
} }
// @ts-expect-error todo(flow->ts): t.Node is not valid to use in args, function argument typeneeds to be clarified // @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); return callExpression(this.hub.addHelper(helperName), args);
} }
hasLabel(name: string) { hasLabel(name: string) {
@ -688,7 +723,7 @@ export default class Scope {
} }
buildUndefinedNode() { buildUndefinedNode() {
return t.unaryExpression("void", t.numericLiteral(0), true); return unaryExpression("void", numericLiteral(0), true);
} }
registerConstantViolation(path: NodePath) { registerConstantViolation(path: NodePath) {
@ -776,59 +811,59 @@ export default class Scope {
} }
isPure(node: t.Node, constantsOnly?: boolean) { isPure(node: t.Node, constantsOnly?: boolean) {
if (t.isIdentifier(node)) { if (isIdentifier(node)) {
const binding = this.getBinding(node.name); const binding = this.getBinding(node.name);
if (!binding) return false; if (!binding) return false;
if (constantsOnly) return binding.constant; if (constantsOnly) return binding.constant;
return true; return true;
} else if (t.isClass(node)) { } else if (isClass(node)) {
if (node.superClass && !this.isPure(node.superClass, constantsOnly)) { if (node.superClass && !this.isPure(node.superClass, constantsOnly)) {
return false; return false;
} }
return this.isPure(node.body, constantsOnly); return this.isPure(node.body, constantsOnly);
} else if (t.isClassBody(node)) { } else if (isClassBody(node)) {
for (const method of node.body) { for (const method of node.body) {
if (!this.isPure(method, constantsOnly)) return false; if (!this.isPure(method, constantsOnly)) return false;
} }
return true; return true;
} else if (t.isBinary(node)) { } else if (isBinary(node)) {
return ( return (
this.isPure(node.left, constantsOnly) && this.isPure(node.left, constantsOnly) &&
this.isPure(node.right, constantsOnly) this.isPure(node.right, constantsOnly)
); );
} else if (t.isArrayExpression(node)) { } else if (isArrayExpression(node)) {
for (const elem of node.elements) { for (const elem of node.elements) {
if (!this.isPure(elem, constantsOnly)) return false; if (!this.isPure(elem, constantsOnly)) return false;
} }
return true; return true;
} else if (t.isObjectExpression(node)) { } else if (isObjectExpression(node)) {
for (const prop of node.properties) { for (const prop of node.properties) {
if (!this.isPure(prop, constantsOnly)) return false; if (!this.isPure(prop, constantsOnly)) return false;
} }
return true; return true;
} else if (t.isMethod(node)) { } else if (isMethod(node)) {
if (node.computed && !this.isPure(node.key, constantsOnly)) return false; if (node.computed && !this.isPure(node.key, constantsOnly)) return false;
if (node.kind === "get" || node.kind === "set") return false; if (node.kind === "get" || node.kind === "set") return false;
return true; return true;
} else if (t.isProperty(node)) { } else if (isProperty(node)) {
// @ts-expect-error todo(flow->ts): computed in not present on private properties // @ts-expect-error todo(flow->ts): computed in not present on private properties
if (node.computed && !this.isPure(node.key, constantsOnly)) return false; if (node.computed && !this.isPure(node.key, constantsOnly)) return false;
return this.isPure(node.value, constantsOnly); return this.isPure(node.value, constantsOnly);
} else if (t.isUnaryExpression(node)) { } else if (isUnaryExpression(node)) {
return this.isPure(node.argument, constantsOnly); return this.isPure(node.argument, constantsOnly);
} else if (t.isTaggedTemplateExpression(node)) { } else if (isTaggedTemplateExpression(node)) {
return ( return (
t.matchesPattern(node.tag, "String.raw") && matchesPattern(node.tag, "String.raw") &&
!this.hasBinding("String", true) && !this.hasBinding("String", true) &&
this.isPure(node.quasi, constantsOnly) this.isPure(node.quasi, constantsOnly)
); );
} else if (t.isTemplateLiteral(node)) { } else if (isTemplateLiteral(node)) {
for (const expression of node.expressions) { for (const expression of node.expressions) {
if (!this.isPure(expression, constantsOnly)) return false; if (!this.isPure(expression, constantsOnly)) return false;
} }
return true; return true;
} else { } else {
return t.isPureish(node); return isPureish(node);
} }
} }
@ -969,7 +1004,7 @@ export default class Scope {
let declarPath = !unique && path.getData(dataKey); let declarPath = !unique && path.getData(dataKey);
if (!declarPath) { if (!declarPath) {
const declar = t.variableDeclaration(kind, []); const declar = variableDeclaration(kind, []);
// @ts-expect-error todo(flow->ts): avoid modifying nodes // @ts-expect-error todo(flow->ts): avoid modifying nodes
declar._blockHoist = blockHoist; declar._blockHoist = blockHoist;
@ -977,7 +1012,7 @@ export default class Scope {
if (!unique) path.setData(dataKey, declarPath); if (!unique) path.setData(dataKey, declarPath);
} }
const declarator = t.variableDeclarator(opts.id, opts.init); const declarator = variableDeclarator(opts.id, opts.init);
declarPath.node.declarations.push(declarator); declarPath.node.declarations.push(declarator);
this.registerBinding(kind, declarPath.get("declarations").pop()); this.registerBinding(kind, declarPath.get("declarations").pop());
} }

View File

@ -1,6 +1,13 @@
import Binding from "../binding"; import Binding from "../binding";
import splitExportDeclaration from "@babel/helper-split-export-declaration"; import splitExportDeclaration from "@babel/helper-split-export-declaration";
import * as t from "@babel/types"; import {
VISITOR_KEYS,
assignmentExpression,
identifier,
toExpression,
variableDeclaration,
variableDeclarator,
} from "@babel/types";
import type { Visitor } from "../../types"; import type { Visitor } from "../../types";
const renameVisitor: Visitor<Renamer> = { const renameVisitor: Visitor<Renamer> = {
@ -68,15 +75,12 @@ export default class Renamer {
if (!path.isFunctionDeclaration() && !path.isClassDeclaration()) return; if (!path.isFunctionDeclaration() && !path.isClassDeclaration()) return;
if (this.binding.kind !== "hoisted") return; if (this.binding.kind !== "hoisted") return;
path.node.id = t.identifier(this.oldName); path.node.id = identifier(this.oldName);
path.node._blockHoist = 3; path.node._blockHoist = 3;
path.replaceWith( path.replaceWith(
t.variableDeclaration("let", [ variableDeclaration("let", [
t.variableDeclarator( variableDeclarator(identifier(this.newName), toExpression(path.node)),
t.identifier(this.newName),
t.toExpression(path.node),
),
]), ]),
); );
} }
@ -90,14 +94,14 @@ export default class Renamer {
if (!path.isFunctionExpression() && !path.isClassExpression()) return; if (!path.isFunctionExpression() && !path.isClassExpression()) return;
if (this.binding.kind !== "local") return; if (this.binding.kind !== "local") return;
path.node.id = t.identifier(this.oldName); path.node.id = identifier(this.oldName);
this.binding.scope.parent.push({ this.binding.scope.parent.push({
id: t.identifier(this.newName), id: identifier(this.newName),
}); });
path.replaceWith( path.replaceWith(
t.assignmentExpression("=", t.identifier(this.newName), path.node), assignmentExpression("=", identifier(this.newName), path.node),
); );
} }
@ -152,7 +156,7 @@ function skipAllButComputedMethodKey(path) {
// So it's a method with a computed key. Make sure to skip every other key the // So it's a method with a computed key. Make sure to skip every other key the
// traversal would visit. // traversal would visit.
const keys = t.VISITOR_KEYS[path.type]; const keys = VISITOR_KEYS[path.type];
for (const key of keys) { for (const key of keys) {
if (key !== "key") path.skipKey(key); if (key !== "key") path.skipKey(key);
} }

View File

@ -1,4 +1,4 @@
import * as t from "@babel/types"; import type * as t from "@babel/types";
import { NodePath } from "./index"; import { NodePath } from "./index";
import { VirtualTypeAliases } from "./path/generated/virtual-types"; import { VirtualTypeAliases } from "./path/generated/virtual-types";

View File

@ -1,5 +1,5 @@
import * as virtualTypes from "./path/lib/virtual-types"; import * as virtualTypes from "./path/lib/virtual-types";
import * as t from "@babel/types"; import { DEPRECATED_KEYS, FLIPPED_ALIAS_KEYS, TYPES } from "@babel/types";
/** /**
* explode() will take a visitor object with all of the various shorthands * explode() will take a visitor object with all of the various shorthands
@ -85,9 +85,9 @@ export function explode(visitor) {
const fns = visitor[nodeType]; const fns = visitor[nodeType];
let aliases: Array<string> | undefined = t.FLIPPED_ALIAS_KEYS[nodeType]; let aliases: Array<string> | undefined = FLIPPED_ALIAS_KEYS[nodeType];
const deprecatedKey = t.DEPRECATED_KEYS[nodeType]; const deprecatedKey = DEPRECATED_KEYS[nodeType];
if (deprecatedKey) { if (deprecatedKey) {
console.trace( console.trace(
`Visitor defined for ${nodeType} but it has been renamed to ${deprecatedKey}`, `Visitor defined for ${nodeType} but it has been renamed to ${deprecatedKey}`,
@ -136,7 +136,7 @@ export function verify(visitor) {
if (shouldIgnoreKey(nodeType)) continue; if (shouldIgnoreKey(nodeType)) continue;
if (t.TYPES.indexOf(nodeType) < 0) { if (TYPES.indexOf(nodeType) < 0) {
throw new Error( throw new Error(
`You gave us a visitor for the node type ${nodeType} but it's not a valid type`, `You gave us a visitor for the node type ${nodeType} but it's not a valid type`,
); );