/
report.js
140 lines (115 loc) · 3.46 KB
/
report.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
128
129
130
131
132
133
134
135
136
137
138
139
140
'use strict';
/**
* Report a problem.
*
* This function accounts for `disabledRanges` attached to the result.
* That is, if the reported problem is within a disabledRange,
* it is ignored. Otherwise, it is attached to the result as a
* postcss warning.
*
* It also accounts for the rule's severity.
*
* You *must* pass *either* a node or a line number.
*
* @type {typeof import('stylelint').utils.report}
*/
module.exports = function report(problem) {
const { ruleName, result, message, messageArgs, line, node, index, endIndex, word } = problem;
result.stylelint = result.stylelint || {
ruleSeverities: {},
customMessages: {},
ruleMetadata: {},
};
// In quiet mode, mere warnings are ignored
if (result.stylelint.quiet && result.stylelint.ruleSeverities[ruleName] !== 'error') {
return;
}
const { start } = (node && node.rangeBy({ index, endIndex })) || {};
// If a line is not passed, use the node.rangeBy method to get the
// line number that the complaint pertains to
const startLine = line || (start && start.line);
if (!startLine) {
throw new Error('You must pass either a node or a line number');
}
const { ignoreDisables } = result.stylelint.config || {};
if (result.stylelint.disabledRanges) {
const ranges =
result.stylelint.disabledRanges[ruleName] || result.stylelint.disabledRanges.all || [];
for (const range of ranges) {
if (
// If the problem is within a disabledRange,
// and that disabledRange's rules include this one,
// do not register a warning
range.start <= startLine &&
(range.end === undefined || range.end >= startLine) &&
(!range.rules || range.rules.includes(ruleName))
) {
// Collect disabled warnings
// Used to report `needlessDisables` in subsequent processing.
const disabledWarnings =
result.stylelint.disabledWarnings || (result.stylelint.disabledWarnings = []);
disabledWarnings.push({
rule: ruleName,
line: startLine,
});
if (!ignoreDisables) {
return;
}
break;
}
}
}
const severity = result.stylelint.ruleSeverities && result.stylelint.ruleSeverities[ruleName];
if (!result.stylelint.stylelintError && severity === 'error') {
result.stylelint.stylelintError = true;
}
/** @type {import('stylelint').WarningOptions} */
const warningProperties = {
severity,
rule: ruleName,
};
if (node) {
warningProperties.node = node;
}
if (problem.start) {
warningProperties.start = problem.start;
} else if (index) {
warningProperties.index = index;
}
if (problem.end) {
warningProperties.end = problem.end;
} else if (endIndex) {
warningProperties.endIndex = endIndex;
}
if (word) {
warningProperties.word = word;
}
const { customMessages } = result.stylelint;
const warningMessage = buildWarningMessage(
(customMessages && customMessages[ruleName]) || message,
messageArgs,
);
result.warn(warningMessage, warningProperties);
};
/**
* @param {import('stylelint').RuleMessage} message
* @param {import('stylelint').Problem['messageArgs']} messageArgs
* @returns {string}
*/
function buildWarningMessage(message, messageArgs) {
const args = messageArgs || [];
if (typeof message === 'string') {
return printfLike(message, ...args);
}
return message(...args);
}
/**
* @param {string} format
* @param {Array<unknown>} args
* @returns {string}
*/
function printfLike(format, ...args) {
return args.reduce((/** @type {string} */ result, arg) => {
return result.replace(/%[ds]/, String(arg));
}, format);
}