Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix function-no-unknown false positives for unspaced operators against nested brackets #6842

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

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

Fixed: `function-no-unknown` false positives for unspaced operators against nested brackets
3 changes: 3 additions & 0 deletions lib/rules/function-no-unknown/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ testRule({
{
code: 'a { background: -webkit-gradient(linear, 0 50%, 0 100%, from(white), color-stop(0.5, yellow), to(red)); }',
},
{
code: 'a { height: calc(10px*(5*(10 - 5))); }',
},
],

reject: [
Expand Down
51 changes: 29 additions & 22 deletions lib/rules/function-no-unknown/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
'use strict';

const fs = require('fs');
const valueParser = require('postcss-value-parser');
const functionsListPath = require('css-functions-list');
const { tokenize } = require('@csstools/css-tokenizer');
const {
parseCommaSeparatedListOfComponentValues,
isFunctionNode,
isSimpleBlockNode,
} = require('@csstools/css-parser-algorithms');

const declarationValueIndex = require('../../utils/declarationValueIndex');
const optionsMatches = require('../../utils/optionsMatches');
const report = require('../../utils/report');
const ruleMessages = require('../../utils/ruleMessages');
const validateOptions = require('../../utils/validateOptions');
const isStandardSyntaxFunction = require('../../utils/isStandardSyntaxFunction');
const isCustomFunction = require('../../utils/isCustomFunction');
const { isRegExp, isString } = require('../../utils/validateTypes');
const isStandardSyntaxValue = require('../../utils/isStandardSyntaxValue');
Expand Down Expand Up @@ -60,39 +64,42 @@ const rule = (primary, secondaryOptions) => {

if (!isStandardSyntaxValue(value)) return;

valueParser(value).walk((node) => {
const name = node.value;
/**
* @param {import('@csstools/css-parser-algorithms').ComponentValue} componentValue
*/
const walker = (componentValue) => {
if (!isFunctionNode(componentValue)) return;

if (node.type !== 'function') {
return;
}
const name = componentValue.getName();

if (!isStandardSyntaxFunction(node)) {
return;
}
Comment on lines -70 to -72
Copy link
Member

@mattxwang mattxwang May 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we've removed this check, is it possible that this rule will now flag errors for SASS/CSS-in-JS users? If so, I imagine that this might be slightly breaking for those users (and/or something we propagate to configs)?

(I could also be missing something about how SASS and CSS-in-JS play together with this rule, or with @csstools)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch :)

module.exports = function isStandardSyntaxFunction(node) {
	// Function nodes without names are things in parentheses like Sass lists
	if (!node.value) {
		return false;
	}

	if (node.value.startsWith('#{')) {
		return false;
	}

	// CSS-in-JS interpolation
	if (node.value.startsWith('${')) {
		return false;
	}

	// CSS-in-JS syntax
	if (node.value.startsWith('`')) {
		return false;
	}

	return true;
};

None of those cases would be parsed as a FunctionNode by @csstools/css-parser-algorithms.

Only <ident><open-parenthesis> (e.g. foo() starts a function in standard CSS.

This behavior makes isFunctionNode the equivalent of isStandardSyntaxFunction.

is it possible that this rule will now flag errors for SASS/CSS-in-JS users?

no

if (isCustomFunction(name)) return;

if (isCustomFunction(name)) {
return;
}
if (optionsMatches(secondaryOptions, 'ignoreFunctions', name)) return;

if (optionsMatches(secondaryOptions, 'ignoreFunctions', name)) {
return;
}

if (functionsList.includes(name.toLowerCase())) {
return;
}
if (functionsList.includes(name.toLowerCase())) return;

report({
message: messages.rejected,
messageArgs: [name],
node: decl,
index: declarationValueIndex(decl) + node.sourceIndex,
index: declarationValueIndex(decl) + componentValue.name[2],
result,
ruleName,
word: name,
});
});
};

parseCommaSeparatedListOfComponentValues(tokenize({ css: value }))
.flatMap((x) => x)
.forEach((componentValue) => {
if (isFunctionNode(componentValue) || isSimpleBlockNode(componentValue)) {
walker(componentValue);

componentValue.walk((entry) => {
walker(entry.node);
});
}
});
});
};
};
Expand Down