Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
/** Check if node is a template literal with no expressions (e.g., `foo`) */
function isConstantTemplateLiteral(t, node) {
return (
t.isTemplateLiteral(node) &&
node.expressions.length === 0 &&
node.quasis.length === 1
);
}

/** Create a template literal node with no expressions (e.g., `foo`) */
function constantTemplateLiteral(t, value) {
return t.templateLiteral(
[t.templateElement({ raw: value, cooked: value }, true)],
[]
);
}

module.exports = function ({ types: t }, options = {}) {
const rename = options.rename || {};

Expand Down Expand Up @@ -78,6 +95,39 @@ module.exports = function ({ types: t }, options = {}) {
path.skip();
},
},
BinaryExpression: {
exit(path) {
const node = path.node;
if (node.operator !== "in") {
return;
}

let oldName;
let isTemplateLiteral = false;

if (t.isStringLiteral(node.left)) {
oldName = node.left.value;
} else if (isConstantTemplateLiteral(t, node.left)) {
oldName = node.left.quasis[0].value.cooked;
isTemplateLiteral = true;
} else {
return;
}

const newName = nameMap.get(oldName);
if (newName === undefined) {
return;
}

const newNode = isTemplateLiteral
? constantTemplateLiteral(t, newName)
: t.stringLiteral(newName);

const replacedNode = t.binaryExpression("in", newNode, node.right);
path.replaceWith(replacedNode);
path.skip();
},
},
},
};
};
55 changes: 55 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,59 @@ describe("babel-plugin-transform-rename-properties", () => {
});
});
});

describe("for `in` operator", () => {
it("renames string literal property", () => {
compare("'foo' in obj", "'__FOO__' in obj", {
rename: { foo: "__FOO__" },
});
});
it("does not rename non-literal property", () => {
compare("prop in obj", "prop in obj", {
rename: { prop: "__PROP__" },
});

compare("('prop' + '1') in obj", "('prop' + '1') in obj", {
rename: { prop: "__PROP__" },
});
});
it("does not rename numeric literal property", () => {
compare("42 in obj", "42 in obj", {
rename: { 42: "__FORTY_TWO__" },
});
});
it("renames multiple `in` expressions", () => {
compare(
"'foo' in obj && 'bar' in obj",
"'__FOO__' in obj && '__BAR__' in obj",
{
rename: { foo: "__FOO__", bar: "__BAR__" },
}
);
});
it("renames `in` when right-hand side contains property access", () => {
compare("'foo' in obj.bar", "'__FOO__' in obj.__BAR__", {
rename: { foo: "__FOO__", bar: "__BAR__" },
});
});
it("renames constant template literal property", () => {
compare("`foo` in obj", "`__FOO__` in obj", {
rename: { foo: "__FOO__" },
});
});
it("does not rename non-constant template literal property", () => {
compare("`${prop}` in obj", "`${prop}` in obj", {
rename: { prop: "__PROP__" },
});

compare("`prop${prop}` in obj", "`prop${prop}` in obj", {
rename: { prop: "__PROP__" },
});
});
it("renames nested `in` expressions", () => {
compare("'foo' in ('bar' in obj)", "'__FOO__' in ('__BAR__' in obj)", {
rename: { foo: "__FOO__", bar: "__BAR__" },
});
});
});
});