Skip to content

Commit

Permalink
Add regex support to ignoreValues for value-no-vendor-prefix (#7650)
Browse files Browse the repository at this point in the history
The previously non-documented partial regex support that performed the comparison on the unprefixed value has been scrapped in favor of a documented support which compares exactly.
  • Loading branch information
Mouvedia committed Apr 25, 2024
1 parent 8c23fce commit c4c1816
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 16 deletions.
5 changes: 5 additions & 0 deletions .changeset/cuddly-spies-impress.md
@@ -0,0 +1,5 @@
---
"stylelint": minor
---

Added: regex support to `ignoreValues` for `value-no-vendor-prefix`
23 changes: 19 additions & 4 deletions lib/rules/value-no-vendor-prefix/README.md
Expand Up @@ -55,22 +55,37 @@ a { background: linear-gradient(bottom, #000, #fff); }

## Optional secondary options

### `ignoreValues: ["string"]`
### `ignoreValues: ["/regex/", /regex/, "string"]`

Given:

```json
["grab", "max-content"]
["grab", "hangul", "/^-apple-/"]
```

The following patterns are _not_ considered problems:

<!-- prettier-ignore -->
```css
cursor: -webkit-grab;
a { cursor: -webkit-grab; }
```

<!-- prettier-ignore -->
```css
.foo { max-width: -moz-max-content; }
a { list-style-type: -moz-hangul; }
```

<!-- prettier-ignore -->
```css
a { list-style-type: -moz-hangul-consonant; }
```

<!-- prettier-ignore -->
```css
a { -webkit-appearance: -apple-pay-button; }
```

> [!WARNING]
> An _exact_ match comparison will be performed for non-regex strings in the next major version.
> If you want to keep the legacy behavior, please consider using a regex instead.
> E.g. `[/^(-webkit-|-moz-|-ms-)?inline-/]`.
15 changes: 11 additions & 4 deletions lib/rules/value-no-vendor-prefix/__tests__/index.mjs
Expand Up @@ -155,18 +155,25 @@ testRule({

testRule({
ruleName,
config: [true, { ignoreValues: ['grab', 'max-content', '/^linear-/'] }],
config: [
true,
{ ignoreValues: ['grab', 'hangul', '/^-webkit-linear-/', /^-moz-use-text-color$/] },
],
fix: true,

accept: [
{
code: 'cursor: -webkit-grab;',
code: 'a { cursor: -webkit-grab; }',
},
{
code: '.foo { max-width: -moz-max-content; }',
code: 'a { list-style-type: -moz-hangul-consonant; }',
},
{
code: '.foo { background: -webkit-linear-gradient(bottom, #000, #fff); }',
code: 'a { background: -webkit-linear-gradient(bottom, #000, #fff); }',
},
{
code: 'a { outline-color: -moz-use-text-color; }',
description: 'RegExp: exact match',
},
],
reject: [
Expand Down
30 changes: 26 additions & 4 deletions lib/rules/value-no-vendor-prefix/index.cjs
Expand Up @@ -7,13 +7,13 @@ const hasPrefix = require('../../utils/hasPrefix.cjs');
const isAutoprefixable = require('../../utils/isAutoprefixable.cjs');
const isStandardSyntaxDeclaration = require('../../utils/isStandardSyntaxDeclaration.cjs');
const isStandardSyntaxProperty = require('../../utils/isStandardSyntaxProperty.cjs');
const validateTypes = require('../../utils/validateTypes.cjs');
const optionsMatches = require('../../utils/optionsMatches.cjs');
const report = require('../../utils/report.cjs');
const ruleMessages = require('../../utils/ruleMessages.cjs');
const setDeclarationValue = require('../../utils/setDeclarationValue.cjs');
const validateOptions = require('../../utils/validateOptions.cjs');
const vendor = require('../../utils/vendor.cjs');
const validateTypes = require('../../utils/validateTypes.cjs');

const ruleName = 'value-no-vendor-prefix';

Expand All @@ -26,7 +26,8 @@ const meta = {
fixable: true,
};

/** @type {import('stylelint').Rule} */
/** @typedef { Array<string | RegExp> } IgnoreValues */
/** @type {import('stylelint').Rule<boolean, IgnoreValues>} */
const rule = (primary, secondaryOptions, context) => {
return (root, result) => {
const validOptions = validateOptions(
Expand All @@ -37,7 +38,7 @@ const rule = (primary, secondaryOptions, context) => {
optional: true,
actual: secondaryOptions,
possible: {
ignoreValues: [validateTypes.isString],
ignoreValues: [validateTypes.isString, validateTypes.isRegExp],
},
},
);
Expand All @@ -46,6 +47,19 @@ const rule = (primary, secondaryOptions, context) => {
return;
}

/**
* @type {{ raw: IgnoreValues, unprefixed: IgnoreValues }}
* @todo use Object.groupBy once Node v20 support is dropped
*/
const groups = { raw: [], unprefixed: [] };

secondaryOptions?.ignoreValues?.forEach((value) => {
const useRawValue = validateTypes.isRegExp(value) || (value.startsWith('/') && value.match(/\/i?$/));
const group = useRawValue ? 'raw' : 'unprefixed';

groups[group].push(value);
});

root.walkDecls((decl) => {
const { value } = decl;

Expand All @@ -55,7 +69,15 @@ const rule = (primary, secondaryOptions, context) => {
return;
}

if (optionsMatches(secondaryOptions, 'ignoreValues', vendor.unprefixed(value))) {
if (optionsMatches(groups, 'raw', value)) {
return;
}

/**
* @todo consolidate in the next major
* @see stylelint/stylelin#7542
*/
if (optionsMatches(groups, 'unprefixed', vendor.unprefixed(value))) {
return;
}

Expand Down
31 changes: 27 additions & 4 deletions lib/rules/value-no-vendor-prefix/index.mjs
Expand Up @@ -4,14 +4,15 @@ import hasPrefix from '../../utils/hasPrefix.mjs';
import isAutoprefixable from '../../utils/isAutoprefixable.mjs';
import isStandardSyntaxDeclaration from '../../utils/isStandardSyntaxDeclaration.mjs';
import isStandardSyntaxProperty from '../../utils/isStandardSyntaxProperty.mjs';
import { isString } from '../../utils/validateTypes.mjs';
import optionsMatches from '../../utils/optionsMatches.mjs';
import report from '../../utils/report.mjs';
import ruleMessages from '../../utils/ruleMessages.mjs';
import setDeclarationValue from '../../utils/setDeclarationValue.mjs';
import validateOptions from '../../utils/validateOptions.mjs';
import vendor from '../../utils/vendor.mjs';

import { isRegExp, isString } from '../../utils/validateTypes.mjs';

const ruleName = 'value-no-vendor-prefix';

const messages = ruleMessages(ruleName, {
Expand All @@ -23,7 +24,8 @@ const meta = {
fixable: true,
};

/** @type {import('stylelint').Rule} */
/** @typedef { Array<string | RegExp> } IgnoreValues */
/** @type {import('stylelint').Rule<boolean, IgnoreValues>} */
const rule = (primary, secondaryOptions, context) => {
return (root, result) => {
const validOptions = validateOptions(
Expand All @@ -34,7 +36,7 @@ const rule = (primary, secondaryOptions, context) => {
optional: true,
actual: secondaryOptions,
possible: {
ignoreValues: [isString],
ignoreValues: [isString, isRegExp],
},
},
);
Expand All @@ -43,6 +45,19 @@ const rule = (primary, secondaryOptions, context) => {
return;
}

/**
* @type {{ raw: IgnoreValues, unprefixed: IgnoreValues }}
* @todo use Object.groupBy once Node v20 support is dropped
*/
const groups = { raw: [], unprefixed: [] };

secondaryOptions?.ignoreValues?.forEach((value) => {
const useRawValue = isRegExp(value) || (value.startsWith('/') && value.match(/\/i?$/));
const group = useRawValue ? 'raw' : 'unprefixed';

groups[group].push(value);
});

root.walkDecls((decl) => {
const { value } = decl;

Expand All @@ -52,7 +67,15 @@ const rule = (primary, secondaryOptions, context) => {
return;
}

if (optionsMatches(secondaryOptions, 'ignoreValues', vendor.unprefixed(value))) {
if (optionsMatches(groups, 'raw', value)) {
return;
}

/**
* @todo consolidate in the next major
* @see stylelint/stylelin#7542
*/
if (optionsMatches(groups, 'unprefixed', vendor.unprefixed(value))) {
return;
}

Expand Down

0 comments on commit c4c1816

Please sign in to comment.