diff --git a/lib/rules/property-no-unknown/README.md b/lib/rules/property-no-unknown/README.md index 61a30ee911..f5f82d4e40 100644 --- a/lib/rules/property-no-unknown/README.md +++ b/lib/rules/property-no-unknown/README.md @@ -127,6 +127,27 @@ The following patterns are _not_ considered violations: } ``` +### `ignoreAtRules: ["/regex/", /regex/, "string"]` + +Ignores properties nested within specified at-rules. + +Given: + +``` +["supports"] +``` + +The following patterns are _not_ considered violations: + + +```css +@supports (display: grid) { + a { + my-property: 1; + } +} +``` + ### `checkPrefixed: true | false` (default: `false`) If `true`, this rule will check vendor-prefixed properties. diff --git a/lib/rules/property-no-unknown/__tests__/index.js b/lib/rules/property-no-unknown/__tests__/index.js index 2861598661..9e901babe5 100644 --- a/lib/rules/property-no-unknown/__tests__/index.js +++ b/lib/rules/property-no-unknown/__tests__/index.js @@ -360,3 +360,53 @@ testRule({ }, ], }); + +testRule({ + ruleName, + config: [true, { ignoreAtRules: ['supports', /^my-/] }], + + accept: [ + { + code: '@supports (display: grid) { my-property: 1; }', + }, + { + code: '@my-at-rule { foo: 1; }', + }, + { + code: '@supports (display:grid) { @media (min-width: 10px) { foo: 1; } }', + }, + { + code: '@supports (display:grid) { @media (min-width: 10px) { a { foo: 1; } } }', + }, + { + code: '@my-other-at-rule { a { foo: 1; } }', + }, + ], + + reject: [ + { + code: '@media screen { a { foo: 1; } }', + message: messages.rejected('foo'), + line: 1, + column: 21, + }, + { + code: '@not-my-at-rule { foo: 1; }', + message: messages.rejected('foo'), + line: 1, + column: 19, + }, + { + code: 'a { foo: 1; }', + message: messages.rejected('foo'), + line: 1, + column: 5, + }, + { + code: '@not-my-at-rule foobar { foo: 1; }', + message: messages.rejected('foo'), + line: 1, + column: 26, + }, + ], +}); diff --git a/lib/rules/property-no-unknown/index.js b/lib/rules/property-no-unknown/index.js index ec9045042c..26a914b301 100644 --- a/lib/rules/property-no-unknown/index.js +++ b/lib/rules/property-no-unknown/index.js @@ -33,6 +33,7 @@ function rule(actual, options) { ignoreProperties: [_.isString, _.isRegExp], checkPrefixed: _.isBoolean, ignoreSelectors: [_.isString, _.isRegExp], + ignoreAtRules: [_.isString, _.isRegExp], }, optional: true, }, @@ -44,7 +45,9 @@ function rule(actual, options) { const shouldCheckPrefixed = _.get(options, 'checkPrefixed'); - root.walkDecls((decl) => { + root.walkDecls(checkStatement); + + function checkStatement(decl) { const prop = decl.prop; if (!isStandardSyntaxProperty(prop)) { @@ -73,6 +76,18 @@ function rule(actual, options) { return; } + let node = decl.parent; + + while (node && node.type !== 'root') { + const { type, name } = node; + + if (type === 'atrule' && optionsMatches(options, 'ignoreAtRules', name)) { + return; + } + + node = node.parent; + } + if (allValidProperties.has(prop.toLowerCase())) { return; } @@ -83,7 +98,7 @@ function rule(actual, options) { result, ruleName, }); - }); + } }; }