Microbouji patch/8136 (#9073)
* Fix optional chaining bug regarding spread in function calls * Revamp optional-chain to be top down Instead of going both upwards and downwards from the first real optional expression, we can just start from the top down. * Add more tests
This commit is contained in:
parent
856edbf95f
commit
844dd33f3d
@ -7,32 +7,40 @@ export default declare((api, options) => {
|
|||||||
|
|
||||||
const { loose = false } = options;
|
const { loose = false } = options;
|
||||||
|
|
||||||
function optional(path, replacementPath) {
|
return {
|
||||||
const { scope } = path;
|
name: "proposal-optional-chaining",
|
||||||
|
inherits: syntaxOptionalChaining,
|
||||||
|
|
||||||
|
visitor: {
|
||||||
|
"OptionalCallExpression|OptionalMemberExpression"(path) {
|
||||||
|
const { parentPath, scope } = path;
|
||||||
const optionals = [];
|
const optionals = [];
|
||||||
|
|
||||||
let objectPath = path;
|
let optionalPath = path;
|
||||||
while (
|
while (
|
||||||
objectPath.isOptionalMemberExpression() ||
|
optionalPath.isOptionalMemberExpression() ||
|
||||||
objectPath.isOptionalCallExpression()
|
optionalPath.isOptionalCallExpression()
|
||||||
) {
|
) {
|
||||||
const { node } = objectPath;
|
const { node } = optionalPath;
|
||||||
if (node.optional) {
|
if (node.optional) {
|
||||||
optionals.push(node);
|
optionals.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (objectPath.isOptionalMemberExpression()) {
|
if (optionalPath.isOptionalMemberExpression()) {
|
||||||
objectPath.node.type = "MemberExpression";
|
optionalPath.node.type = "MemberExpression";
|
||||||
objectPath = objectPath.get("object");
|
optionalPath = optionalPath.get("object");
|
||||||
} else {
|
} else if (optionalPath.isOptionalCallExpression()) {
|
||||||
objectPath.node.type = "CallExpression";
|
optionalPath.node.type = "CallExpression";
|
||||||
objectPath = objectPath.get("callee");
|
optionalPath = optionalPath.get("callee");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let replacementPath = path;
|
||||||
|
if (parentPath.isUnaryExpression({ operator: "delete" })) {
|
||||||
|
replacementPath = parentPath;
|
||||||
|
}
|
||||||
for (let i = optionals.length - 1; i >= 0; i--) {
|
for (let i = optionals.length - 1; i >= 0; i--) {
|
||||||
const node = optionals[i];
|
const node = optionals[i];
|
||||||
node.optional = false;
|
|
||||||
|
|
||||||
const isCall = t.isCallExpression(node);
|
const isCall = t.isCallExpression(node);
|
||||||
const replaceKey = isCall ? "callee" : "object";
|
const replaceKey = isCall ? "callee" : "object";
|
||||||
@ -79,7 +87,10 @@ export default declare((api, options) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
node.arguments.unshift(t.cloneNode(context));
|
node.arguments.unshift(t.cloneNode(context));
|
||||||
node.callee = t.memberExpression(node.callee, t.identifier("call"));
|
node.callee = t.memberExpression(
|
||||||
|
node.callee,
|
||||||
|
t.identifier("call"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +100,11 @@ export default declare((api, options) => {
|
|||||||
? t.binaryExpression("==", t.cloneNode(check), t.nullLiteral())
|
? t.binaryExpression("==", t.cloneNode(check), t.nullLiteral())
|
||||||
: t.logicalExpression(
|
: t.logicalExpression(
|
||||||
"||",
|
"||",
|
||||||
t.binaryExpression("===", t.cloneNode(check), t.nullLiteral()),
|
t.binaryExpression(
|
||||||
|
"===",
|
||||||
|
t.cloneNode(check),
|
||||||
|
t.nullLiteral(),
|
||||||
|
),
|
||||||
t.binaryExpression(
|
t.binaryExpression(
|
||||||
"===",
|
"===",
|
||||||
t.cloneNode(ref),
|
t.cloneNode(ref),
|
||||||
@ -103,40 +118,6 @@ export default declare((api, options) => {
|
|||||||
|
|
||||||
replacementPath = replacementPath.get("alternate");
|
replacementPath = replacementPath.get("alternate");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function findReplacementPath(path) {
|
|
||||||
return path.find(path => {
|
|
||||||
const { parentPath } = path;
|
|
||||||
|
|
||||||
if (path.key == "object" && parentPath.isOptionalMemberExpression()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (path.key == "callee" && parentPath.isOptionalCallExpression()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
path.key == "argument" &&
|
|
||||||
parentPath.isUnaryExpression({ operator: "delete" })
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: "proposal-optional-chaining",
|
|
||||||
inherits: syntaxOptionalChaining,
|
|
||||||
|
|
||||||
visitor: {
|
|
||||||
"OptionalCallExpression|OptionalMemberExpression"(path) {
|
|
||||||
if (!path.node.optional) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional(path, findReplacementPath(path));
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
a?.(...args);
|
||||||
|
|
||||||
|
a?.b(...args);
|
||||||
|
|
||||||
|
a?.b(...args).c;
|
||||||
|
|
||||||
|
a?.b(...args).c(...args);
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"external-helpers",
|
||||||
|
"proposal-optional-chaining",
|
||||||
|
"transform-spread"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
var _a, _a2, _a3, _a4, _a4$b;
|
||||||
|
|
||||||
|
(_a = a) === null || _a === void 0 ? void 0 : _a.apply(void 0, babelHelpers.toConsumableArray(args));
|
||||||
|
(_a2 = a) === null || _a2 === void 0 ? void 0 : _a2.b.apply(_a2, babelHelpers.toConsumableArray(args));
|
||||||
|
(_a3 = a) === null || _a3 === void 0 ? void 0 : _a3.b.apply(_a3, babelHelpers.toConsumableArray(args)).c;
|
||||||
|
(_a4 = a) === null || _a4 === void 0 ? void 0 : (_a4$b = _a4.b.apply(_a4, babelHelpers.toConsumableArray(args))).c.apply(_a4$b, babelHelpers.toConsumableArray(args));
|
||||||
Loading…
x
Reference in New Issue
Block a user