Skip to content

Commit

Permalink
Fix function-calc-no-unspaced-operator false negatives
Browse files Browse the repository at this point in the history
This significantly changes the implementation by switching `postcss-value-parser` to `@csstools/css-parser-algorithms`.
Because `postcss-value-parser` is inactive and leaves bugs for a while,
and we've used already `@csstools/css-parser-algorithms` for some rules.

In addition, this updates `tsconfig.json` to support ES2023 libraries.
This change uses `Array.prototype.findLast()`, which has been supported since Node.js 18.0.0.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLast
  • Loading branch information
ybiquitous committed Apr 26, 2024
1 parent 68cb920 commit b8713a9
Show file tree
Hide file tree
Showing 7 changed files with 534 additions and 303 deletions.
5 changes: 5 additions & 0 deletions .changeset/happy-laws-attack.md
@@ -0,0 +1,5 @@
---
"stylelint": minor
---

Fixed: `function-calc-no-unspaced-operator` false negatives
4 changes: 4 additions & 0 deletions lib/reference/keywords.cjs
Expand Up @@ -479,9 +479,13 @@ const namedColorsKeywords = new Set([
'yellowgreen',
]);

/** @see https://drafts.csswg.org/css-values/#typedef-calc-keyword */
const calcKeywords = new Set(['e', 'pi', 'infinity', '-infinity', 'NaN']);

exports.animationNameKeywords = animationNameKeywords;
exports.animationShorthandKeywords = animationShorthandKeywords;
exports.basicKeywords = basicKeywords;
exports.calcKeywords = calcKeywords;
exports.camelCaseKeywords = camelCaseKeywords;
exports.counterIncrementKeywords = counterIncrementKeywords;
exports.counterResetKeywords = counterResetKeywords;
Expand Down
3 changes: 3 additions & 0 deletions lib/reference/keywords.mjs
Expand Up @@ -474,3 +474,6 @@ export const namedColorsKeywords = new Set([
'yellow',
'yellowgreen',
]);

/** @see https://drafts.csswg.org/css-values/#typedef-calc-keyword */
export const calcKeywords = new Set(['e', 'pi', 'infinity', '-infinity', 'NaN']);
119 changes: 71 additions & 48 deletions lib/rules/function-calc-no-unspaced-operator/__tests__/index.mjs
Expand Up @@ -83,23 +83,23 @@ testRule({
code: 'a { padding: calc(calc(1em * 2) / 3) calc(calc(1em * 2) / 3); }',
},
{
code: 'a { top: calc(+1px)}',
code: 'a { top: calc(+1px); }',
description: 'sign',
},
{
code: 'a { top: calc(1px + -1px)}',
code: 'a { top: calc(1px + -1px); }',
description: 'sign after operator',
},
{
code: 'a { top: calc(-1px * -1)}',
code: 'a { top: calc(-1px * -1); }',
description: 'sign after operator and at start',
},
{
code: 'a { top: calc( +1px)}',
code: 'a { top: calc( +1px); }',
description: 'multiple spaces before sign at start',
},
{
code: 'a { top: calc(\t+1px)}',
code: 'a { top: calc(\t+1px); }',
description: 'tab before sign at start',
},
{
Expand Down Expand Up @@ -163,35 +163,35 @@ testRule({
description: 'ignore function have calc in name',
},
{
code: 'margin-top: calc(var(--some-variable) +\n var(--some-other-variable));',
code: 'a { margin-top: calc(var(--some-variable) +\n var(--some-other-variable)); }',
description: 'newline and spaces after operator',
},
{
code: 'margin-top: calc(var(--some-variable) +\r\n var(--some-other-variable));',
code: 'a { margin-top: calc(var(--some-variable) +\r\n var(--some-other-variable)); }',
description: 'CRLF newline and spaces after operator',
},
{
code: 'margin-top: calc(var(--some-variable) +\n\tvar(--some-other-variable));',
code: 'a { margin-top: calc(var(--some-variable) +\n\tvar(--some-other-variable)); }',
description: 'newline and tab after operator',
},
{
code: 'margin-top: calc(var(--some-variable) +\r\n\tvar(--some-other-variable));',
code: 'a { margin-top: calc(var(--some-variable) +\r\n\tvar(--some-other-variable)); }',
description: 'CRLF newline and tab after operator',
},
{
code: 'margin-top: calc(var(--some-variable)\n + var(--some-other-variable));',
code: 'a { margin-top: calc(var(--some-variable)\n + var(--some-other-variable)); }',
description: 'newline and spaces before operator',
},
{
code: 'margin-top: calc(var(--some-variable)\r\n + var(--some-other-variable));',
code: 'a { margin-top: calc(var(--some-variable)\r\n + var(--some-other-variable)); }',
description: 'CRLF newline and spaces before operator',
},
{
code: 'margin-top: calc(var(--some-variable)\n\t+ var(--some-other-variable));',
code: 'a { margin-top: calc(var(--some-variable)\n\t+ var(--some-other-variable)); }',
description: 'newline and tab before operator',
},
{
code: 'margin-top: calc(var(--some-variable)\r\n\t+ var(--some-other-variable));',
code: 'a { margin-top: calc(var(--some-variable)\r\n\t+ var(--some-other-variable)); }',
description: 'CRLF newline and tab before operator',
},
{
Expand Down Expand Up @@ -237,24 +237,28 @@ testRule({
description:
'single argument: functions that accept more than one argument are not supported yet',
},
{
code: 'a { padding: calc(1px --2px); }',
description: '"--" prefixed token is considered as <ident-token>',
},
],

reject: [
{
code: 'a { top: calc(2px+1px) }',
fixed: 'a { top: calc(2px + 1px) }',
code: 'a { top: calc(2px+1px); }',
fixed: 'a { top: calc(2px + 1px); }',
description: 'no space before or after operator',
warnings: [
{ message: messages.expectedBefore('+'), line: 1, column: 18, endLine: 1, endColumn: 19 },
{ message: messages.expectedAfter('+'), line: 1, column: 19, endLine: 1, endColumn: 20 },
{ message: messages.expectedAfter('+'), line: 1, column: 18, endLine: 1, endColumn: 19 },
],
},
{
code: 'a { transform: rotate(acos(2+1)) }',
fixed: 'a { transform: rotate(acos(2 + 1)) }',
code: 'a { transform: rotate(acos(2+1)); }',
fixed: 'a { transform: rotate(acos(2 + 1)); }',
warnings: [
{ message: messages.expectedBefore('+'), line: 1, column: 29, endLine: 1, endColumn: 30 },
{ message: messages.expectedAfter('+'), line: 1, column: 30, endLine: 1, endColumn: 31 },
{ message: messages.expectedAfter('+'), line: 1, column: 29, endLine: 1, endColumn: 30 },
],
},
{
Expand All @@ -276,8 +280,8 @@ testRule({
skip: true,
},
{
code: 'a { top: calc(1px +\t-1px) }',
fixed: 'a { top: calc(1px + -1px) }',
code: 'a { top: calc(1px +\t-1px); }',
fixed: 'a { top: calc(1px + -1px); }',
description: 'tab before sign after operator',
message: messages.expectedAfter('+'),
line: 1,
Expand All @@ -286,8 +290,8 @@ testRule({
endColumn: 20,
},
{
code: 'a { top: calc(1px + -1px) }',
fixed: 'a { top: calc(1px + -1px) }',
code: 'a { top: calc(1px + -1px); }',
fixed: 'a { top: calc(1px + -1px); }',
description: 'multiple spaces before sign after operator',
message: messages.expectedAfter('+'),
line: 1,
Expand Down Expand Up @@ -370,7 +374,7 @@ testRule({
{
code: 'a { top: calc(1px +2px); }',
fixed: 'a { top: calc(1px + 2px); }',
message: messages.expectedOperatorBeforeSign('+'),
message: messages.expectedAfter('+'),
line: 1,
column: 19,
endLine: 1,
Expand All @@ -379,15 +383,15 @@ testRule({
{
code: 'a { top: calc(1px -2px); }',
fixed: 'a { top: calc(1px - 2px); }',
message: messages.expectedOperatorBeforeSign('-'),
message: messages.expectedAfter('-'),
line: 1,
column: 19,
endLine: 1,
endColumn: 20,
},
{
code: 'a { padding: 10px calc(1px +\t-1px)}',
fixed: 'a { padding: 10px calc(1px + -1px)}',
code: 'a { padding: 10px calc(1px +\t-1px); }',
fixed: 'a { padding: 10px calc(1px + -1px); }',
description: 'tab before sign after operator',
message: messages.expectedAfter('+'),
line: 1,
Expand All @@ -396,8 +400,8 @@ testRule({
endColumn: 29,
},
{
code: 'a { padding: 10px calc(1px + -1px)}',
fixed: 'a { padding: 10px calc(1px + -1px)}',
code: 'a { padding: 10px calc(1px + -1px); }',
fixed: 'a { padding: 10px calc(1px + -1px); }',
description: 'multiple spaces before sign after operator',
message: messages.expectedAfter('+'),
line: 1,
Expand Down Expand Up @@ -462,7 +466,7 @@ testRule({
{
code: 'a { padding: 10px calc(1px +2px); }',
fixed: 'a { padding: 10px calc(1px + 2px); }',
message: messages.expectedOperatorBeforeSign('+'),
message: messages.expectedAfter('+'),
line: 1,
column: 28,
endLine: 1,
Expand All @@ -471,15 +475,15 @@ testRule({
{
code: 'a { padding: 10px calc(1px -2px); }',
fixed: 'a { padding: 10px calc(1px - 2px); }',
message: messages.expectedOperatorBeforeSign('-'),
message: messages.expectedAfter('-'),
line: 1,
column: 28,
endLine: 1,
endColumn: 29,
},
{
code: 'a { padding: calc(1rem\t + 1em)}',
fixed: 'a { padding: calc(1rem + 1em)}',
code: 'a { padding: calc(1rem\t + 1em); }',
fixed: 'a { padding: calc(1rem + 1em); }',
description: 'several whitespace characters before operator starting from space',
message: messages.expectedBefore('+'),
line: 1,
Expand All @@ -488,8 +492,8 @@ testRule({
endColumn: 26,
},
{
code: 'a { padding: calc(1rem \t+ 1em)}',
fixed: 'a { padding: calc(1rem + 1em)}',
code: 'a { padding: calc(1rem \t+ 1em); }',
fixed: 'a { padding: calc(1rem + 1em); }',
description: 'several whitespace characters before operator starting from tab',
message: messages.expectedBefore('+'),
line: 1,
Expand All @@ -498,8 +502,8 @@ testRule({
endColumn: 27,
},
{
code: 'a { padding: calc(1rem\t\f\r\t+ 1em)}',
fixed: 'a { padding: calc(1rem + 1em)}',
code: 'a { padding: calc(1rem\t\f\r\t+ 1em); }',
fixed: 'a { padding: calc(1rem + 1em); }',
description: 'several incorrect whitespace characters before operator',
message: messages.expectedBefore('+'),
line: 1,
Expand All @@ -508,8 +512,8 @@ testRule({
endColumn: 28,
},
{
code: 'a { padding: calc(1rem + \t1em)}',
fixed: 'a { padding: calc(1rem + 1em)}',
code: 'a { padding: calc(1rem + \t1em); }',
fixed: 'a { padding: calc(1rem + 1em); }',
description: 'several whitespace characters after operator starting from space',
message: messages.expectedAfter('+'),
line: 1,
Expand All @@ -518,8 +522,8 @@ testRule({
endColumn: 25,
},
{
code: 'a { padding: calc(1rem +\t \t1em)}',
fixed: 'a { padding: calc(1rem + 1em)}',
code: 'a { padding: calc(1rem +\t \t1em); }',
fixed: 'a { padding: calc(1rem + 1em); }',
description: 'several whitespace characters after operator starting from tab',
message: messages.expectedAfter('+'),
line: 1,
Expand All @@ -528,8 +532,8 @@ testRule({
endColumn: 25,
},
{
code: 'a { padding: calc(1rem +\t\r\f\t1em)}',
fixed: 'a { padding: calc(1rem + 1em)}',
code: 'a { padding: calc(1rem +\t\r\f\t1em); }',
fixed: 'a { padding: calc(1rem + 1em); }',
description: 'several incorrect whitespace characters after operator',
message: messages.expectedAfter('+'),
line: 1,
Expand All @@ -538,8 +542,8 @@ testRule({
endColumn: 25,
},
{
code: 'a { padding: calc(1rem +\t \t\f\n\f\t1em)}',
fixed: 'a { padding: calc(1rem +\n\f\t1em)}',
code: 'a { padding: calc(1rem +\t \t\f\n\f\t1em); }',
fixed: 'a { padding: calc(1rem +\n\f\t1em); }',
description: 'several whitespace characters after operator but before the \\n',
message: messages.expectedAfter('+'),
line: 1,
Expand All @@ -548,15 +552,34 @@ testRule({
endColumn: 25,
},
{
code: 'a { padding: calc(1rem + \t\r\n 1em)}',
fixed: 'a { padding: calc(1rem +\r\n 1em)}',
code: 'a { padding: calc(1rem + \t\r\n 1em); }',
fixed: 'a { padding: calc(1rem +\r\n 1em); }',
description: 'several whitespace characters after operator but before the \\r\\n',
message: messages.expectedAfter('+'),
line: 1,
column: 24,
endLine: 1,
endColumn: 25,
},
{
code: 'a { padding: calc(1px +-2px); }',
fixed: 'a { padding: calc(1px + -2px); }',
message: messages.expectedAfter('+'),
line: 1,
column: 23,
endLine: 1,
endColumn: 24,
},
{
code: 'a { padding: calc(1px+2px-3px); }',
fixed: 'a { padding: calc(1px + 2px - 3px); }',
warnings: [
{ message: messages.expectedBefore('+'), line: 1, endLine: 1, column: 22, endColumn: 23 },
{ message: messages.expectedAfter('+'), line: 1, endLine: 1, column: 22, endColumn: 23 },
{ message: messages.expectedBefore('-'), line: 1, endLine: 1, column: 26, endColumn: 27 },
{ message: messages.expectedAfter('-'), line: 1, endLine: 1, column: 26, endColumn: 27 },
],
},
],
});

Expand Down Expand Up @@ -606,7 +629,7 @@ testRule({
{
code: 'a { top: calc(100% -#{$foo}); }',
fixed: 'a { top: calc(100% - #{$foo}); }',
message: messages.expectedOperatorBeforeSign('-'),
message: messages.expectedAfter('-'),
line: 1,
column: 20,
endLine: 1,
Expand Down

0 comments on commit b8713a9

Please sign in to comment.