-
Notifications
You must be signed in to change notification settings - Fork 92
/
index.js
127 lines (109 loc) · 3.18 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import { isRegExp, isString } from "lodash";
import { rules, utils } from "stylelint";
import valueParser from "postcss-value-parser";
import { namespace, ruleUrl, ALL_FUNCTIONS } from "../../utils";
const ruleToCheckAgainst = "function-no-unknown";
export const ruleName = namespace(ruleToCheckAgainst);
export const messages = utils.ruleMessages(ruleName, {
rejected: (...args) => {
return rules[ruleToCheckAgainst].messages
.rejected(...args)
.replace(` (${ruleToCheckAgainst})`, "");
}
});
export const meta = {
url: ruleUrl(ruleName)
};
function isNamespacedFunction(fn) {
const namespacedFunc = /^\w+\.\w+$/;
return namespacedFunc.test(fn);
}
function isAtUseAsSyntax(nodes) {
const [first, second, third] = nodes.slice(-3);
return (
first.type === "word" &&
first.value === "as" &&
second.type === "space" &&
third.type === "word"
);
}
function getAtUseNamespace(nodes) {
if (isAtUseAsSyntax(nodes)) {
const [last] = nodes.slice(-1);
return last.value;
}
const [first] = nodes;
const parts = first.value.split("/");
const [last] = parts.slice(-1);
return last;
}
export default function rule(primaryOption, secondaryOptions) {
return (root, result) => {
const validOptions = utils.validateOptions(
result,
ruleName,
{
actual: primaryOption
},
{
actual: secondaryOptions,
possible: {
ignoreFunctions: [isString, isRegExp]
},
optional: true
}
);
if (!validOptions) {
return;
}
const optionsFunctions =
(secondaryOptions && secondaryOptions.ignoreFunctions) || [];
const ignoreFunctions = ALL_FUNCTIONS.concat(optionsFunctions);
const ignoreFunctionsAsSet = new Set(ignoreFunctions);
const newSecondaryOptions = Object.assign({}, secondaryOptions, {
ignoreFunctions
});
utils.checkAgainstRule(
{
ruleName: ruleToCheckAgainst,
ruleSettings: [primaryOption, newSecondaryOptions],
root
},
warning => {
const { node, index } = warning;
// NOTE: Using `valueParser` is necessary for extracting a function name. This may be a performance waste.
valueParser(node.value).walk(valueNode => {
const { type, value: funcName } = valueNode;
if (type !== "function") {
return;
}
if (isNamespacedFunction(funcName)) {
const atUseNamespaces = [];
root.walkAtRules(/^use$/i, atRule => {
const { nodes } = valueParser(atRule.params);
atUseNamespaces.push(getAtUseNamespace(nodes));
});
if (atUseNamespaces.length) {
const [namespace] = funcName.split(".");
if (atUseNamespaces.includes(namespace)) {
return;
}
}
}
if (!ignoreFunctionsAsSet.has(funcName)) {
utils.report({
message: messages.rejected(funcName),
ruleName,
result,
node,
index
});
}
});
}
);
};
}
rule.ruleName = ruleName;
rule.messages = messages;
rule.meta = meta;