2022-11-15 18:26:06 +08:00
|
|
|
import { fileURLToPath } from "node:url";
|
2025-10-28 02:51:50 +08:00
|
|
|
import { outdent } from "outdent";
|
|
|
|
|
import { OPTIONAL_OBJECT } from "../../shims/shared.js";
|
2025-10-27 23:24:02 +08:00
|
|
|
import { createIdentifier, isIdentifier } from "./utilities.js";
|
2022-11-15 18:26:06 +08:00
|
|
|
|
2026-05-27 15:19:05 +08:00
|
|
|
/* Doesn't work for computed property, and spread arguments */
|
2025-10-28 02:51:50 +08:00
|
|
|
|
2022-11-15 18:26:06 +08:00
|
|
|
/**
|
|
|
|
|
* @param {import("@babel/types").Node} node
|
|
|
|
|
* @returns {boolean}
|
|
|
|
|
*/
|
|
|
|
|
function isMethodCall(node, { methodName, argumentsLength }) {
|
|
|
|
|
return (
|
|
|
|
|
(node.type === "CallExpression" ||
|
|
|
|
|
node.type === "OptionalCallExpression") &&
|
|
|
|
|
!node.optional &&
|
|
|
|
|
node.arguments.length === argumentsLength &&
|
|
|
|
|
node.arguments.every(({ type }) => type !== "SpreadElement") &&
|
|
|
|
|
(node.callee.type === "MemberExpression" ||
|
|
|
|
|
node.callee.type === "OptionalMemberExpression") &&
|
|
|
|
|
!node.callee.computed &&
|
|
|
|
|
node.callee.object.type !== "ThisExpression" &&
|
2025-10-27 23:24:02 +08:00
|
|
|
isIdentifier(node.callee.property, methodName)
|
2022-11-15 18:26:06 +08:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* `foo.at(index)` -> `__at(false, foo, index)`
|
|
|
|
|
* `foo?.at(index)` -> `__at(true, foo, index)`
|
|
|
|
|
*
|
|
|
|
|
* @param {import("@babel/types").CallExpression | import("@babel/types").OptionalCallExpression} node
|
|
|
|
|
* @returns {import("@babel/types").CallExpression}
|
|
|
|
|
*/
|
|
|
|
|
function transformMethodCallToFunctionCall(node, functionName) {
|
|
|
|
|
// `__at(isOptionalObject, object, ...arguments)`
|
|
|
|
|
|
2025-10-28 02:51:50 +08:00
|
|
|
let flags = 0;
|
|
|
|
|
const comments = [];
|
|
|
|
|
if (node.callee.type === "OptionalMemberExpression") {
|
|
|
|
|
flags |= OPTIONAL_OBJECT;
|
|
|
|
|
comments.push("OPTIONAL_OBJECT: true");
|
|
|
|
|
} else {
|
|
|
|
|
comments.push("OPTIONAL_OBJECT: false");
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-27 23:24:02 +08:00
|
|
|
return {
|
|
|
|
|
...node,
|
|
|
|
|
callee: createIdentifier(functionName),
|
|
|
|
|
arguments: [
|
|
|
|
|
{
|
2025-10-28 02:51:50 +08:00
|
|
|
type: "NumericLiteral",
|
|
|
|
|
value: flags,
|
2025-10-27 23:24:02 +08:00
|
|
|
leadingComments: [
|
2025-10-28 02:51:50 +08:00
|
|
|
{
|
|
|
|
|
type: "CommentBlock",
|
|
|
|
|
value: ` ${new Intl.ListFormat("en-US", { type: "conjunction" }).format(comments)} `,
|
|
|
|
|
},
|
2025-10-27 23:24:02 +08:00
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
node.callee.object,
|
|
|
|
|
...node.arguments,
|
|
|
|
|
],
|
|
|
|
|
};
|
2022-11-15 18:26:06 +08:00
|
|
|
}
|
|
|
|
|
|
2025-10-28 02:51:50 +08:00
|
|
|
function createMethodCallTransform({ methodName, argumentsLength }) {
|
|
|
|
|
const functionName = `__${methodName}`;
|
|
|
|
|
const fileName = `method-${methodName.replaceAll(
|
2025-12-21 23:44:08 +08:00
|
|
|
/[A-Z]/g,
|
2025-10-28 02:51:50 +08:00
|
|
|
(character) => `-${character.toLowerCase()}`,
|
|
|
|
|
)}.js`;
|
|
|
|
|
const functionImplementationUrl = new URL(
|
|
|
|
|
`../../shims/${fileName}`,
|
|
|
|
|
import.meta.url,
|
|
|
|
|
);
|
2022-11-15 18:26:06 +08:00
|
|
|
const functionImplementationPath = fileURLToPath(functionImplementationUrl);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
shouldSkip: (text, file) =>
|
|
|
|
|
!text.includes(`.${methodName}(`) || file === functionImplementationPath,
|
|
|
|
|
test: (node) => isMethodCall(node, { methodName, argumentsLength }),
|
|
|
|
|
transform: (node) => transformMethodCallToFunctionCall(node, functionName),
|
2025-10-28 02:51:50 +08:00
|
|
|
inject: outdent`
|
|
|
|
|
import ${functionName} from ${JSON.stringify(functionImplementationPath)};
|
|
|
|
|
`,
|
2022-11-15 18:26:06 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default createMethodCallTransform;
|