blob: f6c206e2682dfd6e85148c6caa824229c50343de [file] [log] [blame]
/* @flow */
"use strict";
const _ = require("lodash");
/*:: type rangeDataType = {
all: Array<Object>,
}
*/
/*:: type rangeType = {
unusedRule: string,
end?: number,
start: number,
used?: boolean,
}*/
/*:: type unusedRangeT = {
start: number,
end?: number,
}*/
module.exports = function(
results /*: Array<stylelint$result>*/
) /*: stylelint$needlessDisablesReport*/ {
const report = [];
results.forEach(result => {
// File with `CssSyntaxError` have not `_postcssResult`
if (!result._postcssResult) {
return;
}
const unused = { source: result.source, ranges: [] };
const rangeData /*: ?rangeDataType*/ = _.cloneDeep(
result._postcssResult.stylelint.disabledRanges
);
if (!rangeData) {
return;
}
result.warnings.forEach(warning => {
const rule /*: string*/ = warning.rule;
const ruleRanges /*: Array<Object>*/ = rangeData[rule];
if (ruleRanges) {
// Back to front so we get the *last* range that applies to the warning
for (const range of ruleRanges.reverse()) {
if (isWarningInRange(warning, range)) {
range.used = true;
return;
}
}
}
for (const range of rangeData.all.reverse()) {
if (isWarningInRange(warning, range)) {
range.used = true;
return;
}
}
});
Object.keys(rangeData).forEach(rule => {
rangeData[rule].forEach((range /*: rangeType*/) => {
// Is an equivalent range already marked as unused?
const alreadyMarkedUnused /*: ?unusedRangeT*/ = unused.ranges.find((
unusedRange /*: unusedRangeT*/
) => {
return (
unusedRange.start === range.start && unusedRange.end === range.end
);
});
// If this range is unused and no equivalent is marked,
// mark this range as unused
if (!range.used && !alreadyMarkedUnused) {
range.unusedRule = rule;
unused.ranges.push(range);
}
// If this range is used but an equivalent has been marked as unused,
// remove that equivalent. This can happen because of the duplication
// of ranges in rule-specific range sets and the "all" range set
if (range.used && alreadyMarkedUnused) {
_.remove(unused.ranges, alreadyMarkedUnused);
}
});
});
unused.ranges = _.sortBy(unused.ranges, ["start", "end"]);
report.push(unused);
});
return report;
};
function isWarningInRange(
warning /*: {
column: number,
rule: string,
line: number,
severity: string,
text: string,
}*/,
range /*: {
rules?: Array<string>,
start: number,
end?: number,
used?: boolean,
}*/
) /*: boolean*/ {
const rule = warning.rule;
const line = warning.line;
// Need to check if range.end exist, because line number type cannot be compared to undefined
return (
range.start <= line &&
((range.end !== undefined && range.end >= line) ||
range.end === undefined) &&
(!range.rules || range.rules.indexOf(rule) !== -1)
);
}