Skip to content

Commit

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

- Added: `function-disallowed-list` 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-disallowed-list/README.md
@@ -0,0 +1,60 @@
# function-disallowed-list

Specify a list of disallowed 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-disallowed-list/__tests__/index.js
@@ -0,0 +1,78 @@
"use strict";

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

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

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

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

accept: [
{
code: `
@use "sass:math"
.a { margin-left: math.round(100.5); }
`,
description: "Math library function, allowed."
},
{
code: `
@function funcName($num){@return $num;}
.a { margin-left: funcName(3); }
`,
description: "Declared function is allowed."
}
],
reject: [
{
code: `
@use "sass:math"
.a { margin-left: math.random(100); }
`,
line: 3,
column: 16,
message: messages.rejected("random"),
description: "Math library function, not allowed."
},
{
code: `
@function test($num){@return $num;}
.a { margin-left: test(3); }
`,
line: 3,
column: 14,
message: messages.rejected("test"),
description: "Declared function is not allowed (regex)."
}
]
});
69 changes: 69 additions & 0 deletions src/rules/function-disallowed-list/index.js
@@ -0,0 +1,69 @@
"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-disallowed-list");

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

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

function rule(disallowedOption) {
const disallowedFunctions = [].concat(disallowedOption);

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

if (!validOptions) {
return;
}

root.walkDecls(decl => {
valueParser(decl.value).walk(node => {
if (
node.type !== "function" ||
isNativeCssFunction(node.value) ||
node.value === ""
) {
return;
}

const hasNamespace = node.value.indexOf(".");
const nameWithoutNamespace =
hasNamespace > -1 ? node.value.slice(hasNamespace + 1) : node.value;
disallowedFunctions.forEach(functionName => {
if (
(isString(functionName) && nameWithoutNamespace === functionName) ||
(isRegExp(functionName) && nameWithoutNamespace.match(functionName))
) {
utils.report({
message: messages.rejected(nameWithoutNamespace),
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-disallowed-list": require("./function-disallowed-list"),
"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 4251f55

Please sign in to comment.