Skip to content
This repository was archived by the owner on Sep 30, 2025. It is now read-only.

Commit 128f147

Browse files
Create polaris/declaration-property-value-disallowed-list Stylelint rule (#7993)
1 parent 4b8553c commit 128f147

File tree

5 files changed

+126
-1
lines changed

5 files changed

+126
-1
lines changed

.changeset/stale-trainers-heal.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/stylelint-polaris': minor
3+
---
4+
5+
Created `polaris/declaration-property-value-disallowed-list` rule to ignore failures in `@font-face` at-rules

stylelint-polaris/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ const stylelintPolarisCoverageOptions = {
8888
],
8989
typography: [
9090
{
91-
'declaration-property-value-disallowed-list': {
91+
'polaris/declaration-property-value-disallowed-list': {
9292
'font-weight': [/(\$.*|[0-9]+)/],
9393
},
9494
'declaration-property-unit-disallowed-list': [
@@ -487,6 +487,7 @@ module.exports = {
487487
'./plugins/at-rule-disallowed-list',
488488
'./plugins/custom-property-allowed-list',
489489
'./plugins/media-query-allowed-list',
490+
'./plugins/declaration-property-value-disallowed-list',
490491
],
491492
rules: {
492493
'polaris/coverage': stylelintPolarisCoverageOptions,
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
const stylelint = require('stylelint');
2+
3+
const {
4+
isRegExp,
5+
isString,
6+
validateObjectWithArrayProps,
7+
} = require('../../utils');
8+
9+
const ruleName = 'polaris/declaration-property-value-disallowed-list';
10+
11+
/**
12+
* @typedef {{
13+
* [property: string]: string | RegExp | (string | RegExp)[]
14+
* }} PrimaryOptions
15+
*/
16+
17+
/**
18+
* Wrapper for the Stylelint `declaration-property-value-disallowed-list` rule
19+
* that ignores failures in `@font-face` at-rules.
20+
*/
21+
const {rule} = stylelint.createPlugin(
22+
ruleName,
23+
/** @param {PrimaryOptions} primary */
24+
(primary) => {
25+
return (root, result) => {
26+
const validOptions = stylelint.utils.validateOptions(result, ruleName, {
27+
actual: primary,
28+
possible: [validateObjectWithArrayProps(isString, isRegExp)],
29+
});
30+
31+
if (!validOptions) return;
32+
33+
stylelint.utils.checkAgainstRule(
34+
{
35+
ruleName: 'declaration-property-value-disallowed-list',
36+
ruleSettings: primary,
37+
root,
38+
},
39+
(warning) => {
40+
if (
41+
warning.node.type === 'decl' &&
42+
warning.node.parent.type === 'atrule' &&
43+
warning.node.parent.name === 'font-face'
44+
) {
45+
return;
46+
}
47+
48+
stylelint.utils.report({
49+
ruleName,
50+
result,
51+
node: warning.node,
52+
message: warning.text,
53+
});
54+
},
55+
);
56+
};
57+
},
58+
);
59+
60+
module.exports = {
61+
rule,
62+
ruleName,
63+
};
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const {ruleName} = require('.');
2+
3+
const config = {
4+
'font-weight': [/(\$.*|[0-9]+)/],
5+
};
6+
7+
testRule({
8+
ruleName,
9+
plugins: [__dirname],
10+
config,
11+
customSyntax: 'postcss-scss',
12+
accept: [
13+
{
14+
code: '@font-face { font-weight: 400; }',
15+
description: '@font-face descriptors are ignored',
16+
},
17+
],
18+
19+
reject: [
20+
{
21+
code: '.class { font-weight: 400; }',
22+
description: 'Not using a Polaris custom property',
23+
message:
24+
'Unexpected value "400" for property "font-weight" (declaration-property-value-disallowed-list)',
25+
},
26+
],
27+
});

stylelint-polaris/utils/index.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,34 @@ function isString(value) {
230230
return typeof value === 'string' || value instanceof String;
231231
}
232232

233+
/**
234+
* Check whether the variable is an object and all its properties are one or more values
235+
* that satisfy the specified validator(s):
236+
*
237+
* @example
238+
* ignoreProperties = {
239+
* value1: ["item11", "item12", "item13"],
240+
* value2: "item2",
241+
* };
242+
* validateObjectWithArrayProps(isString)(ignoreProperties);
243+
* //=> true
244+
*
245+
* @typedef {(value: unknown) => boolean} Validator
246+
* @param {...Validator} validators
247+
* @returns {Validator}
248+
*/
249+
function validateObjectWithArrayProps(...validators) {
250+
return (value) => {
251+
if (!isPlainObject(value)) {
252+
return false;
253+
}
254+
255+
return Object.values(value)
256+
.flat()
257+
.every((item) => validators.some((validator) => validator(item)));
258+
};
259+
}
260+
233261
/**
234262
* Returns the arguments expected by Stylelint rules that support functional custom messages
235263
* @param {string} ruleName The category's default message
@@ -259,6 +287,7 @@ module.exports.isPlainObject = isPlainObject;
259287
module.exports.isRegExp = isRegExp;
260288
module.exports.isScssInterpolation = isScssInterpolation;
261289
module.exports.isString = isString;
290+
module.exports.validateObjectWithArrayProps = validateObjectWithArrayProps;
262291
module.exports.matchesStringOrRegExp = matchesStringOrRegExp;
263292
module.exports.scssInterpolationExpression = scssInterpolationExpression;
264293
module.exports.scssInterpolationRegExp = scssInterpolationRegExp;

0 commit comments

Comments
 (0)