Add stricter Optional Chain node validation (#11250)
* Add stricter Optional Chain node validation Optional chains cannot start with `optional: false`. Somewhere in the `object`/`callee` tree, there must be an optional with `optional: true`. * Print current node's type when finding an error.
This commit is contained in:
parent
59976680df
commit
56500603da
@ -1,5 +1,6 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import defineType, {
|
import defineType, {
|
||||||
|
assertOptionalChainStart,
|
||||||
assertEach,
|
assertEach,
|
||||||
assertNodeType,
|
assertNodeType,
|
||||||
assertNodeOrValueType,
|
assertNodeOrValueType,
|
||||||
@ -105,7 +106,9 @@ defineType("OptionalMemberExpression", {
|
|||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
optional: {
|
optional: {
|
||||||
validate: assertValueType("boolean"),
|
validate: !process.env.BABEL_TYPES_8_BREAKING
|
||||||
|
? assertValueType("boolean")
|
||||||
|
: chain(assertValueType("boolean"), assertOptionalChainStart()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -151,7 +154,9 @@ defineType("OptionalCallExpression", {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
optional: {
|
optional: {
|
||||||
validate: assertValueType("boolean"),
|
validate: !process.env.BABEL_TYPES_8_BREAKING
|
||||||
|
? assertValueType("boolean")
|
||||||
|
: chain(assertValueType("boolean"), assertOptionalChainStart()),
|
||||||
},
|
},
|
||||||
typeArguments: {
|
typeArguments: {
|
||||||
validate: assertNodeType("TypeParameterInstantiation"),
|
validate: assertNodeType("TypeParameterInstantiation"),
|
||||||
|
|||||||
@ -186,6 +186,34 @@ export function assertShape(shape: { [string]: FieldOptions }): Validator {
|
|||||||
return validate;
|
return validate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function assertOptionalChainStart(): Validator {
|
||||||
|
function validate(node) {
|
||||||
|
let current = node;
|
||||||
|
while (node) {
|
||||||
|
const { type } = current;
|
||||||
|
if (type === "OptionalCallExpression") {
|
||||||
|
if (current.optional) return;
|
||||||
|
current = current.callee;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "OptionalMemberExpression") {
|
||||||
|
if (current.optional) return;
|
||||||
|
current = current.object;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new TypeError(
|
||||||
|
`Non-optional ${node.type} must chain from an optional OptionalMemberExpression or OptionalCallExpression. Found chain from ${current?.type}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return validate;
|
||||||
|
}
|
||||||
|
|
||||||
export function chain(...fns: Array<Validator>): Validator {
|
export function chain(...fns: Array<Validator>): Validator {
|
||||||
function validate(...args) {
|
function validate(...args) {
|
||||||
for (const fn of fns) {
|
for (const fn of fns) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user