/** * @author Toru Nagashima */ 'use strict' /** * @typedef {import('eslint').Linter.LintMessage} LintMessage */ /** * @typedef {object} GroupState * @property {Set} GroupState.disableAllKeys * @property {Map} GroupState.disableRuleKeys */ module.exports = { /** @param {string} code */ preprocess(code) { return [code] }, /** * @param {LintMessage[][]} messages * @returns {LintMessage[]} */ postprocess(messages) { const state = { /** @type {GroupState} */ block: { disableAllKeys: new Set(), disableRuleKeys: new Map() }, /** @type {GroupState} */ line: { disableAllKeys: new Set(), disableRuleKeys: new Map() } } /** @type {string[]} */ const usedDisableDirectiveKeys = [] /** @type {Map} */ const unusedDisableDirectiveReports = new Map() // Filter messages which are in disabled area. const filteredMessages = messages[0].filter((message) => { if (message.ruleId === 'vue/comment-directive') { const directiveType = message.messageId const data = message.message.split(' ') switch (directiveType) { case 'disableBlock': state.block.disableAllKeys.add(data[1]) break case 'disableLine': state.line.disableAllKeys.add(data[1]) break case 'enableBlock': state.block.disableAllKeys.clear() break case 'enableLine': state.line.disableAllKeys.clear() break case 'disableBlockRule': addDisableRule(state.block.disableRuleKeys, data[1], data[2]) break case 'disableLineRule': addDisableRule(state.line.disableRuleKeys, data[1], data[2]) break case 'enableBlockRule': state.block.disableRuleKeys.delete(data[1]) break case 'enableLineRule': state.line.disableRuleKeys.delete(data[1]) break case 'clear': state.block.disableAllKeys.clear() state.block.disableRuleKeys.clear() state.line.disableAllKeys.clear() state.line.disableRuleKeys.clear() break default: // unused eslint-disable comments report unusedDisableDirectiveReports.set(messageToKey(message), message) break } return false } else { const disableDirectiveKeys = [] if (state.block.disableAllKeys.size > 0) { disableDirectiveKeys.push(...state.block.disableAllKeys) } if (state.line.disableAllKeys.size > 0) { disableDirectiveKeys.push(...state.line.disableAllKeys) } if (message.ruleId) { const block = state.block.disableRuleKeys.get(message.ruleId) if (block) { disableDirectiveKeys.push(...block) } const line = state.line.disableRuleKeys.get(message.ruleId) if (line) { disableDirectiveKeys.push(...line) } } if (disableDirectiveKeys.length > 0) { // Store used eslint-disable comment key usedDisableDirectiveKeys.push(...disableDirectiveKeys) return false } else { return true } } }) if (unusedDisableDirectiveReports.size > 0) { for (const key of usedDisableDirectiveKeys) { // Remove used eslint-disable comments unusedDisableDirectiveReports.delete(key) } // Reports unused eslint-disable comments filteredMessages.push(...unusedDisableDirectiveReports.values()) filteredMessages.sort(compareLocations) } return filteredMessages }, supportsAutofix: true } /** * @param {Map} disableRuleKeys * @param {string} rule * @param {string} key */ function addDisableRule(disableRuleKeys, rule, key) { let keys = disableRuleKeys.get(rule) if (keys) { keys.push(key) } else { keys = [key] disableRuleKeys.set(rule, keys) } } /** * @param {LintMessage} message * @returns {string} message key */ function messageToKey(message) { return `line:${message.line},column${ // -1 because +1 by ESLint's `report-translator`. message.column - 1 }` } /** * Compares the locations of two objects in a source file * @param {Position} itemA The first object * @param {Position} itemB The second object * @returns {number} A value less than 1 if itemA appears before itemB in the source file, greater than 1 if * itemA appears after itemB in the source file, or 0 if itemA and itemB have the same location. */ function compareLocations(itemA, itemB) { return itemA.line - itemB.line || itemA.column - itemB.column }