diff --git a/.changeset/serious-pumas-march.md b/.changeset/serious-pumas-march.md new file mode 100644 index 0000000000..e87b97cb35 --- /dev/null +++ b/.changeset/serious-pumas-march.md @@ -0,0 +1,5 @@ +--- +"stylelint": patch +--- + +Fixed: `declaration-property-value-no-unknown` false negatives for nested declarations diff --git a/lib/reference/__tests__/atKeywords.test.mjs b/lib/reference/__tests__/atKeywords.test.mjs new file mode 100644 index 0000000000..3c86e7ef86 --- /dev/null +++ b/lib/reference/__tests__/atKeywords.test.mjs @@ -0,0 +1,9 @@ +import { atKeywords, nestingSupportedAtKeywords } from '../atKeywords.js'; + +describe('atKeywords', () => { + it('nestingSupportedAtKeywords is a subset of all atKeywords', () => { + for (const keyword of nestingSupportedAtKeywords) { + expect(atKeywords).toContain(keyword); + } + }); +}); diff --git a/lib/reference/atKeywords.js b/lib/reference/atKeywords.js index 5773d14ea1..6934f883a3 100644 --- a/lib/reference/atKeywords.js +++ b/lib/reference/atKeywords.js @@ -2,6 +2,9 @@ const uniteSets = require('../utils/uniteSets.js'); +// https://www.w3.org/TR/css-nesting-1/#conditionals +const nestingSupportedAtKeywords = new Set(['container', 'layer', 'media', 'scope', 'supports']); + // https://www.w3.org/TR/css-page-3/#syntax-page-selector const pageMarginAtKeywords = new Set([ 'top-left-corner', @@ -45,6 +48,7 @@ const atKeywords = uniteSets(pageMarginAtKeywords, [ 'page', 'property', 'scroll-timeline', + 'scope', 'styleset', 'stylistic', 'supports', @@ -54,4 +58,5 @@ const atKeywords = uniteSets(pageMarginAtKeywords, [ module.exports = { atKeywords, + nestingSupportedAtKeywords, }; diff --git a/lib/rules/declaration-property-value-no-unknown/__tests__/index.mjs b/lib/rules/declaration-property-value-no-unknown/__tests__/index.mjs index 83dd0d19ea..6aeaaa9de6 100644 --- a/lib/rules/declaration-property-value-no-unknown/__tests__/index.mjs +++ b/lib/rules/declaration-property-value-no-unknown/__tests__/index.mjs @@ -9,6 +9,9 @@ testRule({ { code: 'a { top: 0; }', }, + { + code: 'a { @media screen { top: 0; } }', + }, { code: 'a { margin: auto 10em; }', }, @@ -66,6 +69,14 @@ testRule({ endLine: 1, endColumn: 17, }, + { + code: 'a { @media screen { top: unknown; } }', + message: messages.rejected('top', 'unknown'), + line: 1, + column: 26, + endLine: 1, + endColumn: 33, + }, { code: 'a { top: red; }', message: messages.rejected('top', 'red'), diff --git a/lib/rules/declaration-property-value-no-unknown/index.js b/lib/rules/declaration-property-value-no-unknown/index.js index 46153e6725..136b6ee803 100644 --- a/lib/rules/declaration-property-value-no-unknown/index.js +++ b/lib/rules/declaration-property-value-no-unknown/index.js @@ -15,6 +15,7 @@ const isStandardSyntaxProperty = require('../../utils/isStandardSyntaxProperty') const isStandardSyntaxDeclaration = require('../../utils/isStandardSyntaxDeclaration'); const { isAtRule } = require('../../utils/typeGuards'); const { isRegExp, isString } = require('../../utils/validateTypes'); +const { nestingSupportedAtKeywords } = require('../../reference/atKeywords'); const ruleName = 'declaration-property-value-no-unknown'; @@ -111,7 +112,7 @@ const rule = (primary, secondaryOptions) => { } const { error } = - parent && isAtRule(parent) + parent && isAtRule(parent) && !nestingSupportedAtKeywords.has(parent.name.toLowerCase()) ? forkedLexer.matchAtruleDescriptor(parent.name, prop, cssTreeValueNode) : forkedLexer.matchProperty(prop, cssTreeValueNode);