Skip to content

Commit

Permalink
Add stricter Optional Chain node validation (#11250)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
jridgewell committed Mar 17, 2020
1 parent 5997668 commit 5650060
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 2 deletions.
9 changes: 7 additions & 2 deletions packages/babel-types/src/definitions/experimental.js
@@ -1,5 +1,6 @@
// @flow
import defineType, {
assertOptionalChainStart,
assertEach,
assertNodeType,
assertNodeOrValueType,
Expand Down Expand Up @@ -105,7 +106,9 @@ defineType("OptionalMemberExpression", {
default: false,
},
optional: {
validate: assertValueType("boolean"),
validate: !process.env.BABEL_TYPES_8_BREAKING
? assertValueType("boolean")
: chain(assertValueType("boolean"), assertOptionalChainStart()),
},
},
});
Expand Down Expand Up @@ -151,7 +154,9 @@ defineType("OptionalCallExpression", {
),
},
optional: {
validate: assertValueType("boolean"),
validate: !process.env.BABEL_TYPES_8_BREAKING
? assertValueType("boolean")
: chain(assertValueType("boolean"), assertOptionalChainStart()),
},
typeArguments: {
validate: assertNodeType("TypeParameterInstantiation"),
Expand Down
28 changes: 28 additions & 0 deletions packages/babel-types/src/definitions/utils.js
Expand Up @@ -186,6 +186,34 @@ export function assertShape(shape: { [string]: FieldOptions }): Validator {
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 {
function validate(...args) {
for (const fn of fns) {
Expand Down

0 comments on commit 5650060

Please sign in to comment.