diff --git a/lib/rules/at-rule-property-required-list/index.js b/lib/rules/at-rule-property-required-list/index.js index 9689aeb7af..58574e2fb6 100644 --- a/lib/rules/at-rule-property-required-list/index.js +++ b/lib/rules/at-rule-property-required-list/index.js @@ -3,8 +3,9 @@ const isStandardSyntaxAtRule = require('../../utils/isStandardSyntaxAtRule'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); +const validateObjectWithArrayProps = require('../../utils/validateObjectWithArrayProps'); const validateOptions = require('../../utils/validateOptions'); -const { isPlainObject } = require('../../utils/validateTypes'); +const { isString } = require('../../utils/validateTypes'); const ruleName = 'at-rule-property-required-list'; @@ -21,7 +22,7 @@ const rule = (primary) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: primary, - possible: [isPlainObject], + possible: [validateObjectWithArrayProps(isString)], }); if (!validOptions) { diff --git a/lib/rules/declaration-property-unit-allowed-list/index.js b/lib/rules/declaration-property-unit-allowed-list/index.js index da4c5322e1..d914c1a7f6 100644 --- a/lib/rules/declaration-property-unit-allowed-list/index.js +++ b/lib/rules/declaration-property-unit-allowed-list/index.js @@ -8,8 +8,9 @@ const matchesStringOrRegExp = require('../../utils/matchesStringOrRegExp'); const optionsMatches = require('../../utils/optionsMatches'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); +const validateObjectWithArrayProps = require('../../utils/validateObjectWithArrayProps'); const validateOptions = require('../../utils/validateOptions'); -const { isPlainObject } = require('../../utils/validateTypes'); +const { isString } = require('../../utils/validateTypes'); const vendor = require('../../utils/vendor'); const ruleName = 'declaration-property-unit-allowed-list'; @@ -30,7 +31,7 @@ const rule = (primary, secondaryOptions) => { ruleName, { actual: primary, - possible: [isPlainObject], + possible: [validateObjectWithArrayProps(isString)], }, { actual: secondaryOptions, diff --git a/lib/rules/declaration-property-unit-disallowed-list/index.js b/lib/rules/declaration-property-unit-disallowed-list/index.js index bcc48f130a..665d2e31a3 100644 --- a/lib/rules/declaration-property-unit-disallowed-list/index.js +++ b/lib/rules/declaration-property-unit-disallowed-list/index.js @@ -7,8 +7,9 @@ const getUnitFromValueNode = require('../../utils/getUnitFromValueNode'); const matchesStringOrRegExp = require('../../utils/matchesStringOrRegExp'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); +const validateObjectWithArrayProps = require('../../utils/validateObjectWithArrayProps'); const validateOptions = require('../../utils/validateOptions'); -const { isPlainObject } = require('../../utils/validateTypes'); +const { isString } = require('../../utils/validateTypes'); const vendor = require('../../utils/vendor'); const ruleName = 'declaration-property-unit-disallowed-list'; @@ -26,7 +27,7 @@ const rule = (primary) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: primary, - possible: [isPlainObject], + possible: [validateObjectWithArrayProps(isString)], }); if (!validOptions) { diff --git a/lib/rules/declaration-property-value-allowed-list/index.js b/lib/rules/declaration-property-value-allowed-list/index.js index e5e5819663..5636ebde64 100644 --- a/lib/rules/declaration-property-value-allowed-list/index.js +++ b/lib/rules/declaration-property-value-allowed-list/index.js @@ -1,10 +1,12 @@ 'use strict'; const matchesStringOrRegExp = require('../../utils/matchesStringOrRegExp'); +const optionsMatches = require('../../utils/optionsMatches'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); +const validateObjectWithArrayProps = require('../../utils/validateObjectWithArrayProps'); const validateOptions = require('../../utils/validateOptions'); -const { isPlainObject } = require('../../utils/validateTypes'); +const { isString, isRegExp } = require('../../utils/validateTypes'); const vendor = require('../../utils/vendor'); const ruleName = 'declaration-property-value-allowed-list'; @@ -22,7 +24,7 @@ const rule = (primary) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: primary, - possible: [isPlainObject], + possible: [validateObjectWithArrayProps([isString, isRegExp])], }); if (!validOptions) { @@ -42,13 +44,7 @@ const rule = (primary) => { return; } - const propList = primary[propKey]; - - if (!propList || propList.length === 0) { - return; - } - - if (matchesStringOrRegExp(value, propList)) { + if (optionsMatches(primary, propKey, value)) { return; } diff --git a/lib/rules/declaration-property-value-disallowed-list/index.js b/lib/rules/declaration-property-value-disallowed-list/index.js index 1b50cdb948..1fe299923b 100644 --- a/lib/rules/declaration-property-value-disallowed-list/index.js +++ b/lib/rules/declaration-property-value-disallowed-list/index.js @@ -1,10 +1,12 @@ 'use strict'; const matchesStringOrRegExp = require('../../utils/matchesStringOrRegExp'); +const optionsMatches = require('../../utils/optionsMatches'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); +const validateObjectWithArrayProps = require('../../utils/validateObjectWithArrayProps'); const validateOptions = require('../../utils/validateOptions'); -const { isPlainObject } = require('../../utils/validateTypes'); +const { isString, isRegExp } = require('../../utils/validateTypes'); const vendor = require('../../utils/vendor'); const ruleName = 'declaration-property-value-disallowed-list'; @@ -22,7 +24,7 @@ const rule = (primary) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: primary, - possible: [isPlainObject], + possible: [validateObjectWithArrayProps([isString, isRegExp])], }); if (!validOptions) { @@ -42,13 +44,7 @@ const rule = (primary) => { return; } - const propList = primary[propKey]; - - if (!propList || propList.length === 0) { - return; - } - - if (!matchesStringOrRegExp(value, propList)) { + if (!optionsMatches(primary, propKey, value)) { return; } diff --git a/lib/rules/media-feature-name-value-allowed-list/index.js b/lib/rules/media-feature-name-value-allowed-list/index.js index 82ad43c1f2..44bfb77a2f 100644 --- a/lib/rules/media-feature-name-value-allowed-list/index.js +++ b/lib/rules/media-feature-name-value-allowed-list/index.js @@ -5,11 +5,13 @@ const mediaParser = require('postcss-media-query-parser').default; const atRuleParamIndex = require('../../utils/atRuleParamIndex'); const isRangeContextMediaFeature = require('../../utils/isRangeContextMediaFeature'); const matchesStringOrRegExp = require('../../utils/matchesStringOrRegExp'); +const optionsMatches = require('../../utils/optionsMatches'); const rangeContextNodeParser = require('../rangeContextNodeParser'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); +const validateObjectWithArrayProps = require('../../utils/validateObjectWithArrayProps'); const validateOptions = require('../../utils/validateOptions'); -const { isPlainObject } = require('../../utils/validateTypes'); +const { isString, isRegExp } = require('../../utils/validateTypes'); const vendor = require('../../utils/vendor'); const ruleName = 'media-feature-name-value-allowed-list'; @@ -27,7 +29,7 @@ const rule = (primary) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: primary, - possible: [isPlainObject], + possible: [validateObjectWithArrayProps([isString, isRegExp])], }); if (!validOptions) { @@ -78,13 +80,7 @@ const rule = (primary) => { return; } - const allowedValues = primary[allowedValuesKey]; - - if (allowedValues == null) { - return; - } - - if (matchesStringOrRegExp(value, allowedValues)) { + if (optionsMatches(primary, allowedValuesKey, value)) { return; } diff --git a/lib/rules/rule-selector-property-disallowed-list/index.js b/lib/rules/rule-selector-property-disallowed-list/index.js index 01c7702dd5..cc02388317 100644 --- a/lib/rules/rule-selector-property-disallowed-list/index.js +++ b/lib/rules/rule-selector-property-disallowed-list/index.js @@ -4,8 +4,9 @@ const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule'); const matchesStringOrRegExp = require('../../utils/matchesStringOrRegExp'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); +const validateObjectWithArrayProps = require('../../utils/validateObjectWithArrayProps'); const validateOptions = require('../../utils/validateOptions'); -const { isPlainObject } = require('../../utils/validateTypes'); +const { isString, isRegExp } = require('../../utils/validateTypes'); const ruleName = 'rule-selector-property-disallowed-list'; @@ -22,7 +23,7 @@ const rule = (primary) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: primary, - possible: [isPlainObject], + possible: [validateObjectWithArrayProps([isString, isRegExp])], }); if (!validOptions) { diff --git a/lib/utils/__tests__/matchesStringOrRegExp.test.js b/lib/utils/__tests__/matchesStringOrRegExp.test.js index 8c635357ce..55afb39367 100644 --- a/lib/utils/__tests__/matchesStringOrRegExp.test.js +++ b/lib/utils/__tests__/matchesStringOrRegExp.test.js @@ -102,3 +102,7 @@ it('matchesStringOrRegExp comparing with a actual RegExp comparisonValue', () => pattern: /FOO/, }); }); + +it('matchesStringOrRegExp comparing with an empty array', () => { + expect(matchesStringOrRegExp('.foo', [])).toBeFalsy(); +}); diff --git a/lib/utils/__tests__/optionsMatches.test.js b/lib/utils/__tests__/optionsMatches.test.js index ba192dec94..6a2765f67c 100644 --- a/lib/utils/__tests__/optionsMatches.test.js +++ b/lib/utils/__tests__/optionsMatches.test.js @@ -29,3 +29,11 @@ it('optionsMatches matches a RegExp', () => { expect(optionsMatches({ foo: ['/\\.bar$/', '.baz'] }, 'foo', '.baz')).toBeTruthy(); expect(optionsMatches({ foo: ['/\\.bar$/', 'qux'] }, 'foo', '.baz')).toBeFalsy(); }); + +it('optionsMatches does not match any value without the property', () => { + expect(optionsMatches({}, 'foo', 'bar')).toBeFalsy(); +}); + +it('optionsMatches does not match any value with the empty array property', () => { + expect(optionsMatches({ foo: [] }, 'foo', 'bar')).toBeFalsy(); +}); diff --git a/lib/utils/validateObjectWithArrayProps.js b/lib/utils/validateObjectWithArrayProps.js index 21d42e3812..66894fc8c6 100644 --- a/lib/utils/validateObjectWithArrayProps.js +++ b/lib/utils/validateObjectWithArrayProps.js @@ -3,39 +3,41 @@ const { isPlainObject } = require('./validateTypes'); /** - * @template T - * @typedef {(i: T) => boolean} Validator - */ - -/** - * Check whether the variable is an object and all its properties are arrays of string values: + * Check whether the variable is an object and all its properties are arrays of values + * that satisfy the specified validator(s): * + * @example * ignoreProperties = { * value1: ["item11", "item12", "item13"], * value2: ["item21", "item22", "item23"], * value3: ["item31", "item32", "item33"], - * } - * @template T - * @param {Validator|Validator[]} validator + * }; + * validateObjectWithArrayProps(isString)(ignoreProperties); + * //=> true + * + * @template {(value: unknown) => boolean} Validator + * @param {Validator | Validator[]} validator * @returns {(value: unknown) => boolean} */ -module.exports = (validator) => (value) => { - if (!isPlainObject(value)) { - return false; - } - - return Object.values(value).every((array) => { - if (!Array.isArray(array)) { +module.exports = function validateObjectWithArrayProps(validator) { + return (value) => { + if (!isPlainObject(value)) { return false; } - // Make sure the array items are strings - return array.every((item) => { - if (Array.isArray(validator)) { - return validator.some((v) => v(item)); + return Object.values(value).every((array) => { + if (!Array.isArray(array)) { + return false; } - return validator(item); + // Make sure the array items are strings + return array.every((item) => { + if (Array.isArray(validator)) { + return validator.some((v) => v(item)); + } + + return validator(item); + }); }); - }); + }; };