Skip to content

Commit

Permalink
Add function-blacklist rule (stylelint-scss#422)
Browse files Browse the repository at this point in the history
  • Loading branch information
pamelalozano16 committed Aug 3, 2023
1 parent 3684e7d commit 3c559d5
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,7 @@
# 5.0.2

- Added: `function-blacklist` rule support to ban specific built-in functions (#422).

# 5.0.1

- Fixed: `function-no-unknown` ignore empty function names (#826).
Expand Down
60 changes: 60 additions & 0 deletions src/rules/function-blacklist/README.md
@@ -0,0 +1,60 @@
# function-blacklist

Specify a list of blacklisted functions.

<!-- prettier-ignore -->
```css
a { margin-left: math.random(100); }
/** ↑
* This function */
```

## Options

`array|string|regex`: `["array", "of", "unprefixed", /functions/, "regex"]|"function"|"/regex/"|/regex/`

If a string is surrounded with `"/"` (e.g. `"/^rgb/"`), it is interpreted as a regular expression.

Given:

```json
["math.random", "double"]
```

The following patterns are considered warnings:

<!-- prettier-ignore -->
```scss
a { margin-left: math.random(100); }
```

<!-- prettier-ignore -->
```scss
@function double($num) {@return $num * 2;}
a {
margin-left: double($num);
}
```

The following patterns are _not_ considered warnings:

<!-- prettier-ignore -->
```scss
a { margin-left: math.abs(100); }
```

<!-- prettier-ignore -->
```scss
@function timesTwo($num) {@return $num * 2;}
a {
margin-left: timesTwo($num);
}
```

<!-- prettier-ignore -->
```scss
@function random($num) {@return $num;}
a {
margin-left: random($num);
}
```
78 changes: 78 additions & 0 deletions src/rules/function-blacklist/__tests__/index.js
@@ -0,0 +1,78 @@
"use strict";

const { messages, ruleName } = require("..");

// Testing single value
testRule({
ruleName,
config: ["math.random"],
customSyntax: "postcss-scss",

accept: [
{
code: `
@use "sass:math"
.a { margin-left: math.round(100.5); }
`,
description: "Math library function, not blacklisted."
}
],
reject: [
{
code: `
@use "sass:math"
.a { margin-left: math.random(100); }
`,
line: 3,
column: 18,
message: messages.rejected("math.random"),
description: "Math library function, not blacklisted."
}
]
});

// Testing an array
testRule({
ruleName,
config: [["math.random", "test"]],
customSyntax: "postcss-scss",

accept: [
{
code: `
@use "sass:math"
.a { margin-left: math.round(100.5); }
`,
description: "Math library function, not blacklisted."
},
{
code: `
@function funcName($num){@return $num;}
.a { margin-left: funcName(3); }
`,
description: "Declared function is not blacklisted."
}
],
reject: [
{
code: `
@use "sass:math"
.a { margin-left: math.random(100); }
`,
line: 3,
column: 16,
message: messages.rejected("math.random"),
description: "Math library function, not blacklisted."
},
{
code: `
@function test($num){@return $num;}
.a { margin-left: test(3); }
`,
line: 3,
column: 14,
message: messages.rejected("test"),
description: "Declared function is blacklisted."
}
]
});
62 changes: 62 additions & 0 deletions src/rules/function-blacklist/index.js
@@ -0,0 +1,62 @@
"use strict";

const { utils } = require("stylelint");
const { isRegExp, isString } = require("../../utils/validateTypes");
const namespace = require("../../utils/namespace");
const ruleUrl = require("../../utils/ruleUrl");
const valueParser = require("postcss-value-parser");
const isNativeCssFunction = require("../../utils/isNativeCssFunction");

const ruleName = namespace("function-blacklist");

const messages = utils.ruleMessages(ruleName, {
rejected: func => `Unexpected function "${func}"`
});

const meta = {
url: ruleUrl(ruleName)
};

function rule(blacklistOption) {
const blacklist = [].concat(blacklistOption);

return (root, result) => {
const validOptions = utils.validateOptions(result, ruleName, {
actual: blacklistOption,
possible: [isString, isRegExp]
});

if (!validOptions) {
return;
}

root.walkDecls(decl => {
valueParser(decl.value).walk(node => {
if (
node.type !== "function" ||
isNativeCssFunction(node.value) ||
node.value === ""
) {
return;
}
blacklist.forEach(functionName => {
if (node.value === functionName) {
utils.report({
message: messages.rejected(node.value),
node: decl,
word: decl.name,
result,
ruleName
});
}
});
});
});
};
}

rule.ruleName = ruleName;
rule.messages = messages;
rule.meta = meta;

module.exports = rule;
1 change: 1 addition & 0 deletions src/rules/index.js
Expand Up @@ -42,6 +42,7 @@ const rules = {
"double-slash-comment-empty-line-before": require("./double-slash-comment-empty-line-before"),
"double-slash-comment-inline": require("./double-slash-comment-inline"),
"double-slash-comment-whitespace-inside": require("./double-slash-comment-whitespace-inside"),
"function-blacklist": require("./function-blacklist"),
"function-color-relative": require("./function-color-relative"),
"function-no-unknown": require("./function-no-unknown"),
"function-quote-no-quoted-strings-inside": require("./function-quote-no-quoted-strings-inside"),
Expand Down

0 comments on commit 3c559d5

Please sign in to comment.