Refactor Object.{groupBy,hasOwn}() transform (#19218)

This commit is contained in:
fisker Cheung authored and GitHub committed 2026-05-27 15:19:05 +08:00
1 parent 6ec71b6463
commit c018e68c5e
7 files changed
+90 -79

No files matched your search

@@ -0,0 +1,63 @@
import { fileURLToPath } from "node:url";
import { outdent } from "outdent";
import { createIdentifier, isIdentifier } from "./utilities.js";
/* Only works for function `Object.hasOwn()` and `Object.groupBy()` */
/**
* @param {import("@babel/types").Node} node
* @returns {boolean}
*/
function isFunctionCall(node, { function: name, argumentsLength }) {
const [objectName, methodName] = name.split(".");
return (
node.type === "CallExpression" &&
!node.optional &&
node.arguments.length === argumentsLength &&
node.arguments.every(({ type }) => type !== "SpreadElement") &&
node.callee.type === "MemberExpression" &&
!node.callee.optional &&
!node.callee.computed &&
isIdentifier(node.callee.object, objectName) &&
isIdentifier(node.callee.property, methodName)
);
}
/**
* `Object.hasOwn(foo, bar)` -> `__Object_hasOwn(foo, bar)`
*
* @param {import("@babel/types").CallExpression | import("@babel/types").OptionalCallExpression} node
* @returns {import("@babel/types").CallExpression}
*/
function transformFunctionCall(node, functionName) {
return {
...node,
callee: createIdentifier(functionName),
};
}
function createFunctionCallTransform({ function: name, argumentsLength }) {
const functionName = `__${name.replaceAll(".", "_")}`;
const fileName = `function-${name
.replace(/^[A-Z]/, (character) => character.toLowerCase())
.replaceAll(/\W/g, "-")
.replaceAll(/[A-Z]/g, (character) => `-${character.toLowerCase()}`)}.js`;
const functionImplementationUrl = new URL(
`../../shims/${fileName}`,
import.meta.url,
);
const functionImplementationPath = fileURLToPath(functionImplementationUrl);
return {
shouldSkip: (text, file) =>
!text.includes(`${name}(`) || file === functionImplementationPath,
test: (node) => isFunctionCall(node, { function: name, argumentsLength }),
transform: (node) => transformFunctionCall(node, functionName),
inject: outdent`
import ${functionName} from ${JSON.stringify(functionImplementationPath)};
`,
};
}
export default createFunctionCallTransform;
@@ -3,7 +3,7 @@ import { outdent } from "outdent";
import { OPTIONAL_OBJECT } from "../../shims/shared.js";
import { createIdentifier, isIdentifier } from "./utilities.js";
/* Doesn't work for optional call, computed property, and spread arguments */
/* Doesn't work for computed property, and spread arguments */
/**
* @param {import("@babel/types").Node} node
@@ -1,43 +1,8 @@
import { fileURLToPath } from "node:url";
import { outdent } from "outdent";
import { createIdentifier, isIdentifier } from "./utilities.js";
import createFunctionCallTransform from "./create-function-call-transform.js";
/*
`Object.groupBy(items, callbackFn)` -> `__objectGroupBy(items, callbackFn)`
*/
const transformObjectGroupBy = createFunctionCallTransform({
function: "Object.groupBy",
argumentsLength: 2,
});
const functionName = "__objectGroupBy";
const functionImplementationPath = fileURLToPath(
new URL("../../shims/object-group-by.js", import.meta.url),
);
const transformObjectHasOwnCall = {
shouldSkip: (text) => !text.includes("Object.groupBy("),
/**
* @param {import("@babel/types").Node} node
* @returns {boolean}
*/
test: (node) =>
node.type === "CallExpression" &&
!node.optional &&
node.arguments.length === 2 &&
node.arguments.every(({ type }) => type !== "SpreadElement") &&
node.callee.type === "MemberExpression" &&
!node.callee.optional &&
!node.callee.computed &&
isIdentifier(node.callee.object, "Object") &&
isIdentifier(node.callee.property, "groupBy"),
/**
* @param {import("@babel/types").CallExpression} node
* @returns {import("@babel/types").CallExpression}
*/
transform: (node) => ({
...node,
callee: createIdentifier(functionName),
}),
inject: outdent`
import ${functionName} from ${JSON.stringify(functionImplementationPath)};
`,
};
export default transformObjectHasOwnCall;
export default transformObjectGroupBy;
@@ -1,33 +1,8 @@
import { createMemberExpression, isIdentifier } from "./utilities.js";
import createFunctionCallTransform from "./create-function-call-transform.js";
/*
`Object.hasOwn(foo, "bar")` -> `Object.prototype.hasOwnProperty.call(foo, "bar")`
*/
const transformObjectHasOwn = createFunctionCallTransform({
function: "Object.hasOwn",
argumentsLength: 2,
});
const transformObjectHasOwnCall = {
shouldSkip: (text) => !text.includes("Object.hasOwn("),
/**
* @param {import("@babel/types").Node} node
* @returns {boolean}
*/
test: (node) =>
node.type === "CallExpression" &&
!node.optional &&
node.arguments.length === 2 &&
node.arguments.every(({ type }) => type !== "SpreadElement") &&
node.callee.type === "MemberExpression" &&
!node.callee.optional &&
!node.callee.computed &&
isIdentifier(node.callee.object, "Object") &&
isIdentifier(node.callee.property, "hasOwn"),
/**
* @param {import("@babel/types").CallExpression} node
* @returns {import("@babel/types").CallExpression}
*/
transform: (node) => ({
...node,
callee: createMemberExpression("Object.prototype.hasOwnProperty.call"),
}),
};
export default transformObjectHasOwnCall;
export default transformObjectHasOwn;