Skip to content

Commit

Permalink
Fix false positives for css-in-js in color-no-hex (#5186)
Browse files Browse the repository at this point in the history
* Use postcss-value-parser for color-no-hex rule

`style-search` returns false positives when used with `css-in-js` object
notation. This change continues with the discussion in #4826 and moves
another test away from using `style-search`.
  • Loading branch information
Dru89 committed Mar 8, 2021
1 parent d9dddf2 commit caaf2e5
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 18 deletions.
78 changes: 78 additions & 0 deletions lib/rules/color-no-hex/__tests__/index.js
Expand Up @@ -94,3 +94,81 @@ testRule({
},
],
});

testRule({
ruleName,
config: [true],
syntax: 'css-in-js',

accept: [
{
code: `
export const s = styled.a({
color: "pink",
});
`,
},
{
code: `
export const s = styled.a({
color: "rgba(0, 0, 0, 0)",
});
`,
},
{
code: `
export const s = styled.a({
content: '"#abcdef"',
});
`,
},
{
code: `
export const s = styled.a({
background: 'url(#aabbcc)',
});
`,
},
{
code: `
export const s = styled.a({
color: "red /* #ff0000 */",
});
`,
},
{
code: `
export const s = styled.a({
color: "linear-gradient(green, rgb(123, 123, 123), hsl(24, 70%, 80%))",
});
`,
},
],

reject: [
{
code: `
export const s = styled.a({
color: "#abcdef",
});
`,

message: messages.rejected('#abcdef'),
line: 3,
column: 13,
},
{
code: `
export const s = styled.a({
backgroundColor: "linear-gradient(#aaa, #ffff, #01234567)",
});
`,

warnings: [
{ message: messages.rejected('#aaa'), line: 3, column: 39 },
{ message: messages.rejected('#ffff'), line: 3, column: 45 },
{ message: messages.rejected('#01234567'), line: 3, column: 52 },
],
},
],
});
39 changes: 21 additions & 18 deletions lib/rules/color-no-hex/index.js
Expand Up @@ -2,9 +2,12 @@

'use strict';

const valueParser = require('postcss-value-parser');

const declarationValueIndex = require('../../utils/declarationValueIndex');
const getDeclarationValue = require('../../utils/getDeclarationValue');
const report = require('../../utils/report');
const ruleMessages = require('../../utils/ruleMessages');
const styleSearch = require('style-search');
const validateOptions = require('../../utils/validateOptions');

const ruleName = 'color-no-hex';
Expand All @@ -13,6 +16,9 @@ const messages = ruleMessages(ruleName, {
rejected: (hex) => `Unexpected hex color "${hex}"`,
});

const HEX = /^#[0-9A-Za-z]+/;
const IGNORED_FUNCTIONS = new Set(['url']);

function rule(actual) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual });
Expand All @@ -22,28 +28,17 @@ function rule(actual) {
}

root.walkDecls((decl) => {
const declString = decl.toString();

styleSearch({ source: declString, target: '#' }, (match) => {
// If there's not a colon, comma, or whitespace character before, we'll assume this is
// not intended to be a hex color, but is instead something like the
// hash in a url() argument
if (!/[:,\s]/.test(declString[match.startIndex - 1])) {
return;
}

const hexMatch = /^#[0-9A-Za-z]+/.exec(declString.substr(match.startIndex));
const parsedValue = valueParser(getDeclarationValue(decl));

if (!hexMatch) {
return;
}
parsedValue.walk((node) => {
if (isIgnoredFunction(node)) return false;

const hexValue = hexMatch[0];
if (!isHexColor(node)) return;

report({
message: messages.rejected(hexValue),
message: messages.rejected(node.value),
node: decl,
index: match.startIndex,
index: declarationValueIndex(decl) + node.sourceIndex,
result,
ruleName,
});
Expand All @@ -52,6 +47,14 @@ function rule(actual) {
};
}

function isIgnoredFunction({ type, value }) {
return type === 'function' && IGNORED_FUNCTIONS.has(value.toLowerCase());
}

function isHexColor({ type, value }) {
return type === 'word' && HEX.test(value);
}

rule.ruleName = ruleName;
rule.messages = messages;
module.exports = rule;

0 comments on commit caaf2e5

Please sign in to comment.