Implement proposal-optional-chaining (#2572)
* Implement proposal-optional-chaining Closes #2568 * Refactor optional token printingmaster
parent
fea408ff5c
commit
e378f5e9ba
|
@ -220,6 +220,14 @@ FastPath.prototype.needsParens = function(options) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "MemberExpression": {
|
||||||
|
return (
|
||||||
|
parent.type === "MemberExpression" &&
|
||||||
|
parent.object === node &&
|
||||||
|
node.optional
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
case "SpreadElement":
|
case "SpreadElement":
|
||||||
case "SpreadProperty":
|
case "SpreadProperty":
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -24,7 +24,8 @@ function parse(text, parsers, opts) {
|
||||||
"dynamicImport",
|
"dynamicImport",
|
||||||
"numericSeparator",
|
"numericSeparator",
|
||||||
"importMeta",
|
"importMeta",
|
||||||
"optionalCatchBinding"
|
"optionalCatchBinding",
|
||||||
|
"optionalChaining"
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -422,7 +422,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
|
|
||||||
return concat([
|
return concat([
|
||||||
n.name,
|
n.name,
|
||||||
n.optional ? "?" : "",
|
printOptionalToken(path),
|
||||||
n.typeAnnotation && !isFunctionDeclarationIdentifier ? ": " : "",
|
n.typeAnnotation && !isFunctionDeclarationIdentifier ? ": " : "",
|
||||||
path.call(print, "typeAnnotation")
|
path.call(print, "typeAnnotation")
|
||||||
]);
|
]);
|
||||||
|
@ -809,6 +809,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
case "NewExpression":
|
case "NewExpression":
|
||||||
case "CallExpression": {
|
case "CallExpression": {
|
||||||
const isNew = n.type === "NewExpression";
|
const isNew = n.type === "NewExpression";
|
||||||
|
const optional = printOptionalToken(path);
|
||||||
if (
|
if (
|
||||||
// We want to keep require calls as a unit
|
// We want to keep require calls as a unit
|
||||||
(!isNew &&
|
(!isNew &&
|
||||||
|
@ -837,6 +838,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
return concat([
|
return concat([
|
||||||
isNew ? "new " : "",
|
isNew ? "new " : "",
|
||||||
path.call(print, "callee"),
|
path.call(print, "callee"),
|
||||||
|
optional,
|
||||||
path.call(print, "typeParameters"),
|
path.call(print, "typeParameters"),
|
||||||
concat(["(", join(", ", path.map(print, "arguments")), ")"])
|
concat(["(", join(", ", path.map(print, "arguments")), ")"])
|
||||||
]);
|
]);
|
||||||
|
@ -851,6 +853,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
return concat([
|
return concat([
|
||||||
isNew ? "new " : "",
|
isNew ? "new " : "",
|
||||||
path.call(print, "callee"),
|
path.call(print, "callee"),
|
||||||
|
optional,
|
||||||
printFunctionTypeParameters(path, options, print),
|
printFunctionTypeParameters(path, options, print),
|
||||||
printArgumentsList(path, options, print)
|
printArgumentsList(path, options, print)
|
||||||
]);
|
]);
|
||||||
|
@ -969,7 +972,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
comments.printDanglingComments(path, options),
|
comments.printDanglingComments(path, options),
|
||||||
softline,
|
softline,
|
||||||
rightBrace,
|
rightBrace,
|
||||||
n.optional ? "?" : ""
|
printOptionalToken(path)
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -985,7 +988,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
: ""
|
: ""
|
||||||
),
|
),
|
||||||
concat([options.bracketSpacing ? line : softline, rightBrace]),
|
concat([options.bracketSpacing ? line : softline, rightBrace]),
|
||||||
n.optional ? "?" : "",
|
printOptionalToken(path),
|
||||||
n.typeAnnotation ? ": " : "",
|
n.typeAnnotation ? ": " : "",
|
||||||
path.call(print, "typeAnnotation")
|
path.call(print, "typeAnnotation")
|
||||||
]);
|
]);
|
||||||
|
@ -1117,9 +1120,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n.optional) {
|
parts.push(printOptionalToken(path));
|
||||||
parts.push("?");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n.typeAnnotation) {
|
if (n.typeAnnotation) {
|
||||||
parts.push(": ", path.call(print, "typeAnnotation"));
|
parts.push(": ", path.call(print, "typeAnnotation"));
|
||||||
|
@ -2072,7 +2073,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
case "FunctionTypeParam":
|
case "FunctionTypeParam":
|
||||||
return concat([
|
return concat([
|
||||||
path.call(print, "name"),
|
path.call(print, "name"),
|
||||||
n.optional ? "?" : "",
|
printOptionalToken(path),
|
||||||
n.name ? ": " : "",
|
n.name ? ": " : "",
|
||||||
path.call(print, "typeAnnotation")
|
path.call(print, "typeAnnotation")
|
||||||
]);
|
]);
|
||||||
|
@ -2221,7 +2222,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
isGetterOrSetter(n) ? n.kind + " " : "",
|
isGetterOrSetter(n) ? n.kind + " " : "",
|
||||||
variance || "",
|
variance || "",
|
||||||
path.call(print, "key"),
|
path.call(print, "key"),
|
||||||
n.optional ? "?" : "",
|
printOptionalToken(path),
|
||||||
isFunctionNotation(n) ? "" : ": ",
|
isFunctionNotation(n) ? "" : ": ",
|
||||||
path.call(print, "value")
|
path.call(print, "value")
|
||||||
]);
|
]);
|
||||||
|
@ -2387,9 +2388,9 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
if (n.computed) {
|
if (n.computed) {
|
||||||
parts.push("]");
|
parts.push("]");
|
||||||
}
|
}
|
||||||
if (n.optional) {
|
|
||||||
parts.push("?");
|
parts.push(printOptionalToken(path));
|
||||||
}
|
|
||||||
if (n.typeAnnotation) {
|
if (n.typeAnnotation) {
|
||||||
parts.push(": ");
|
parts.push(": ");
|
||||||
parts.push(path.call(print, "typeAnnotation"));
|
parts.push(path.call(print, "typeAnnotation"));
|
||||||
|
@ -2531,7 +2532,7 @@ function genericPrintNoParens(path, options, print, args) {
|
||||||
n.computed ? "[" : "",
|
n.computed ? "[" : "",
|
||||||
path.call(print, "key"),
|
path.call(print, "key"),
|
||||||
n.computed ? "]" : "",
|
n.computed ? "]" : "",
|
||||||
n.optional ? "?" : "",
|
printOptionalToken(path),
|
||||||
printFunctionParams(
|
printFunctionParams(
|
||||||
path,
|
path,
|
||||||
print,
|
print,
|
||||||
|
@ -3400,12 +3401,27 @@ function printClass(path, options, print) {
|
||||||
return parts;
|
return parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function printOptionalToken(path) {
|
||||||
|
const node = path.getValue();
|
||||||
|
if (!node.optional) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
node.type === "CallExpression" ||
|
||||||
|
(node.type === "MemberExpression" && node.computed)
|
||||||
|
) {
|
||||||
|
return "?.";
|
||||||
|
}
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
|
||||||
function printMemberLookup(path, options, print) {
|
function printMemberLookup(path, options, print) {
|
||||||
const property = path.call(print, "property");
|
const property = path.call(print, "property");
|
||||||
const n = path.getValue();
|
const n = path.getValue();
|
||||||
|
const optional = printOptionalToken(path);
|
||||||
|
|
||||||
if (!n.computed) {
|
if (!n.computed) {
|
||||||
return concat([".", property]);
|
return concat([optional, ".", property]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -3413,11 +3429,11 @@ function printMemberLookup(path, options, print) {
|
||||||
(n.property.type === "Literal" && typeof n.property.value === "number") ||
|
(n.property.type === "Literal" && typeof n.property.value === "number") ||
|
||||||
n.property.type === "NumericLiteral"
|
n.property.type === "NumericLiteral"
|
||||||
) {
|
) {
|
||||||
return concat(["[", property, "]"]);
|
return concat([optional, "[", property, "]"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return group(
|
return group(
|
||||||
concat(["[", indent(concat([softline, property])), softline, "]"])
|
concat([optional, "[", indent(concat([softline, property])), softline, "]"])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3455,6 +3471,7 @@ function printMemberChain(path, options, print) {
|
||||||
path,
|
path,
|
||||||
() =>
|
() =>
|
||||||
concat([
|
concat([
|
||||||
|
printOptionalToken(path),
|
||||||
printFunctionTypeParameters(path, options, print),
|
printFunctionTypeParameters(path, options, print),
|
||||||
printArgumentsList(path, options, print)
|
printArgumentsList(path, options, print)
|
||||||
]),
|
]),
|
||||||
|
@ -3485,9 +3502,11 @@ function printMemberChain(path, options, print) {
|
||||||
// Note: the comments of the root node have already been printed, so we
|
// Note: the comments of the root node have already been printed, so we
|
||||||
// need to extract this first call without printing them as they would
|
// need to extract this first call without printing them as they would
|
||||||
// if handled inside of the recursive call.
|
// if handled inside of the recursive call.
|
||||||
|
const node = path.getValue();
|
||||||
printedNodes.unshift({
|
printedNodes.unshift({
|
||||||
node: path.getValue(),
|
node,
|
||||||
printed: concat([
|
printed: concat([
|
||||||
|
printOptionalToken(path),
|
||||||
printFunctionTypeParameters(path, options, print),
|
printFunctionTypeParameters(path, options, print),
|
||||||
printArgumentsList(path, options, print)
|
printArgumentsList(path, options, print)
|
||||||
])
|
])
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`chaining.js 1`] = `
|
||||||
|
var street = user.address?.street
|
||||||
|
var fooValue = myForm.querySelector('input[name=foo]')?.value
|
||||||
|
|
||||||
|
obj?.prop;
|
||||||
|
obj?.[expr];
|
||||||
|
func?.(...args);
|
||||||
|
|
||||||
|
a?.();
|
||||||
|
a?.[++x];
|
||||||
|
a?.b.c(++x).d;
|
||||||
|
a?.b[3].c?.(x).d;
|
||||||
|
(a?.b).c;
|
||||||
|
delete a?.b;
|
||||||
|
|
||||||
|
a?.b[3].c?.(x).d.e?.f[3].g?.(y).h;
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
var street = user.address?.street;
|
||||||
|
var fooValue = myForm.querySelector("input[name=foo]")?.value;
|
||||||
|
|
||||||
|
obj?.prop;
|
||||||
|
obj?.[expr];
|
||||||
|
func?.(...args);
|
||||||
|
|
||||||
|
a?.();
|
||||||
|
a?.[++x];
|
||||||
|
a?.b.c(++x).d;
|
||||||
|
a?.b[3].c?.(x).d;
|
||||||
|
(a?.b).c;
|
||||||
|
delete a?.b;
|
||||||
|
|
||||||
|
a?.b[3].c?.(x).d.e?.f[3].g?.(y).h;
|
||||||
|
|
||||||
|
`;
|
|
@ -0,0 +1,15 @@
|
||||||
|
var street = user.address?.street
|
||||||
|
var fooValue = myForm.querySelector('input[name=foo]')?.value
|
||||||
|
|
||||||
|
obj?.prop;
|
||||||
|
obj?.[expr];
|
||||||
|
func?.(...args);
|
||||||
|
|
||||||
|
a?.();
|
||||||
|
a?.[++x];
|
||||||
|
a?.b.c(++x).d;
|
||||||
|
a?.b[3].c?.(x).d;
|
||||||
|
(a?.b).c;
|
||||||
|
delete a?.b;
|
||||||
|
|
||||||
|
a?.b[3].c?.(x).d.e?.f[3].g?.(y).h;
|
|
@ -0,0 +1 @@
|
||||||
|
run_spec(__dirname, { parser: "babylon" });
|
Loading…
Reference in New Issue