Skip to content

Commit

Permalink
Move external/runtime-helpers logic out of babel/helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolo-ribaudo committed May 17, 2024
1 parent 507e581 commit 7883619
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 65 deletions.
19 changes: 18 additions & 1 deletion packages/babel-core/src/tools/build-external-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,24 @@ function buildHelpers(

const ref = (refs[name] = getHelperReference(name));

const { nodes } = helpers.get(name, getHelperReference, ref);
const { nodes } = helpers.get(
name,
getHelperReference,
namespace ? null : `_${name}`,
[],
namespace
? (ast, exportName, mapExportBindingAssignments) => {
mapExportBindingAssignments(node =>
assignmentExpression("=", ref, node),
);
ast.body.push(
expressionStatement(
assignmentExpression("=", ref, identifier(exportName)),
),
);
}
: null,
);

body.push(...nodes);
});
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-core/src/transformation/file/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ export default class File {
const { nodes, globals } = helpers.get(
name,
dep => dependencies[dep],
uid,
uid.name,
Object.keys(this.scope.getAllBindings()),
);

Expand Down
100 changes: 82 additions & 18 deletions packages/babel-core/test/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -840,24 +840,6 @@ describe("api", function () {
});

describe("buildExternalHelpers", function () {
describe("smoke tests", function () {
it("builds external helpers in global output type", function () {
babel.buildExternalHelpers(null, "global");
});

it("builds external helpers in module output type", function () {
babel.buildExternalHelpers(null, "module");
});

it("builds external helpers in umd output type", function () {
babel.buildExternalHelpers(null, "umd");
});

it("builds external helpers in var output type", function () {
babel.buildExternalHelpers(null, "var");
});
});

it("all", function () {
const script = babel.buildExternalHelpers();
expect(script).toEqual(expect.stringContaining("classCallCheck"));
Expand All @@ -880,6 +862,88 @@ describe("api", function () {
const script = babel.buildExternalHelpers(["typeof"]);
expect(script).toEqual(expect.stringContaining("typeof"));
});

describe("output types", function () {
it("global", function () {
const script = babel.buildExternalHelpers(["get"], "global");
expect(script).toMatchInlineSnapshot(`
"(function (global) {
var babelHelpers = global.babelHelpers = {};
function _get() {
return babelHelpers.get = _get = \\"undefined\\" != typeof Reflect && Reflect.get ? Reflect.get.bind() : function (e, t, r) {
var p = babelHelpers.superPropBase(e, t);
if (p) {
var n = Object.getOwnPropertyDescriptor(p, t);
return n.get ? n.get.call(arguments.length < 3 ? e : r) : n.value;
}
}, _get.apply(this, arguments);
}
babelHelpers.get = _get;
})(typeof global === \\"undefined\\" ? self : global);"
`);
});

it("umd", function () {
const script = babel.buildExternalHelpers(["get"], "umd");
expect(script).toMatchInlineSnapshot(`
"(function (root, factory) {
if (typeof define === \\"function\\" && define.amd) {
define([\\"exports\\"], factory);
} else if (typeof exports === \\"object\\") {
factory(exports);
} else {
factory(root.babelHelpers = {});
}
})(this, function (global) {
var babelHelpers = global;
function _get() {
return babelHelpers.get = _get = \\"undefined\\" != typeof Reflect && Reflect.get ? Reflect.get.bind() : function (e, t, r) {
var p = babelHelpers.superPropBase(e, t);
if (p) {
var n = Object.getOwnPropertyDescriptor(p, t);
return n.get ? n.get.call(arguments.length < 3 ? e : r) : n.value;
}
}, _get.apply(this, arguments);
}
babelHelpers.get = _get;
});"
`);
});

it("var", function () {
const script = babel.buildExternalHelpers(["get"], "var");
expect(script).toMatchInlineSnapshot(`
"var babelHelpers = {};
function _get() {
return babelHelpers.get = _get = \\"undefined\\" != typeof Reflect && Reflect.get ? Reflect.get.bind() : function (e, t, r) {
var p = babelHelpers.superPropBase(e, t);
if (p) {
var n = Object.getOwnPropertyDescriptor(p, t);
return n.get ? n.get.call(arguments.length < 3 ? e : r) : n.value;
}
}, _get.apply(this, arguments);
}
babelHelpers.get = _get;
babelHelpers;"
`);
});

it("module", function () {
const script = babel.buildExternalHelpers(["get"], "module");
expect(script).toMatchInlineSnapshot(`
"export { _get as get };
function _get() {
return _get = \\"undefined\\" != typeof Reflect && Reflect.get ? Reflect.get.bind() : function (e, t, r) {
var p = _superPropBase(e, t);
if (p) {
var n = Object.getOwnPropertyDescriptor(p, t);
return n.get ? n.get.call(arguments.length < 3 ? e : r) : n.value;
}
}, _get.apply(this, arguments);
}"
`);
});
});
});

describe("handle parsing errors", function () {
Expand Down
88 changes: 45 additions & 43 deletions packages/babel-helpers/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
import {
assignmentExpression,
cloneNode,
expressionStatement,
exportNamedDeclaration,
exportSpecifier,
identifier,
} from "@babel/types";
import { cloneNode, identifier } from "@babel/types";
import type * as t from "@babel/types";
import helpers from "./helpers-generated.ts";
import type { HelperMetadata } from "./helpers-generated.ts";
Expand All @@ -31,25 +24,34 @@ function deep(obj: any, path: string, value?: unknown) {
}
}

type AdjustAst = (
ast: t.Program,
exportName: string,
mapExportBindingAssignments: (
map: (node: t.Expression) => t.Expression,
) => void,
) => void;

/**
* Given a helper AST and information about how it will be used, update the AST to match the usage.
*/
function permuteHelperAST(
ast: t.Program,
metadata: HelperMetadata,
id?: t.Identifier | t.MemberExpression,
localBindings?: string[],
getDependency?: GetDependency,
bindingName: string | undefined,
localBindings: string[] | undefined,
getDependency: GetDependency | undefined,
adjustAst: AdjustAst | undefined,
) {
const { locals, dependencies, exportBindingAssignments, exportName } =
metadata;

const bindings = new Set(localBindings || []);
if (id?.type === "Identifier") bindings.add(id.name);
if (bindingName) bindings.add(bindingName);
for (const [name, paths] of Object.entries(locals)) {
let newName = name;
if (name === exportName && id?.type === "Identifier") {
newName = id.name;
if (bindingName && name === exportName) {
newName = bindingName;
} else {
while (bindings.has(newName)) newName = "_" + newName;
}
Expand All @@ -70,35 +72,17 @@ function permuteHelperAST(
}
}

if (!id) {
ast.body.push(
exportNamedDeclaration(null, [
exportSpecifier(identifier(exportName), identifier("default")),
]),
);
} else if (id.type === "MemberExpression") {
exportBindingAssignments.forEach(assignPath => {
deep(
ast,
assignPath,
assignmentExpression("=", id, deep(ast, assignPath)),
);
});
ast.body.push(
expressionStatement(
assignmentExpression("=", id, identifier(exportName)),
),
);
} else if (id.type !== "Identifier") {
throw new Error("Unexpected helper format.");
}
adjustAst?.(ast, exportName, map => {
exportBindingAssignments.forEach(p => deep(ast, p, map(deep(ast, p))));
});
}

interface HelperData {
build: (
getDependency: GetDependency,
id: t.Identifier | t.MemberExpression,
localBindings: string[],
getDependency: GetDependency | undefined,
bindingName: string | undefined,
localBindings: string[] | undefined,
adjustAst: AdjustAst | undefined,
) => {
nodes: t.Program["body"];
globals: string[];
Expand All @@ -120,14 +104,15 @@ function loadHelper(name: string) {

helperData[name] = {
minVersion: helper.minVersion,
build(getDependency, id, localBindings) {
build(getDependency, bindingName, localBindings, adjustAst) {
const ast = helper.ast();
permuteHelperAST(
ast,
helper.metadata,
id,
bindingName,
localBindings,
getDependency,
adjustAst,
);

return {
Expand All @@ -147,10 +132,27 @@ function loadHelper(name: string) {
export function get(
name: string,
getDependency?: GetDependency,
id?: t.Identifier | t.MemberExpression,
bindingName?: string,
localBindings?: string[],
adjustAst?: AdjustAst,
) {
return loadHelper(name).build(getDependency, id, localBindings);
if (!process.env.BABEL_8_BREAKING) {
// In older versions, bindingName was a t.Identifier | t.MemberExpression
if (typeof bindingName === "object") {
const id = bindingName as t.Identifier | t.MemberExpression | null;
if (id?.type === "Identifier") {
bindingName = id.name;
} else {
bindingName = undefined;
}
}
}
return loadHelper(name).build(
getDependency,
bindingName,
localBindings,
adjustAst,
);
}

export function minVersion(name: string) {
Expand Down
23 changes: 21 additions & 2 deletions packages/babel-plugin-transform-runtime/scripts/build-dist.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,24 @@ function getRuntimeRoot(runtimeName) {
);
}

function adjustEsmHelperAst(ast, exportName) {
ast.body.push(
template.statement({ sourceType: "module" }).ast`
export { ${t.identifier(exportName)} as default };
`
);
}
function adjustCjsHelperAst(ast, exportName, mapExportBindingAssignments) {
mapExportBindingAssignments(
node => template.expression.ast`module.exports = ${node}`
);
ast.body.push(
template.statement.ast`
module.exports = ${t.identifier(exportName)};
`
);
}

function buildHelper(
runtimeName,
helperFilename,
Expand All @@ -294,8 +312,9 @@ function buildHelper(
const helper = helpers.get(
helperName,
dep => dependencies[dep],
esm ? null : template.expression.ast`module.exports`,
bindings
null,
bindings,
esm ? adjustEsmHelperAst : adjustCjsHelperAst
);
tree.body.push(...helper.nodes);

Expand Down

0 comments on commit 7883619

Please sign in to comment.