diff --git a/.flowconfig b/.flowconfig index c39ebba..6c51505 100644 --- a/.flowconfig +++ b/.flowconfig @@ -2,3 +2,6 @@ /lib/.* /docs/.* /reports/.* + +[options] +esproposal.optional_chaining=enable \ No newline at end of file diff --git a/__tests__/src/rules/has-valid-accessibility-ignores-invert-colors-test.js b/__tests__/src/rules/has-valid-accessibility-ignores-invert-colors-test.js index 7a59cc4..5249ad4 100644 --- a/__tests__/src/rules/has-valid-accessibility-ignores-invert-colors-test.js +++ b/__tests__/src/rules/has-valid-accessibility-ignores-invert-colors-test.js @@ -65,6 +65,9 @@ ruleTester.run('has-valid-accessibility-ignores-invert-colors', rule, { );`, }, + { + code: ``, + }, ].map(parserOptionsMapper), invalid: [ { diff --git a/__tests__/src/rules/has-valid-accessibility-states-test.js b/__tests__/src/rules/has-valid-accessibility-states-test.js index 42ace9e..5c1e513 100644 --- a/__tests__/src/rules/has-valid-accessibility-states-test.js +++ b/__tests__/src/rules/has-valid-accessibility-states-test.js @@ -40,6 +40,10 @@ ruleTester.run('has-valid-accessibility-states', rule, { }, { code: ';' }, { code: ';' }, + { + code: + '', + }, ].map(parserOptionsMapper), invalid: [ { diff --git a/__tests__/src/rules/has-valid-important-for-accessibility-test.js b/__tests__/src/rules/has-valid-important-for-accessibility-test.js index f1d7aa6..3166752 100644 --- a/__tests__/src/rules/has-valid-important-for-accessibility-test.js +++ b/__tests__/src/rules/has-valid-important-for-accessibility-test.js @@ -29,6 +29,10 @@ ruleTester.run('has-valid-important-for-accessibility', rule, { { code: '' }, { code: '' }, { code: '' }, + { + code: + '', + }, ].map(parserOptionsMapper), invalid: [ { diff --git a/src/factory/valid-prop.js b/src/factory/valid-prop.js index 7fd4248..a495d13 100644 --- a/src/factory/valid-prop.js +++ b/src/factory/valid-prop.js @@ -5,6 +5,7 @@ import { elementType, getLiteralPropValue } from 'jsx-ast-utils'; import { generateObjSchema } from '../util/schemas'; import type { ESLintContext } from '../../flow/eslint'; import isOneOf from '../util/isOneOf'; +import isNodePropExpression from '../util/isNodePropExpression'; /** * Produces an ESLint rule that validates a prop against an array of acceptable values @@ -30,24 +31,27 @@ const createValidPropRule = ( JSXAttribute: (node: JSXAttribute) => { const attrName = elementType(node); if (attrName === propName) { - // ensure we are only checking literal prop values - const attrValue = getLiteralPropValue(node); - let invalid = false; + const isExpression = isNodePropExpression(node); + if (!isExpression) { + // ensure we are only checking literal prop values + const attrValue = getLiteralPropValue(node); + let invalid = false; - if (Array.isArray(attrValue)) { - const validate = attrValue.map((strValue) => - isOneOf(strValue, validValues) - ); - invalid = validate.indexOf(false) > -1; - } else { - invalid = !isOneOf(attrValue, validValues); - } + if (Array.isArray(attrValue)) { + const validate = attrValue.map((strValue) => + isOneOf(strValue, validValues) + ); + invalid = validate.indexOf(false) > -1; + } else { + invalid = !isOneOf(attrValue, validValues); + } - if (invalid) { - context.report({ - node, - message: errorMessage, - }); + if (invalid) { + context.report({ + node, + message: errorMessage, + }); + } } } }, diff --git a/src/util/isNodePropExpression.js b/src/util/isNodePropExpression.js new file mode 100644 index 0000000..0febefb --- /dev/null +++ b/src/util/isNodePropExpression.js @@ -0,0 +1,11 @@ +// @flow +import type { JSXAttribute } from 'ast-types-flow'; + +const ALLOWED_TYPES = ['Identifier', 'ConditionalExpression']; + +export default function isattrPropExpression(attr: JSXAttribute): boolean { + // $FlowFixMe + const expression = attr.value?.expression; + // $FlowFixMe + return expression && ALLOWED_TYPES.includes(expression.type); +} diff --git a/src/util/isNodePropValueBoolean.js b/src/util/isNodePropValueBoolean.js index 181b416..a9a403b 100644 --- a/src/util/isNodePropValueBoolean.js +++ b/src/util/isNodePropValueBoolean.js @@ -1,5 +1,6 @@ // @flow import type { JSXAttribute } from 'ast-types-flow'; +import isNodePropExpression from './isNodePropExpression'; export default function isattrPropValueBoolean(attr: JSXAttribute): boolean { /** @@ -9,34 +10,20 @@ export default function isattrPropValueBoolean(attr: JSXAttribute): boolean { * of the boolean, so that we can correctly identify this error. */ - if (typeof attr !== 'object' || !attr.hasOwnProperty('value')) { - // Loose check for correct data being passed in to this function - throw new Error('isattrPropValueBoolean expects a attr object as argument'); - } - - const { value } = attr; - - if (value === null) { + const isExpression = isNodePropExpression(attr); + if (isExpression || attr.value === null) { + // we can't determine the resulting value type of JSXExpressions // attr.value is null when it is declared as a prop but not equal to anything. This defaults to `true` in JSX - return true; - } - - if (!value || !value.expression) { - return false; - } - - const { expression } = value; - - if (expression.type === 'Identifier') { - // we can't determine the associated value type of an Identifier expression // treat these cases as though they are valid return true; + } else { + // $FlowFixMe + const { expression } = attr.value; + if (expression?.type !== 'Literal') { + // If not a literal, it cannot be a boolean + return false; + } + // $FlowFixMe + return typeof expression?.value === 'boolean'; } - - if (expression.type !== 'Literal') { - // If not a literal, it cannot be a boolean - return false; - } - // $FlowFixMe - return typeof expression.value === 'boolean'; }