From db8922b7afda06a85517d4cb57febb852e551956 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Thu, 12 Mar 2020 03:08:15 -0400 Subject: [PATCH 1/2] 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`. --- .../src/definitions/experimental.js | 9 +++++-- packages/babel-types/src/definitions/utils.js | 27 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/packages/babel-types/src/definitions/experimental.js b/packages/babel-types/src/definitions/experimental.js index a77350105a5b..22f302a9eb5e 100644 --- a/packages/babel-types/src/definitions/experimental.js +++ b/packages/babel-types/src/definitions/experimental.js @@ -1,5 +1,6 @@ // @flow import defineType, { + assertOptionalChainStart, assertEach, assertNodeType, assertValueType, @@ -104,7 +105,9 @@ defineType("OptionalMemberExpression", { default: false, }, optional: { - validate: assertValueType("boolean"), + validate: !process.env.BABEL_TYPES_8_BREAKING + ? assertValueType("boolean") + : chain(assertValueType("boolean"), assertOptionalChainStart()), }, }, }); @@ -150,7 +153,9 @@ defineType("OptionalCallExpression", { ), }, optional: { - validate: assertValueType("boolean"), + validate: !process.env.BABEL_TYPES_8_BREAKING + ? assertValueType("boolean") + : chain(assertValueType("boolean"), assertOptionalChainStart()), }, typeArguments: { validate: assertNodeType("TypeParameterInstantiation"), diff --git a/packages/babel-types/src/definitions/utils.js b/packages/babel-types/src/definitions/utils.js index cc3a347cca4a..76e8456fe8aa 100644 --- a/packages/babel-types/src/definitions/utils.js +++ b/packages/babel-types/src/definitions/utils.js @@ -186,6 +186,33 @@ export function assertShape(shape: { [string]: FieldOptions }): Validator { return validate; } +export function assertOptionalChainStart(): Validator { + function validate(node) { + for (let current = node; current; ) { + 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`, + ); + } + + return validate; +} + export function chain(...fns: Array): Validator { function validate(...args) { for (const fn of fns) { From b1c4c1ec8afe802f5142fc5c2d981b6ad5844613 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Fri, 13 Mar 2020 01:59:57 -0400 Subject: [PATCH 2/2] Print current node's type when finding an error. --- packages/babel-types/src/definitions/utils.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/babel-types/src/definitions/utils.js b/packages/babel-types/src/definitions/utils.js index 76e8456fe8aa..6fc112be19a2 100644 --- a/packages/babel-types/src/definitions/utils.js +++ b/packages/babel-types/src/definitions/utils.js @@ -188,7 +188,8 @@ export function assertShape(shape: { [string]: FieldOptions }): Validator { export function assertOptionalChainStart(): Validator { function validate(node) { - for (let current = node; current; ) { + let current = node; + while (node) { const { type } = current; if (type === "OptionalCallExpression") { if (current.optional) return; @@ -206,7 +207,7 @@ export function assertOptionalChainStart(): Validator { } throw new TypeError( - `Non-optional ${node.type} must chain from an optional OptionalMemberExpression or OptionalCallExpression`, + `Non-optional ${node.type} must chain from an optional OptionalMemberExpression or OptionalCallExpression. Found chain from ${current?.type}`, ); }