From 9fa46f2b3144f324a5929486e4a8556071a8f9eb Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Wed, 23 Feb 2022 17:27:07 +0900 Subject: [PATCH] Loosen up `validateObjectWithArrayProps()` utility --- .../at-rule-property-required-list/README.md | 2 +- .../__tests__/index.js | 2 +- .../at-rule-property-required-list/index.js | 8 +++-- .../README.md | 4 +-- .../__tests__/index.js | 2 +- .../index.js | 7 ++-- .../README.md | 4 +-- .../__tests__/index.js | 2 +- .../index.js | 5 +-- .../README.md | 4 +-- .../__tests__/index.js | 2 +- .../index.js | 4 +-- .../README.md | 4 +-- .../__tests__/index.js | 2 +- .../index.js | 4 +-- .../README.md | 12 ++----- .../__tests__/index.js | 2 +- .../index.js | 4 +-- .../README.md | 4 +-- .../__tests__/index.js | 2 +- .../index.js | 4 +-- lib/rules/unit-allowed-list/README.md | 4 +-- .../unit-allowed-list/__tests__/index.js | 27 +++++++++++++--- lib/rules/unit-allowed-list/index.js | 2 +- lib/rules/unit-disallowed-list/README.md | 8 ++--- .../unit-disallowed-list/__tests__/index.js | 6 ++-- lib/rules/unit-disallowed-list/index.js | 4 +-- lib/utils/__tests__/flattenArray.test.js | 27 ++++++++++++++++ .../validateObjectWithArrayProps.test.js | 32 ++++++++----------- lib/utils/flattenArray.js | 16 ++++++++++ lib/utils/validateObjectWithArrayProps.js | 30 ++++++----------- 31 files changed, 141 insertions(+), 99 deletions(-) create mode 100644 lib/utils/__tests__/flattenArray.test.js create mode 100644 lib/utils/flattenArray.js diff --git a/lib/rules/at-rule-property-required-list/README.md b/lib/rules/at-rule-property-required-list/README.md index 0232e6fe4d..a628c2c0d6 100644 --- a/lib/rules/at-rule-property-required-list/README.md +++ b/lib/rules/at-rule-property-required-list/README.md @@ -11,7 +11,7 @@ Specify a list of required properties for an at-rule. ## Options -`object`: `{ "at-rule-name": ["array", "of", "properties"] }` +`object`: `{ "at-rule-name": ["array", "of", "properties"]|"property" }` Given: diff --git a/lib/rules/at-rule-property-required-list/__tests__/index.js b/lib/rules/at-rule-property-required-list/__tests__/index.js index 9cc2658dac..7c98880102 100644 --- a/lib/rules/at-rule-property-required-list/__tests__/index.js +++ b/lib/rules/at-rule-property-required-list/__tests__/index.js @@ -6,7 +6,7 @@ testRule({ ruleName, config: { 'font-face': ['font-display', 'font-family'], - page: ['margin'], + page: 'margin', }, accept: [ diff --git a/lib/rules/at-rule-property-required-list/index.js b/lib/rules/at-rule-property-required-list/index.js index 58574e2fb6..5710067e5f 100644 --- a/lib/rules/at-rule-property-required-list/index.js +++ b/lib/rules/at-rule-property-required-list/index.js @@ -1,5 +1,6 @@ 'use strict'; +const flattenArray = require('../../utils/flattenArray'); const isStandardSyntaxAtRule = require('../../utils/isStandardSyntaxAtRule'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); @@ -17,7 +18,7 @@ const meta = { url: 'https://stylelint.io/user-guide/rules/list/at-rule-property-required-list', }; -/** @type {import('stylelint').Rule>} */ +/** @type {import('stylelint').Rule>} */ const rule = (primary) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { @@ -36,12 +37,13 @@ const rule = (primary) => { const { name, nodes } = atRule; const atRuleName = name.toLowerCase(); + const propList = flattenArray(primary[atRuleName]); - if (!primary[atRuleName]) { + if (!propList) { return; } - for (const property of primary[atRuleName]) { + for (const property of propList) { const propertyName = property.toLowerCase(); const hasProperty = nodes.find( diff --git a/lib/rules/declaration-property-unit-allowed-list/README.md b/lib/rules/declaration-property-unit-allowed-list/README.md index 9328efd859..035d657348 100644 --- a/lib/rules/declaration-property-unit-allowed-list/README.md +++ b/lib/rules/declaration-property-unit-allowed-list/README.md @@ -11,7 +11,7 @@ a { width: 100px; } ## Options -`object`: `{ "unprefixed-property-name": ["array", "of", "units"] }` +`object`: `{ "unprefixed-property-name": ["array", "of", "units"]|"unit" }` If a property name is surrounded with `"/"` (e.g. `"/^animation/"`), it is interpreted as a regular expression. This allows, for example, easy targeting of shorthands: `/^animation/` will match `animation`, `animation-duration`, `animation-timing-function`, etc. @@ -20,7 +20,7 @@ Given: ```json { "font-size": ["em", "px"], - "/^animation/": ["s"], + "/^animation/": "s", "line-height": [] } ``` diff --git a/lib/rules/declaration-property-unit-allowed-list/__tests__/index.js b/lib/rules/declaration-property-unit-allowed-list/__tests__/index.js index 94dd12b0af..8322bde386 100644 --- a/lib/rules/declaration-property-unit-allowed-list/__tests__/index.js +++ b/lib/rules/declaration-property-unit-allowed-list/__tests__/index.js @@ -8,7 +8,7 @@ testRule({ config: [ { 'font-size': ['px', 'em'], - margin: ['em'], + margin: 'em', 'background-position': ['%'], animation: ['s'], 'line-height': [], diff --git a/lib/rules/declaration-property-unit-allowed-list/index.js b/lib/rules/declaration-property-unit-allowed-list/index.js index d914c1a7f6..fda85bde7e 100644 --- a/lib/rules/declaration-property-unit-allowed-list/index.js +++ b/lib/rules/declaration-property-unit-allowed-list/index.js @@ -3,6 +3,7 @@ const valueParser = require('postcss-value-parser'); const declarationValueIndex = require('../../utils/declarationValueIndex'); +const flattenArray = require('../../utils/flattenArray'); const getUnitFromValueNode = require('../../utils/getUnitFromValueNode'); const matchesStringOrRegExp = require('../../utils/matchesStringOrRegExp'); const optionsMatches = require('../../utils/optionsMatches'); @@ -23,7 +24,7 @@ const meta = { url: 'https://stylelint.io/user-guide/rules/list/declaration-property-unit-allowed-list', }; -/** @type {import('stylelint').Rule>} */ +/** @type {import('stylelint').Rule>} */ const rule = (primary, secondaryOptions) => { return (root, result) => { const validOptions = validateOptions( @@ -60,7 +61,7 @@ const rule = (primary, secondaryOptions) => { return; } - const propList = primary[propKey]; + const propList = flattenArray(primary[propKey]); if (!propList) { return; @@ -84,7 +85,7 @@ const rule = (primary, secondaryOptions) => { const unit = getUnitFromValueNode(node); - if (!unit || (unit && propList.indexOf(unit.toLowerCase())) !== -1) { + if (!unit || (unit && propList.includes(unit.toLowerCase()))) { return; } diff --git a/lib/rules/declaration-property-unit-disallowed-list/README.md b/lib/rules/declaration-property-unit-disallowed-list/README.md index 312c1ad828..f85e11310d 100644 --- a/lib/rules/declaration-property-unit-disallowed-list/README.md +++ b/lib/rules/declaration-property-unit-disallowed-list/README.md @@ -11,7 +11,7 @@ a { width: 100px; } ## Options -`object`: `{ "unprefixed-property-name": ["array", "of", "units"] }` +`object`: `{ "unprefixed-property-name": ["array", "of", "units"]|"unit" }` If a property name is surrounded with `"/"` (e.g. `"/^animation/"`), it is interpreted as a regular expression. This allows, for example, easy targeting of shorthands: `/^animation/` will match `animation`, `animation-duration`, `animation-timing-function`, etc. @@ -20,7 +20,7 @@ Given: ```json { "font-size": ["em", "px"], - "/^animation/": ["s"] + "/^animation/": "s" } ``` diff --git a/lib/rules/declaration-property-unit-disallowed-list/__tests__/index.js b/lib/rules/declaration-property-unit-disallowed-list/__tests__/index.js index 4f95b06bfa..c1b8f74c5f 100644 --- a/lib/rules/declaration-property-unit-disallowed-list/__tests__/index.js +++ b/lib/rules/declaration-property-unit-disallowed-list/__tests__/index.js @@ -8,7 +8,7 @@ testRule({ config: [ { 'font-size': ['px', 'em'], - margin: ['em'], + margin: 'em', 'background-position': ['%'], animation: ['s'], }, diff --git a/lib/rules/declaration-property-unit-disallowed-list/index.js b/lib/rules/declaration-property-unit-disallowed-list/index.js index 665d2e31a3..f7a778f770 100644 --- a/lib/rules/declaration-property-unit-disallowed-list/index.js +++ b/lib/rules/declaration-property-unit-disallowed-list/index.js @@ -3,6 +3,7 @@ const valueParser = require('postcss-value-parser'); const declarationValueIndex = require('../../utils/declarationValueIndex'); +const flattenArray = require('../../utils/flattenArray'); const getUnitFromValueNode = require('../../utils/getUnitFromValueNode'); const matchesStringOrRegExp = require('../../utils/matchesStringOrRegExp'); const report = require('../../utils/report'); @@ -22,7 +23,7 @@ const meta = { url: 'https://stylelint.io/user-guide/rules/list/declaration-property-unit-disallowed-list', }; -/** @type {import('stylelint').Rule>} */ +/** @type {import('stylelint').Rule>} */ const rule = (primary) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { @@ -48,7 +49,7 @@ const rule = (primary) => { return; } - const propList = primary[propKey]; + const propList = flattenArray(primary[propKey]); if (!propList) { return; diff --git a/lib/rules/declaration-property-value-allowed-list/README.md b/lib/rules/declaration-property-value-allowed-list/README.md index a06e33108e..cff3f35c6d 100644 --- a/lib/rules/declaration-property-value-allowed-list/README.md +++ b/lib/rules/declaration-property-value-allowed-list/README.md @@ -11,7 +11,7 @@ a { text-transform: uppercase; } ## Options -`object`: `{ "unprefixed-property-name": ["array", "of", "values"], "unprefixed-property-name": ["/regex/", "non-regex", /regex/] }` +`object`: `{ "unprefixed-property-name": ["array", "of", "values", "/regex/", /regex/]|"value"|"/regex/"|/regex/ }` If a property name is found in the object, only the listed property values are allowed. This rule complains about all non-matching values. (If the property name is not included in the object, anything goes.) @@ -26,7 +26,7 @@ Given: ```json { "transform": ["/scale/"], - "whitespace": ["nowrap"], + "whitespace": "nowrap", "/color/": ["/^green/"] } ``` diff --git a/lib/rules/declaration-property-value-allowed-list/__tests__/index.js b/lib/rules/declaration-property-value-allowed-list/__tests__/index.js index cd3adf1c87..f369dd7e49 100644 --- a/lib/rules/declaration-property-value-allowed-list/__tests__/index.js +++ b/lib/rules/declaration-property-value-allowed-list/__tests__/index.js @@ -8,7 +8,7 @@ testRule({ config: [ { transform: ['/scale/'], - whitespace: ['nowrap'], + whitespace: 'nowrap', '/color/': ['/^green/'], }, ], diff --git a/lib/rules/declaration-property-value-allowed-list/index.js b/lib/rules/declaration-property-value-allowed-list/index.js index c4ab73d5ab..44f2f83848 100644 --- a/lib/rules/declaration-property-value-allowed-list/index.js +++ b/lib/rules/declaration-property-value-allowed-list/index.js @@ -20,12 +20,12 @@ const meta = { url: 'https://stylelint.io/user-guide/rules/list/declaration-property-value-allowed-list', }; -/** @type {import('stylelint').Rule>} */ +/** @type {import('stylelint').Rule>>} */ const rule = (primary) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: primary, - possible: [validateObjectWithArrayProps([isString, isRegExp])], + possible: [validateObjectWithArrayProps(isString, isRegExp)], }); if (!validOptions) { diff --git a/lib/rules/declaration-property-value-disallowed-list/README.md b/lib/rules/declaration-property-value-disallowed-list/README.md index f54968e1dc..9f16fdc823 100644 --- a/lib/rules/declaration-property-value-disallowed-list/README.md +++ b/lib/rules/declaration-property-value-disallowed-list/README.md @@ -11,7 +11,7 @@ a { text-transform: uppercase; } ## Options -`object`: `{ "unprefixed-property-name": ["array", "of", "values"], "unprefixed-property-name": ["/regex/", "non-regex", /regex/] }` +`object`: `{ "unprefixed-property-name": ["array", "of", "values", "/regex/", /regex/]|"value"|"/regex/"|/regex/ }` If a property name is surrounded with `"/"` (e.g. `"/^animation/"`), it is interpreted as a regular expression. This allows, for example, easy targeting of shorthands: `/^animation/` will match `animation`, `animation-duration`, `animation-timing-function`, etc. @@ -24,7 +24,7 @@ Given: ```json { "transform": ["/scale3d/", "/rotate3d/", "/translate3d/"], - "position": ["fixed"], + "position": "fixed", "color": ["/^green/"], "/^animation/": ["/ease/"] } diff --git a/lib/rules/declaration-property-value-disallowed-list/__tests__/index.js b/lib/rules/declaration-property-value-disallowed-list/__tests__/index.js index efa9330e97..4f6a8db1bd 100644 --- a/lib/rules/declaration-property-value-disallowed-list/__tests__/index.js +++ b/lib/rules/declaration-property-value-disallowed-list/__tests__/index.js @@ -8,7 +8,7 @@ testRule({ config: [ { // regular string - 'text-transform': ['uppercase'], + 'text-transform': 'uppercase', // regexes transform: ['/scale3d/', '/rotate3d/', '/translate3d/'], // mixed string and regex diff --git a/lib/rules/declaration-property-value-disallowed-list/index.js b/lib/rules/declaration-property-value-disallowed-list/index.js index 59b0e913a7..6995c716e9 100644 --- a/lib/rules/declaration-property-value-disallowed-list/index.js +++ b/lib/rules/declaration-property-value-disallowed-list/index.js @@ -20,12 +20,12 @@ const meta = { url: 'https://stylelint.io/user-guide/rules/list/declaration-property-value-disallowed-list', }; -/** @type {import('stylelint').Rule>} */ +/** @type {import('stylelint').Rule>>} */ const rule = (primary) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: primary, - possible: [validateObjectWithArrayProps([isString, isRegExp])], + possible: [validateObjectWithArrayProps(isString, isRegExp)], }); if (!validOptions) { diff --git a/lib/rules/media-feature-name-value-allowed-list/README.md b/lib/rules/media-feature-name-value-allowed-list/README.md index 782dfbab8d..80f0316c23 100644 --- a/lib/rules/media-feature-name-value-allowed-list/README.md +++ b/lib/rules/media-feature-name-value-allowed-list/README.md @@ -11,26 +11,20 @@ Specify a list of allowed media feature name and value pairs. ## Options -```js -{ - "unprefixed-media-feature-name": ["array", "of", "values"], - "/unprefixed-media-feature-name/": ["/regex/", "non-regex", /real-regex/] -} -``` +`object`: `{ "unprefixed-media-feature-name": ["array", "of", "values", "/regex/", /regex/]|"value"|"/regex/"|/regex/ }` If a media feature name is found in the object, only its allowed-listed values are allowed. If the media feature name is not included in the object, anything goes. If a name or value is surrounded with `/` (e.g. `"/width$/"`), it is interpreted -as a regular expression. For example, `/width$/` will match `max-width` and -`min-width`. +as a regular expression. For example, `/width$/` will match `max-width` and `min-width`. Given: ```json { "min-width": ["768px", "1024px"], - "/resolution/": ["/dpcm$/"] + "/resolution/": "/dpcm$/" } ``` diff --git a/lib/rules/media-feature-name-value-allowed-list/__tests__/index.js b/lib/rules/media-feature-name-value-allowed-list/__tests__/index.js index 7ac33c41f6..bd56b71400 100644 --- a/lib/rules/media-feature-name-value-allowed-list/__tests__/index.js +++ b/lib/rules/media-feature-name-value-allowed-list/__tests__/index.js @@ -7,7 +7,7 @@ testRule({ config: [ { 'min-width': ['768px', '$sm'], - '/resolution/': ['/dpcm$/'], // Only dpcm unit + '/resolution/': '/dpcm$/', // Only dpcm unit color: [], // Test boolean context width: [], // Test range context }, 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 44bfb77a2f..9412bafb5b 100644 --- a/lib/rules/media-feature-name-value-allowed-list/index.js +++ b/lib/rules/media-feature-name-value-allowed-list/index.js @@ -24,12 +24,12 @@ const meta = { url: 'https://stylelint.io/user-guide/rules/list/media-feature-name-value-allowed-list', }; -/** @type {import('stylelint').Rule>>} */ +/** @type {import('stylelint').Rule>>} */ const rule = (primary) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: primary, - possible: [validateObjectWithArrayProps([isString, isRegExp])], + possible: [validateObjectWithArrayProps(isString, isRegExp)], }); if (!validOptions) { diff --git a/lib/rules/rule-selector-property-disallowed-list/README.md b/lib/rules/rule-selector-property-disallowed-list/README.md index 1a98194335..5fc170da6a 100644 --- a/lib/rules/rule-selector-property-disallowed-list/README.md +++ b/lib/rules/rule-selector-property-disallowed-list/README.md @@ -11,7 +11,7 @@ Specify a list of disallowed properties for selectors within rules. ## Options -`object`: `{ "selector": ["array", "of", "properties"]` +`object`: `{ "selector": ["array", "of", "properties", "/regex/", /regex/]|"property"|"/regex/"|/regex/` If a selector name is surrounded with `"/"` (e.g. `"/anchor/"`), it is interpreted as a regular expression. This allows, for example, easy targeting of all the potential anchors: `/anchor/` will match `.anchor`, `[data-anchor]`, etc. @@ -22,7 +22,7 @@ Given: ```json { "a": ["color", "/margin/"], - "/foo/": ["/size/"] + "/foo/": "/size/" } ``` diff --git a/lib/rules/rule-selector-property-disallowed-list/__tests__/index.js b/lib/rules/rule-selector-property-disallowed-list/__tests__/index.js index 1cda63712c..11dddb6338 100644 --- a/lib/rules/rule-selector-property-disallowed-list/__tests__/index.js +++ b/lib/rules/rule-selector-property-disallowed-list/__tests__/index.js @@ -6,7 +6,7 @@ testRule({ ruleName, config: { a: ['color', '/margin/'], - '/foo/': ['/size/'], + '/foo/': '/size/', }, accept: [ diff --git a/lib/rules/rule-selector-property-disallowed-list/index.js b/lib/rules/rule-selector-property-disallowed-list/index.js index cc02388317..4438525bcc 100644 --- a/lib/rules/rule-selector-property-disallowed-list/index.js +++ b/lib/rules/rule-selector-property-disallowed-list/index.js @@ -18,12 +18,12 @@ const meta = { url: 'https://stylelint.io/user-guide/rules/list/rule-selector-property-disallowed-list', }; -/** @type {import('stylelint').Rule>>} */ +/** @type {import('stylelint').Rule>>} */ const rule = (primary) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: primary, - possible: [validateObjectWithArrayProps([isString, isRegExp])], + possible: [validateObjectWithArrayProps(isString, isRegExp)], }); if (!validOptions) { diff --git a/lib/rules/unit-allowed-list/README.md b/lib/rules/unit-allowed-list/README.md index 999343212d..fc99236359 100644 --- a/lib/rules/unit-allowed-list/README.md +++ b/lib/rules/unit-allowed-list/README.md @@ -65,7 +65,7 @@ a { transform: rotate(30deg); } ## Optional secondary options -### `ignoreProperties: { unit: ["property", "/regex/", /regex/] }` +### `ignoreProperties: { "unit": ["property", "/regex/", /regex/]|"property"|"/regex/"|/regex/ }` Ignore units in the values of declarations with the specified properties. @@ -114,7 +114,7 @@ a { -moz-border-radius-topright: 20rem; } a { height: 100%; } ``` -### `ignoreFunctions: ["/regex/", /regex/, "string"]` +### `ignoreFunctions: ["function", "/regex/", /regex/]|"function"|"/regex/"|/regex/` Ignore units that are inside of the specified functions. diff --git a/lib/rules/unit-allowed-list/__tests__/index.js b/lib/rules/unit-allowed-list/__tests__/index.js index 73e260d3e2..5021320bc9 100644 --- a/lib/rules/unit-allowed-list/__tests__/index.js +++ b/lib/rules/unit-allowed-list/__tests__/index.js @@ -236,7 +236,7 @@ testRule({ testRule({ ruleName, - config: ['px'], + config: 'px', accept: [ { @@ -324,7 +324,7 @@ testRule({ { ignoreProperties: { rem: ['line-height', 'margin', /^border/], - '%': ['width', 'height'], + '%': 'width', }, }, ], @@ -351,11 +351,11 @@ testRule({ testRule({ ruleName, - config: [['px', 'em'], { ignoreFunctions: ['hsl', '/^rgb/', /-gradient$/] }], + config: ['em', { ignoreFunctions: ['hsl', '/^rgb/', /-gradient$/] }], accept: [ { - code: 'a { border: 1px solid hsl(20deg 0% 20% / 80%); }', + code: 'a { border: 1em solid hsl(20deg 0% 20% / 80%); }', description: 'color functions', }, { @@ -381,3 +381,22 @@ testRule({ }, ], }); + +testRule({ + ruleName, + + config: ['em', { ignoreFunctions: 'hsl' }], + + accept: [ + { + code: 'a { border: 1em solid hsl(20deg 0% 20% / 80%); }', + }, + ], + + reject: [ + { + code: 'a { border: 1em solid hsla(162deg, 51, 35, 0.8); }', + message: messages.rejected('deg'), + }, + ], +}); diff --git a/lib/rules/unit-allowed-list/index.js b/lib/rules/unit-allowed-list/index.js index 4b20f1a428..d5416d48b3 100644 --- a/lib/rules/unit-allowed-list/index.js +++ b/lib/rules/unit-allowed-list/index.js @@ -38,7 +38,7 @@ const rule = (primary, secondaryOptions) => { actual: secondaryOptions, possible: { ignoreFunctions: [isString, isRegExp], - ignoreProperties: [validateObjectWithArrayProps([isString, isRegExp])], + ignoreProperties: [validateObjectWithArrayProps(isString, isRegExp)], }, }, ); diff --git a/lib/rules/unit-disallowed-list/README.md b/lib/rules/unit-disallowed-list/README.md index ef5a6908fb..6b8a5a35e3 100644 --- a/lib/rules/unit-disallowed-list/README.md +++ b/lib/rules/unit-disallowed-list/README.md @@ -60,7 +60,7 @@ a { animation: animation-name 5s ease; } ## Optional secondary options -### `ignoreProperties: { unit: ["property", "/regex/", /regex/] }` +### `ignoreProperties: { "unit": ["property", "/regex/", /regex/]|"property"|"/regex/"|/regex/ }` Ignore units in the values of declarations with the specified properties. @@ -71,7 +71,7 @@ Given: ```json { "px": ["font-size", "/^border/"], - "vmin": ["width"] + "vmin": "width" } ``` @@ -109,7 +109,7 @@ a { -moz-border-radius-topright: 40px; } a { height: 100vmin; } ``` -### `ignoreMediaFeatureNames: { unit: ["property", "/regex/", /regex/] }` +### `ignoreMediaFeatureNames: { "unit": ["property", "/regex/", /regex/]|"property"|"/regex/"|/regex/ }` Ignore units for specific feature names. @@ -120,7 +120,7 @@ Given: ```json { "px": ["min-width", "/height$/"], - "dpi": ["resolution"] + "dpi": "resolution" } ``` diff --git a/lib/rules/unit-disallowed-list/__tests__/index.js b/lib/rules/unit-disallowed-list/__tests__/index.js index ffbaf3a1a1..c0c6489af0 100644 --- a/lib/rules/unit-disallowed-list/__tests__/index.js +++ b/lib/rules/unit-disallowed-list/__tests__/index.js @@ -232,7 +232,7 @@ testRule({ testRule({ ruleName, - config: ['px'], + config: 'px', accept: [ { @@ -320,7 +320,7 @@ testRule({ { ignoreProperties: { px: ['font-size', 'margin', /^border/], - vmin: ['width', 'height'], + vmin: 'width', }, }, ], @@ -511,7 +511,7 @@ testRule({ { ignoreMediaFeatureNames: { px: ['min-width', 'height'], - dpi: ['min-resolution', 'resolution'], + dpi: 'min-resolution', '%': ['width', /^min/], }, }, diff --git a/lib/rules/unit-disallowed-list/index.js b/lib/rules/unit-disallowed-list/index.js index ca2f9edac9..1e81e77b4c 100644 --- a/lib/rules/unit-disallowed-list/index.js +++ b/lib/rules/unit-disallowed-list/index.js @@ -53,8 +53,8 @@ const rule = (primary, secondaryOptions) => { optional: true, actual: secondaryOptions, possible: { - ignoreProperties: [validateObjectWithArrayProps([isString, isRegExp])], - ignoreMediaFeatureNames: [validateObjectWithArrayProps([isString, isRegExp])], + ignoreProperties: [validateObjectWithArrayProps(isString, isRegExp)], + ignoreMediaFeatureNames: [validateObjectWithArrayProps(isString, isRegExp)], }, }, ); diff --git a/lib/utils/__tests__/flattenArray.test.js b/lib/utils/__tests__/flattenArray.test.js new file mode 100644 index 0000000000..125d037cf2 --- /dev/null +++ b/lib/utils/__tests__/flattenArray.test.js @@ -0,0 +1,27 @@ +'use strict'; + +const flattenArray = require('../flattenArray'); + +test('accept undefined', () => { + expect(flattenArray(undefined)).toBeUndefined(); +}); + +test('accept null', () => { + expect(flattenArray(null)).toBeUndefined(); +}); + +test('accept a single value', () => { + expect(flattenArray(1)).toEqual([1]); +}); + +test('accept an array with a single value', () => { + expect(flattenArray([1])).toEqual([1]); +}); + +test('accept an array with multiple values', () => { + expect(flattenArray([1, 2])).toEqual([1, 2]); +}); + +test('accept an empty array', () => { + expect(flattenArray([])).toEqual([]); +}); diff --git a/lib/utils/__tests__/validateObjectWithArrayProps.test.js b/lib/utils/__tests__/validateObjectWithArrayProps.test.js index 9c0bae356b..a9acc6353d 100644 --- a/lib/utils/__tests__/validateObjectWithArrayProps.test.js +++ b/lib/utils/__tests__/validateObjectWithArrayProps.test.js @@ -8,22 +8,13 @@ describe('validateObjectWithArrayProps', () => { }); describe('returned validator', () => { - const validator = validateObjectWithArrayProps((x) => x); + const validator = validateObjectWithArrayProps((x) => x > 0); - it('should return false if any of the object properties are not an array', () => { + it('should return false if any of the object properties do not satisfy the validator', () => { expect( validator({ arrayProp: [1, 2], - nonArrayProp: 3, - }), - ).toBeFalsy(); - }); - - it('should return false if any of the object properties array values do not pass the test', () => { - expect( - validator({ - arrayProp: [1, 2], - nonArrayProp: [0, 3], + nonArrayProp: 0, }), ).toBeFalsy(); }); @@ -32,29 +23,32 @@ describe('validateObjectWithArrayProps', () => { expect( validator({ arrayProp: [1, 2], - nonArrayProp: [3, 4], + nonArrayProp: 3, }), ).toBeTruthy(); }); }); describe('returned validator with array', () => { - const validator = validateObjectWithArrayProps([(x) => x > 0, (x) => x < 0]); + const validator = validateObjectWithArrayProps( + (x) => x > 0, + (x) => x < 0, + ); it('should accept an array of validators, any of which can return true', () => { expect( validator({ - arrayProp: [1, 2], - nonArrayProp: [-1, 3], + arrayProp: [1, -1], + nonArrayProp: 3, }), ).toBeTruthy(); }); - it('should be false if none of the validators are true for any value', () => { + it('should return false if none of the validators are true for any value', () => { expect( validator({ - arrayProp: [1, 2], - nonArrayProp: [-1, 0], + arrayProp: [1, -1], + nonArrayProp: 0, }), ).toBeFalsy(); }); diff --git a/lib/utils/flattenArray.js b/lib/utils/flattenArray.js new file mode 100644 index 0000000000..07b602d4bb --- /dev/null +++ b/lib/utils/flattenArray.js @@ -0,0 +1,16 @@ +'use strict'; + +/** + * Convert the specified value to an array. If an array is specified, the array is returned as-is. + * + * @template T + * @param {T | T[] | undefined | null} value + * @returns {T[] | undefined} + */ +module.exports = function flattenArray(value) { + if (value == null) { + return; + } + + return Array.isArray(value) ? value : [value]; +}; diff --git a/lib/utils/validateObjectWithArrayProps.js b/lib/utils/validateObjectWithArrayProps.js index 66894fc8c6..f1eb71cd10 100644 --- a/lib/utils/validateObjectWithArrayProps.js +++ b/lib/utils/validateObjectWithArrayProps.js @@ -3,41 +3,29 @@ const { isPlainObject } = require('./validateTypes'); /** - * Check whether the variable is an object and all its properties are arrays of values + * Check whether the variable is an object and all its properties are one or more values * that satisfy the specified validator(s): * * @example * ignoreProperties = { * value1: ["item11", "item12", "item13"], - * value2: ["item21", "item22", "item23"], - * value3: ["item31", "item32", "item33"], + * value2: "item2", * }; * validateObjectWithArrayProps(isString)(ignoreProperties); * //=> true * - * @template {(value: unknown) => boolean} Validator - * @param {Validator | Validator[]} validator - * @returns {(value: unknown) => boolean} + * @typedef {(value: unknown) => boolean} Validator + * @param {...Validator} validators + * @returns {Validator} */ -module.exports = function validateObjectWithArrayProps(validator) { +module.exports = function validateObjectWithArrayProps(...validators) { return (value) => { if (!isPlainObject(value)) { return false; } - return Object.values(value).every((array) => { - if (!Array.isArray(array)) { - return false; - } - - // Make sure the array items are strings - return array.every((item) => { - if (Array.isArray(validator)) { - return validator.some((v) => v(item)); - } - - return validator(item); - }); - }); + return Object.values(value) + .flat() + .every((item) => validators.some((v) => v(item))); }; };