Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create File class for babel helpers #10575

Merged
merged 1 commit into from Mar 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/babel-core/src/tools/build-external-helpers.js
Expand Up @@ -2,6 +2,7 @@ import * as helpers from "@babel/helpers";
import generator from "@babel/generator";
import template from "@babel/template";
import * as t from "@babel/types";
import File from "../transformation/file/file";

// Wrapped to avoid wasting time parsing this when almost no-one uses
// build-external-helpers.
Expand Down Expand Up @@ -136,6 +137,7 @@ function buildHelpers(body, namespace, whitelist) {

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

helpers.ensure(name, File);
const { nodes } = helpers.get(name, getHelperReference, ref);

body.push(...nodes);
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-core/src/transformation/file/file.js
Expand Up @@ -174,7 +174,7 @@ export default class File {
}

// make sure that the helper exists
helpers.ensure(name);
helpers.ensure(name, File);

const uid = (this.declarations[name] = this.scope.generateUidIdentifier(
name,
Expand Down
37 changes: 28 additions & 9 deletions packages/babel-helpers/src/index.js
Expand Up @@ -13,6 +13,7 @@ function makePath(path) {
return parts.reverse().join(".");
}

let fileClass = undefined;
/**
* Given a file AST for a given helper, get a bunch of metadata about it so that Babel can quickly render
* the helper is whatever context it is needed in.
Expand All @@ -29,7 +30,7 @@ function getHelperMetadata(file) {
const importPaths = [];
const importBindingsReferences = [];

traverse(file, {
const dependencyVisitor = {
ImportDeclaration(child) {
const name = child.node.source.value;
if (!helpers[name]) {
Expand Down Expand Up @@ -72,9 +73,9 @@ function getHelperMetadata(file) {

child.skip();
},
});
};

traverse(file, {
const referenceVisitor = {
Program(path) {
const bindings = path.scope.getAllBindings();

Expand Down Expand Up @@ -111,7 +112,10 @@ function getHelperMetadata(file) {
exportBindingAssignments.push(makePath(child));
}
},
});
};

traverse(file.ast, dependencyVisitor, file.scope);
traverse(file.ast, referenceVisitor, file.scope);

if (!exportPath) throw new Error("Helpers must default-export something.");

Expand Down Expand Up @@ -170,7 +174,7 @@ function permuteHelperAST(file, metadata, id, localBindings, getDependency) {
toRename[exportName] = id.name;
}

traverse(file, {
const visitor = {
Program(path) {
// We need to compute these in advance because removing nodes would
// invalidate the paths.
Expand Down Expand Up @@ -223,7 +227,8 @@ function permuteHelperAST(file, metadata, id, localBindings, getDependency) {
// actually doing the traversal.
path.stop();
},
});
};
traverse(file.ast, visitor, file.scope);
}

const helperData = Object.create(null);
Expand All @@ -238,7 +243,16 @@ function loadHelper(name) {
}

const fn = () => {
return t.file(helper.ast());
const file = { ast: t.file(helper.ast()) };
if (fileClass) {
return new fileClass(
{
filename: `babel-helper://${name}`,
},
file,
);
}
return file;
};

const metadata = getHelperMetadata(fn());
Expand All @@ -249,7 +263,7 @@ function loadHelper(name) {
permuteHelperAST(file, metadata, id, localBindings, getDependency);

return {
nodes: file.program.body,
nodes: file.ast.program.body,
globals: metadata.globals,
};
},
Expand Down Expand Up @@ -280,7 +294,12 @@ export function getDependencies(name: string): $ReadOnlyArray<string> {
return Array.from(loadHelper(name).dependencies.values());
}

export function ensure(name: string) {
export function ensure(name: string, newFileClass?) {
if (!fileClass) {
// optional fileClass used to wrap helper snippets into File instance,
// offering `path.hub` support during traversal
fileClass = newFileClass;
}
loadHelper(name);
}

Expand Down
@@ -0,0 +1 @@
REPLACE_ME;
@@ -0,0 +1,3 @@
{
"plugins": ["./plugin"]
}
@@ -0,0 +1,3 @@
function _$_9496_main_hub_is_found() {}

_$_9496_main_hub_is_found;
22 changes: 22 additions & 0 deletions packages/babel-helpers/test/fixtures/regression/9496/plugin.js
@@ -0,0 +1,22 @@
const defineHelper = require("../../../helpers/define-helper").default;

const main = defineHelper(__dirname, "main", `
export default function helper() {}
`);

module.exports = function() {
return {
visitor: {
Identifier(path) {
if (path.node.name !== "REPLACE_ME") {
if (path.hub) {
path.node.name += "_hub_is_found";
}
return;
}
const helper = this.addHelper(main);
path.replaceWith(helper);
},
},
};
};
Expand Up @@ -134,6 +134,7 @@ function buildHelper(

if (!esm) {
bindings = [];
helpers.ensure(helperName, babel.File);
for (const dep of helpers.getDependencies(helperName)) {
const id = (dependencies[dep] = t.identifier(t.toIdentifier(dep)));
tree.body.push(template.statement.ast`
Expand Down