/** * @author Toru Nagashima */ /* eslint-disable eslint-plugin/report-message-format */ 'use strict' const utils = require('../utils') /** * @typedef {object} RuleAndLocation * @property {string} RuleAndLocation.ruleId * @property {number} RuleAndLocation.index * @property {string} [RuleAndLocation.key] */ const COMMENT_DIRECTIVE_B = /^\s*(eslint-(?:en|dis)able)(?:\s+|$)/ const COMMENT_DIRECTIVE_L = /^\s*(eslint-disable(?:-next)?-line)(?:\s+|$)/ /** * Remove the ignored part from a given directive comment and trim it. * @param {string} value The comment text to strip. * @returns {string} The stripped text. */ function stripDirectiveComment(value) { return value.split(/\s-{2,}\s/u)[0] } /** * Parse a given comment. * @param {RegExp} pattern The RegExp pattern to parse. * @param {string} comment The comment value to parse. * @returns {({type:string,rules:RuleAndLocation[]})|null} The parsing result. */ function parse(pattern, comment) { const text = stripDirectiveComment(comment) const match = pattern.exec(text) if (match == null) { return null } const type = match[1] /** @type {RuleAndLocation[]} */ const rules = [] const rulesRe = /([^\s,]+)[\s,]*/g let startIndex = match[0].length rulesRe.lastIndex = startIndex let res while ((res = rulesRe.exec(text))) { const ruleId = res[1].trim() rules.push({ ruleId, index: startIndex }) startIndex = rulesRe.lastIndex } return { type, rules } } /** * Enable rules. * @param {RuleContext} context The rule context. * @param {{line:number,column:number}} loc The location information to enable. * @param { 'block' | 'line' } group The group to enable. * @param {string | null} rule The rule ID to enable. * @returns {void} */ function enable(context, loc, group, rule) { if (rule) { context.report({ loc, messageId: group === 'block' ? 'enableBlockRule' : 'enableLineRule', data: { rule } }) } else { context.report({ loc, messageId: group === 'block' ? 'enableBlock' : 'enableLine' }) } } /** * Disable rules. * @param {RuleContext} context The rule context. * @param {{line:number,column:number}} loc The location information to disable. * @param { 'block' | 'line' } group The group to disable. * @param {string | null} rule The rule ID to disable. * @param {string} key The disable directive key. * @returns {void} */ function disable(context, loc, group, rule, key) { if (rule) { context.report({ loc, messageId: group === 'block' ? 'disableBlockRule' : 'disableLineRule', data: { rule, key } }) } else { context.report({ loc, messageId: group === 'block' ? 'disableBlock' : 'disableLine', data: { key } }) } } /** * Process a given comment token. * If the comment is `eslint-disable` or `eslint-enable` then it reports the comment. * @param {RuleContext} context The rule context. * @param {Token} comment The comment token to process. * @param {boolean} reportUnusedDisableDirectives To report unused eslint-disable comments. * @returns {void} */ function processBlock(context, comment, reportUnusedDisableDirectives) { const parsed = parse(COMMENT_DIRECTIVE_B, comment.value) if (parsed === null) return if (parsed.type === 'eslint-disable') { if (parsed.rules.length > 0) { const rules = reportUnusedDisableDirectives ? reportUnusedRules(context, comment, parsed.type, parsed.rules) : parsed.rules for (const rule of rules) { disable( context, comment.loc.start, 'block', rule.ruleId, rule.key || '*' ) } } else { const key = reportUnusedDisableDirectives ? reportUnused(context, comment, parsed.type) : '' disable(context, comment.loc.start, 'block', null, key) } } else { if (parsed.rules.length > 0) { for (const rule of parsed.rules) { enable(context, comment.loc.start, 'block', rule.ruleId) } } else { enable(context, comment.loc.start, 'block', null) } } } /** * Process a given comment token. * If the comment is `eslint-disable-line` or `eslint-disable-next-line` then it reports the comment. * @param {RuleContext} context The rule context. * @param {Token} comment The comment token to process. * @param {boolean} reportUnusedDisableDirectives To report unused eslint-disable comments. * @returns {void} */ function processLine(context, comment, reportUnusedDisableDirectives) { const parsed = parse(COMMENT_DIRECTIVE_L, comment.value) if (parsed != null && comment.loc.start.line === comment.loc.end.line) { const line = comment.loc.start.line + (parsed.type === 'eslint-disable-line' ? 0 : 1) const column = -1 if (parsed.rules.length > 0) { const rules = reportUnusedDisableDirectives ? reportUnusedRules(context, comment, parsed.type, parsed.rules) : parsed.rules for (const rule of rules) { disable(context, { line, column }, 'line', rule.ruleId, rule.key || '') enable(context, { line: line + 1, column }, 'line', rule.ruleId) } } else { const key = reportUnusedDisableDirectives ? reportUnused(context, comment, parsed.type) : '' disable(context, { line, column }, 'line', null, key) enable(context, { line: line + 1, column }, 'line', null) } } } /** * Reports unused disable directive. * Do not check the use of directives here. Filter the directives used with postprocess. * @param {RuleContext} context The rule context. * @param {Token} comment The comment token to report. * @param {string} kind The comment directive kind. * @returns {string} The report key */ function reportUnused(context, comment, kind) { const loc = comment.loc context.report({ loc, messageId: 'unused', data: { kind } }) return locToKey(loc.start) } /** * Reports unused disable directive rules. * Do not check the use of directives here. Filter the directives used with postprocess. * @param {RuleContext} context The rule context. * @param {Token} comment The comment token to report. * @param {string} kind The comment directive kind. * @param {RuleAndLocation[]} rules To report rule. * @returns { { ruleId: string, key: string }[] } */ function reportUnusedRules(context, comment, kind, rules) { const sourceCode = context.getSourceCode() const commentStart = comment.range[0] + 4 /*