Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/eight-cars-switch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"stylelint": patch
---

Fixed: `*-no-unknown` false positives for latest specs by integrating `@csstools/css-syntax-patches-for-csstree`
19 changes: 13 additions & 6 deletions lib/lintPostcssResult.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 12 additions & 6 deletions lib/lintPostcssResult.mjs
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { EOL } from 'node:os';
import { createRequire } from 'node:module';

import { DEFAULT_SEVERITY, RULE_NAME_ALL } from './constants.mjs';
import { DEFAULT_CONFIGURATION_COMMENT } from './utils/configurationComment.mjs';
import assignDisabledRanges from './assignDisabledRanges.mjs';
import { emitDeprecationWarning } from './utils/emitWarning.mjs';
import { fork } from 'css-tree';
import getStylelintRule from './utils/getStylelintRule.mjs';
import mergeSyntaxDefinitions from './utils/mergeSyntaxDefinitions.mjs';
import reportUnknownRuleNames from './reportUnknownRuleNames.mjs';
import rules from './rules/index.mjs';
import timing from './timing.mjs';

/** @todo leverage import attributes once support for Node.js v18.19 is dropped */
const require = createRequire(import.meta.url);
const syntaxPatches = require('@csstools/css-syntax-patches-for-csstree/dist/index.json').next;

/** @import {Config, LinterOptions, PostcssResult} from 'stylelint' */

/**
Expand Down Expand Up @@ -171,12 +177,12 @@ function getCachedLexer(config) {
return lexerCache.get(cacheKey);
}

const newLexer = fork({
atrules: config.languageOptions?.syntax?.atRules || {},
properties: config.languageOptions?.syntax?.properties || {},
types: config.languageOptions?.syntax?.types || {},
cssWideKeywords: config.languageOptions?.syntax?.cssWideKeywords || [],
}).lexer;
const newLexer = fork(
mergeSyntaxDefinitions(syntaxPatches, {
...config.languageOptions?.syntax,
atrules: config.languageOptions?.syntax?.atRules,
}),
).lexer;

lexerCache.set(cacheKey, newLexer);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ testRule({
{
code: 'a { color: lightgray; }',
},
{
code: 'a { color: oklch(from red l c calc(h / 2) / alpha); }',
},
{
code: 'a { display: -moz-inline-stack; }',
},
Expand Down Expand Up @@ -186,6 +189,31 @@ testRule({
red;
}`,
},
{
code: 'a { height: calc-size(auto, size); }',
},
{
code: 'a { transition-timing-function: linear(0 0%, 0.999 44.8%, 0.866 58.4%, 0.998 77.2%, 1 100%) }',
},
{
code: 'a { container-type: scroll-state }',
},
{
code: 'a { inset: anchor(center) }',
},
{
code: stripIndent`a {
background: linear-gradient(in hsl longer hue, red, red);
background: radial-gradient(in hsl longer hue, red, red);
background: conic-gradient(in hsl longer hue, red, red);
background: repeating-linear-gradient(in hsl longer hue, red, red 50px);
background: repeating-radial-gradient(in hsl longer hue, red, red 50px);
background: repeating-conic-gradient(in hsl longer hue, red 0%, yellow 15%, red 33%);
}`,
},
{
code: 'a { appearance: base-select }',
},
],

reject: [
Expand Down Expand Up @@ -459,6 +487,24 @@ testRule({
endColumn: 31,
description: 'too few arguments in rgba()',
},
{
code: 'a { color: rgb(from red, r, g, b); }',
message: messages.rejected('color', 'rgb(from red, r, g, b)'),
line: 1,
column: 12,
endLine: 1,
endColumn: 34,
description: 'legacy syntax does not support relative colors',
},
{
code: 'a { color: rgb(from red l c h); }',
message: messages.rejected('color', 'rgb(from red l c h)'),
line: 1,
column: 12,
endLine: 1,
endColumn: 31,
description: 'component keywords are specific to each color function',
},
],
});

Expand Down
36 changes: 15 additions & 21 deletions lib/rules/declaration-property-value-no-unknown/index.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 14 additions & 21 deletions lib/rules/declaration-property-value-no-unknown/index.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { find, fork, parse, string } from 'css-tree';
import { createRequire } from 'node:module';

import { isRegExp, isString } from '../../utils/validateTypes.mjs';
import { atRuleRegexes } from '../../utils/regexes.mjs';
Expand All @@ -12,12 +13,17 @@ import isStandardSyntaxDeclaration from '../../utils/isStandardSyntaxDeclaration
import isStandardSyntaxProperty from '../../utils/isStandardSyntaxProperty.mjs';
import isStandardSyntaxValue from '../../utils/isStandardSyntaxValue.mjs';
import matchesStringOrRegExp from '../../utils/matchesStringOrRegExp.mjs';
import mergeSyntaxDefinitions from '../../utils/mergeSyntaxDefinitions.mjs';
import report from '../../utils/report.mjs';
import ruleMessages from '../../utils/ruleMessages.mjs';
import validateObjectWithArrayProps from '../../utils/validateObjectWithArrayProps.mjs';
import validateObjectWithProps from '../../utils/validateObjectWithProps.mjs';
import validateOptions from '../../utils/validateOptions.mjs';

/** @todo leverage import attributes once support for Node.js v18.19 is dropped */
const require = createRequire(import.meta.url);
const syntaxPatches = require('@csstools/css-syntax-patches-for-csstree/dist/index.json').next;

const ruleName = 'declaration-property-value-no-unknown';

const messages = ruleMessages(ruleName, {
Expand Down Expand Up @@ -67,21 +73,7 @@ const rule = (primary, secondaryOptions) => {
return Boolean(valuePattern && matchesStringOrRegExp(value, valuePattern));
};

/** @type {SecondaryOptions['propertiesSyntax']} */
const propertiesSyntax = {
'text-box-edge':
'auto | [ text | cap | ex | ideographic | ideographic-ink ] [ text | alphabetic | ideographic | ideographic-ink ]?',
'text-box-trim': 'none | trim-start | trim-end | trim-both',
'view-timeline':
"[ <'view-timeline-name'> [ <'view-timeline-axis'> || <'view-timeline-inset'> ]? ]#",
'font-size': '| math',
...secondaryOptions?.propertiesSyntax,
};

/**
* @todo add support for oklab(), oklch(), color(), color-mix(), light-dark(), etc.
* @see https://drafts.csswg.org/css-color-5/
*/
const propertiesSyntax = { ...secondaryOptions?.propertiesSyntax };
const typesSyntax = { ...secondaryOptions?.typesSyntax };

/** @type {Map<string, string>} */
Expand Down Expand Up @@ -121,12 +113,13 @@ const rule = (primary, secondaryOptions) => {
});

const languageOptions = result.stylelint.config?.languageOptions;
const forkedLexer = fork({
atrules: languageOptions?.syntax?.atRules || {},
properties: { ...(languageOptions?.syntax?.properties || {}), ...propertiesSyntax },
types: { ...(languageOptions?.syntax?.types || {}), ...typesSyntax },
cssWideKeywords: languageOptions?.syntax?.cssWideKeywords || [],
}).lexer;
const forkedLexer = fork(
mergeSyntaxDefinitions(
syntaxPatches,
{ ...languageOptions?.syntax, atrules: languageOptions?.syntax?.atRules },
{ properties: propertiesSyntax, types: typesSyntax },
),
).lexer;

root.walkDecls((decl) => {
const { prop } = decl;
Expand Down
Loading
Loading