Support optional chaining in .at() transform (#13528)
This commit is contained in:
1 parent
6d4d011252
commit
bb2104dd7c
3 files changed
+83
-9
No files matched your search
@@ -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;
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)"`);
|
||||||
|
});
|
||||||
Reference in new issue
Block a user