Support optional chaining in .at() transform (#13528)

This commit is contained in:
fisker Cheung authored and GitHub committed 2022-09-26 22:54:17 +08:00
1 parent 6d4d011252
commit bb2104dd7c
3 files changed
+83 -9

No files matched your search

+7 -2
View File
@@ -5,9 +5,14 @@ Note:
2. `postcss` contains `Container` class with `.at` method 2. `postcss` contains `Container` class with `.at` method
*/ */
const at = (object, index, ...args) => const at = (isOptionalObject, object, index, ...args) => {
object.at if (isOptionalObject && (object === undefined || object === null)) {
return;
}
return object.at
? object.at(index, ...args) ? object.at(index, ...args)
: object[index < 0 ? object.length + index : index]; : object[index < 0 ? object.length + index : index];
};
export default at; export default at;
+25 -7
View File
@@ -8,9 +8,14 @@ import { SOURCE_DIR } from "../../utils/index.mjs";
const generate = babelGenerator.default; const generate = babelGenerator.default;
const atHelperPath = fileURLToPath(new URL("../shims/at.js", import.meta.url)); const atHelperPath = fileURLToPath(new URL("../shims/at.js", import.meta.url));
/* Doesn't work for dependencies, optional chaining, and spread arguments */ /* Doesn't work for dependencies, optional call, computed property, and spread arguments */
// `Object.hasOwn(foo, "bar")` -> `Object.prototype.hasOwnProperty.call(foo, "bar")` /**
* `Object.hasOwn(foo, "bar")` -> `Object.prototype.hasOwnProperty.call(foo, "bar")`
*
* @param {import("@babel/types").Node} node
* @returns {boolean}
*/
function transformObjectHasOwnCall(node) { function transformObjectHasOwnCall(node) {
if ( if (
!( !(
@@ -47,16 +52,23 @@ function transformObjectHasOwnCall(node) {
return true; return true;
} }
// `foo.at(index)` -> `__at(foo, index)` /**
* `foo.at(index)` -> `__at(false, foo, index)`
* `foo?.at(index)` -> `__at(true, foo, index)`
*
* @param {import("@babel/types").Node} node
* @returns {boolean}
*/
function transformRelativeIndexingCall(node) { function transformRelativeIndexingCall(node) {
if ( if (
!( !(
node.type === "CallExpression" && (node.type === "CallExpression" ||
node.type === "OptionalCallExpression") &&
!node.optional && !node.optional &&
node.arguments.length === 1 && node.arguments.length === 1 &&
node.arguments.every(({ type }) => type !== "SpreadElement") && node.arguments.every(({ type }) => type !== "SpreadElement") &&
node.callee.type === "MemberExpression" && (node.callee.type === "MemberExpression" ||
!node.callee.optional && node.callee.type === "OptionalMemberExpression") &&
!node.callee.computed && !node.callee.computed &&
node.callee.object.type !== "ThisExpression" && node.callee.object.type !== "ThisExpression" &&
node.callee.property.type === "Identifier" && node.callee.property.type === "Identifier" &&
@@ -66,7 +78,13 @@ function transformRelativeIndexingCall(node) {
return false; return false;
} }
node.arguments.unshift(node.callee.object); node.arguments.unshift(
{
type: "BooleanLiteral",
value: node.callee.type === "OptionalMemberExpression",
},
node.callee.object
);
node.callee = { type: "Identifier", name: "__at" }; node.callee = { type: "Identifier", name: "__at" };
return true; return true;
+51
View File
@@ -0,0 +1,51 @@
/* eslint-disable quotes -- `toMatchInlineSnapshot` uses template literal */
import url from "node:url";
import path from "node:path";
import transformCode from "../../scripts/build/transform/index.js";
const file = url.fileURLToPath(
new URL("../../src/__dummy.js", import.meta.url)
);
const shimsDirectory = url.fileURLToPath(
new URL("../../scripts/build/shims", import.meta.url)
);
const transform = (code) =>
transformCode(code, file).replaceAll(
JSON.stringify(shimsDirectory + path.sep).slice(1, -1),
"<SHIMS>/"
);
test("Object.hasOwn", () => {
expect(transform("Object.hasOwn(foo, bar)")).toMatchInlineSnapshot(
`"Object.prototype.hasOwnProperty.call(foo, bar);"`
);
});
test(".at", () => {
expect(transform("foo.at(-1)")).toMatchInlineSnapshot(`
"import __at from "<SHIMS>/at.js";
__at(false, foo, -1);"
`);
expect(transform("foo?.at(-1)")).toMatchInlineSnapshot(`
"import __at from "<SHIMS>/at.js";
__at(true, foo, -1);"
`);
expect(transform("foo?.bar.baz.at(-1)")).toMatchInlineSnapshot(`
"import __at from "<SHIMS>/at.js";
__at(true, foo?.bar.baz, -1);"
`);
expect(transform("foo.at(-1)?.bar")).toMatchInlineSnapshot(`
"import __at from "<SHIMS>/at.js";
__at(false, foo, -1)?.bar;"
`);
// Don't support optional call
expect(transform("foo.at?.(-1)")).toMatchInlineSnapshot(`"foo.at?.(-1)"`);
});