Skip to content

Commit 0ab45f9

Browse files
andrewigginsclaude
andauthored
Support renaming properties in in expressions (#12)
Adds a BinaryExpression visitor that renames string literal properties used with the `in` operator (e.g., `'foo' in obj` → `'__FOO__' in obj`). This change aims to match Terser's behavior here. * Support renaming properties in `in` expressions Adds a BinaryExpression visitor that renames string literal properties used with the `in` operator (e.g., `'foo' in obj` → `'__FOO__' in obj`). * Support constant template literals in `in` expressions Extends the BinaryExpression visitor to also rename constant template literals (e.g., `foo` in obj → `__FOO__` in obj). Non-constant template literals with expressions are correctly ignored. * Extract template literal helper functions Refactor constant template literal handling into reusable helper functions with JSDoc documentation. --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 810ef21 commit 0ab45f9

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed

index.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
/** Check if node is a template literal with no expressions (e.g., `foo`) */
2+
function isConstantTemplateLiteral(t, node) {
3+
return (
4+
t.isTemplateLiteral(node) &&
5+
node.expressions.length === 0 &&
6+
node.quasis.length === 1
7+
);
8+
}
9+
10+
/** Create a template literal node with no expressions (e.g., `foo`) */
11+
function constantTemplateLiteral(t, value) {
12+
return t.templateLiteral(
13+
[t.templateElement({ raw: value, cooked: value }, true)],
14+
[]
15+
);
16+
}
17+
118
module.exports = function ({ types: t }, options = {}) {
219
const rename = options.rename || {};
320

@@ -78,6 +95,39 @@ module.exports = function ({ types: t }, options = {}) {
7895
path.skip();
7996
},
8097
},
98+
BinaryExpression: {
99+
exit(path) {
100+
const node = path.node;
101+
if (node.operator !== "in") {
102+
return;
103+
}
104+
105+
let oldName;
106+
let isTemplateLiteral = false;
107+
108+
if (t.isStringLiteral(node.left)) {
109+
oldName = node.left.value;
110+
} else if (isConstantTemplateLiteral(t, node.left)) {
111+
oldName = node.left.quasis[0].value.cooked;
112+
isTemplateLiteral = true;
113+
} else {
114+
return;
115+
}
116+
117+
const newName = nameMap.get(oldName);
118+
if (newName === undefined) {
119+
return;
120+
}
121+
122+
const newNode = isTemplateLiteral
123+
? constantTemplateLiteral(t, newName)
124+
: t.stringLiteral(newName);
125+
126+
const replacedNode = t.binaryExpression("in", newNode, node.right);
127+
path.replaceWith(replacedNode);
128+
path.skip();
129+
},
130+
},
81131
},
82132
};
83133
};

test/index.test.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,4 +194,59 @@ describe("babel-plugin-transform-rename-properties", () => {
194194
});
195195
});
196196
});
197+
198+
describe("for `in` operator", () => {
199+
it("renames string literal property", () => {
200+
compare("'foo' in obj", "'__FOO__' in obj", {
201+
rename: { foo: "__FOO__" },
202+
});
203+
});
204+
it("does not rename non-literal property", () => {
205+
compare("prop in obj", "prop in obj", {
206+
rename: { prop: "__PROP__" },
207+
});
208+
209+
compare("('prop' + '1') in obj", "('prop' + '1') in obj", {
210+
rename: { prop: "__PROP__" },
211+
});
212+
});
213+
it("does not rename numeric literal property", () => {
214+
compare("42 in obj", "42 in obj", {
215+
rename: { 42: "__FORTY_TWO__" },
216+
});
217+
});
218+
it("renames multiple `in` expressions", () => {
219+
compare(
220+
"'foo' in obj && 'bar' in obj",
221+
"'__FOO__' in obj && '__BAR__' in obj",
222+
{
223+
rename: { foo: "__FOO__", bar: "__BAR__" },
224+
}
225+
);
226+
});
227+
it("renames `in` when right-hand side contains property access", () => {
228+
compare("'foo' in obj.bar", "'__FOO__' in obj.__BAR__", {
229+
rename: { foo: "__FOO__", bar: "__BAR__" },
230+
});
231+
});
232+
it("renames constant template literal property", () => {
233+
compare("`foo` in obj", "`__FOO__` in obj", {
234+
rename: { foo: "__FOO__" },
235+
});
236+
});
237+
it("does not rename non-constant template literal property", () => {
238+
compare("`${prop}` in obj", "`${prop}` in obj", {
239+
rename: { prop: "__PROP__" },
240+
});
241+
242+
compare("`prop${prop}` in obj", "`prop${prop}` in obj", {
243+
rename: { prop: "__PROP__" },
244+
});
245+
});
246+
it("renames nested `in` expressions", () => {
247+
compare("'foo' in ('bar' in obj)", "'__FOO__' in ('__BAR__' in obj)", {
248+
rename: { foo: "__FOO__", bar: "__BAR__" },
249+
});
250+
});
251+
});
197252
});

0 commit comments

Comments
 (0)