[d565449] | 1 | /**
|
---|
| 2 | * @fileoverview A module that filters reported problems based on `eslint-disable` and `eslint-enable` comments
|
---|
| 3 | * @author Teddy Katz
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | "use strict";
|
---|
| 7 |
|
---|
| 8 | //------------------------------------------------------------------------------
|
---|
| 9 | // Typedefs
|
---|
| 10 | //------------------------------------------------------------------------------
|
---|
| 11 |
|
---|
| 12 | /** @typedef {import("../shared/types").LintMessage} LintMessage */
|
---|
| 13 |
|
---|
| 14 | //------------------------------------------------------------------------------
|
---|
| 15 | // Module Definition
|
---|
| 16 | //------------------------------------------------------------------------------
|
---|
| 17 |
|
---|
| 18 | const escapeRegExp = require("escape-string-regexp");
|
---|
| 19 |
|
---|
| 20 | /**
|
---|
| 21 | * Compares the locations of two objects in a source file
|
---|
| 22 | * @param {{line: number, column: number}} itemA The first object
|
---|
| 23 | * @param {{line: number, column: number}} itemB The second object
|
---|
| 24 | * @returns {number} A value less than 1 if itemA appears before itemB in the source file, greater than 1 if
|
---|
| 25 | * itemA appears after itemB in the source file, or 0 if itemA and itemB have the same location.
|
---|
| 26 | */
|
---|
| 27 | function compareLocations(itemA, itemB) {
|
---|
| 28 | return itemA.line - itemB.line || itemA.column - itemB.column;
|
---|
| 29 | }
|
---|
| 30 |
|
---|
| 31 | /**
|
---|
| 32 | * Groups a set of directives into sub-arrays by their parent comment.
|
---|
| 33 | * @param {Iterable<Directive>} directives Unused directives to be removed.
|
---|
| 34 | * @returns {Directive[][]} Directives grouped by their parent comment.
|
---|
| 35 | */
|
---|
| 36 | function groupByParentComment(directives) {
|
---|
| 37 | const groups = new Map();
|
---|
| 38 |
|
---|
| 39 | for (const directive of directives) {
|
---|
| 40 | const { unprocessedDirective: { parentComment } } = directive;
|
---|
| 41 |
|
---|
| 42 | if (groups.has(parentComment)) {
|
---|
| 43 | groups.get(parentComment).push(directive);
|
---|
| 44 | } else {
|
---|
| 45 | groups.set(parentComment, [directive]);
|
---|
| 46 | }
|
---|
| 47 | }
|
---|
| 48 |
|
---|
| 49 | return [...groups.values()];
|
---|
| 50 | }
|
---|
| 51 |
|
---|
| 52 | /**
|
---|
| 53 | * Creates removal details for a set of directives within the same comment.
|
---|
| 54 | * @param {Directive[]} directives Unused directives to be removed.
|
---|
| 55 | * @param {Token} commentToken The backing Comment token.
|
---|
| 56 | * @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
|
---|
| 57 | */
|
---|
| 58 | function createIndividualDirectivesRemoval(directives, commentToken) {
|
---|
| 59 |
|
---|
| 60 | /*
|
---|
| 61 | * `commentToken.value` starts right after `//` or `/*`.
|
---|
| 62 | * All calculated offsets will be relative to this index.
|
---|
| 63 | */
|
---|
| 64 | const commentValueStart = commentToken.range[0] + "//".length;
|
---|
| 65 |
|
---|
| 66 | // Find where the list of rules starts. `\S+` matches with the directive name (e.g. `eslint-disable-line`)
|
---|
| 67 | const listStartOffset = /^\s*\S+\s+/u.exec(commentToken.value)[0].length;
|
---|
| 68 |
|
---|
| 69 | /*
|
---|
| 70 | * Get the list text without any surrounding whitespace. In order to preserve the original
|
---|
| 71 | * formatting, we don't want to change that whitespace.
|
---|
| 72 | *
|
---|
| 73 | * // eslint-disable-line rule-one , rule-two , rule-three -- comment
|
---|
| 74 | * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
---|
| 75 | */
|
---|
| 76 | const listText = commentToken.value
|
---|
| 77 | .slice(listStartOffset) // remove directive name and all whitespace before the list
|
---|
| 78 | .split(/\s-{2,}\s/u)[0] // remove `-- comment`, if it exists
|
---|
| 79 | .trimEnd(); // remove all whitespace after the list
|
---|
| 80 |
|
---|
| 81 | /*
|
---|
| 82 | * We can assume that `listText` contains multiple elements.
|
---|
| 83 | * Otherwise, this function wouldn't be called - if there is
|
---|
| 84 | * only one rule in the list, then the whole comment must be removed.
|
---|
| 85 | */
|
---|
| 86 |
|
---|
| 87 | return directives.map(directive => {
|
---|
| 88 | const { ruleId } = directive;
|
---|
| 89 |
|
---|
| 90 | const regex = new RegExp(String.raw`(?:^|\s*,\s*)(?<quote>['"]?)${escapeRegExp(ruleId)}\k<quote>(?:\s*,\s*|$)`, "u");
|
---|
| 91 | const match = regex.exec(listText);
|
---|
| 92 | const matchedText = match[0];
|
---|
| 93 | const matchStartOffset = listStartOffset + match.index;
|
---|
| 94 | const matchEndOffset = matchStartOffset + matchedText.length;
|
---|
| 95 |
|
---|
| 96 | const firstIndexOfComma = matchedText.indexOf(",");
|
---|
| 97 | const lastIndexOfComma = matchedText.lastIndexOf(",");
|
---|
| 98 |
|
---|
| 99 | let removalStartOffset, removalEndOffset;
|
---|
| 100 |
|
---|
| 101 | if (firstIndexOfComma !== lastIndexOfComma) {
|
---|
| 102 |
|
---|
| 103 | /*
|
---|
| 104 | * Since there are two commas, this must one of the elements in the middle of the list.
|
---|
| 105 | * Matched range starts where the previous rule name ends, and ends where the next rule name starts.
|
---|
| 106 | *
|
---|
| 107 | * // eslint-disable-line rule-one , rule-two , rule-three -- comment
|
---|
| 108 | * ^^^^^^^^^^^^^^
|
---|
| 109 | *
|
---|
| 110 | * We want to remove only the content between the two commas, and also one of the commas.
|
---|
| 111 | *
|
---|
| 112 | * // eslint-disable-line rule-one , rule-two , rule-three -- comment
|
---|
| 113 | * ^^^^^^^^^^^
|
---|
| 114 | */
|
---|
| 115 | removalStartOffset = matchStartOffset + firstIndexOfComma;
|
---|
| 116 | removalEndOffset = matchStartOffset + lastIndexOfComma;
|
---|
| 117 |
|
---|
| 118 | } else {
|
---|
| 119 |
|
---|
| 120 | /*
|
---|
| 121 | * This is either the first element or the last element.
|
---|
| 122 | *
|
---|
| 123 | * If this is the first element, matched range starts where the first rule name starts
|
---|
| 124 | * and ends where the second rule name starts. This is exactly the range we want
|
---|
| 125 | * to remove so that the second rule name will start where the first one was starting
|
---|
| 126 | * and thus preserve the original formatting.
|
---|
| 127 | *
|
---|
| 128 | * // eslint-disable-line rule-one , rule-two , rule-three -- comment
|
---|
| 129 | * ^^^^^^^^^^^
|
---|
| 130 | *
|
---|
| 131 | * Similarly, if this is the last element, we've already matched the range we want to
|
---|
| 132 | * remove. The previous rule name will end where the last one was ending, relative
|
---|
| 133 | * to the content on the right side.
|
---|
| 134 | *
|
---|
| 135 | * // eslint-disable-line rule-one , rule-two , rule-three -- comment
|
---|
| 136 | * ^^^^^^^^^^^^^
|
---|
| 137 | */
|
---|
| 138 | removalStartOffset = matchStartOffset;
|
---|
| 139 | removalEndOffset = matchEndOffset;
|
---|
| 140 | }
|
---|
| 141 |
|
---|
| 142 | return {
|
---|
| 143 | description: `'${ruleId}'`,
|
---|
| 144 | fix: {
|
---|
| 145 | range: [
|
---|
| 146 | commentValueStart + removalStartOffset,
|
---|
| 147 | commentValueStart + removalEndOffset
|
---|
| 148 | ],
|
---|
| 149 | text: ""
|
---|
| 150 | },
|
---|
| 151 | unprocessedDirective: directive.unprocessedDirective
|
---|
| 152 | };
|
---|
| 153 | });
|
---|
| 154 | }
|
---|
| 155 |
|
---|
| 156 | /**
|
---|
| 157 | * Creates a description of deleting an entire unused disable comment.
|
---|
| 158 | * @param {Directive[]} directives Unused directives to be removed.
|
---|
| 159 | * @param {Token} commentToken The backing Comment token.
|
---|
| 160 | * @returns {{ description, fix, unprocessedDirective }} Details for later creation of an output Problem.
|
---|
| 161 | */
|
---|
| 162 | function createCommentRemoval(directives, commentToken) {
|
---|
| 163 | const { range } = commentToken;
|
---|
| 164 | const ruleIds = directives.filter(directive => directive.ruleId).map(directive => `'${directive.ruleId}'`);
|
---|
| 165 |
|
---|
| 166 | return {
|
---|
| 167 | description: ruleIds.length <= 2
|
---|
| 168 | ? ruleIds.join(" or ")
|
---|
| 169 | : `${ruleIds.slice(0, ruleIds.length - 1).join(", ")}, or ${ruleIds[ruleIds.length - 1]}`,
|
---|
| 170 | fix: {
|
---|
| 171 | range,
|
---|
| 172 | text: " "
|
---|
| 173 | },
|
---|
| 174 | unprocessedDirective: directives[0].unprocessedDirective
|
---|
| 175 | };
|
---|
| 176 | }
|
---|
| 177 |
|
---|
| 178 | /**
|
---|
| 179 | * Parses details from directives to create output Problems.
|
---|
| 180 | * @param {Iterable<Directive>} allDirectives Unused directives to be removed.
|
---|
| 181 | * @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
|
---|
| 182 | */
|
---|
| 183 | function processUnusedDirectives(allDirectives) {
|
---|
| 184 | const directiveGroups = groupByParentComment(allDirectives);
|
---|
| 185 |
|
---|
| 186 | return directiveGroups.flatMap(
|
---|
| 187 | directives => {
|
---|
| 188 | const { parentComment } = directives[0].unprocessedDirective;
|
---|
| 189 | const remainingRuleIds = new Set(parentComment.ruleIds);
|
---|
| 190 |
|
---|
| 191 | for (const directive of directives) {
|
---|
| 192 | remainingRuleIds.delete(directive.ruleId);
|
---|
| 193 | }
|
---|
| 194 |
|
---|
| 195 | return remainingRuleIds.size
|
---|
| 196 | ? createIndividualDirectivesRemoval(directives, parentComment.commentToken)
|
---|
| 197 | : [createCommentRemoval(directives, parentComment.commentToken)];
|
---|
| 198 | }
|
---|
| 199 | );
|
---|
| 200 | }
|
---|
| 201 |
|
---|
| 202 | /**
|
---|
| 203 | * Collect eslint-enable comments that are removing suppressions by eslint-disable comments.
|
---|
| 204 | * @param {Directive[]} directives The directives to check.
|
---|
| 205 | * @returns {Set<Directive>} The used eslint-enable comments
|
---|
| 206 | */
|
---|
| 207 | function collectUsedEnableDirectives(directives) {
|
---|
| 208 |
|
---|
| 209 | /**
|
---|
| 210 | * A Map of `eslint-enable` keyed by ruleIds that may be marked as used.
|
---|
| 211 | * If `eslint-enable` does not have a ruleId, the key will be `null`.
|
---|
| 212 | * @type {Map<string|null, Directive>}
|
---|
| 213 | */
|
---|
| 214 | const enabledRules = new Map();
|
---|
| 215 |
|
---|
| 216 | /**
|
---|
| 217 | * A Set of `eslint-enable` marked as used.
|
---|
| 218 | * It is also the return value of `collectUsedEnableDirectives` function.
|
---|
| 219 | * @type {Set<Directive>}
|
---|
| 220 | */
|
---|
| 221 | const usedEnableDirectives = new Set();
|
---|
| 222 |
|
---|
| 223 | /*
|
---|
| 224 | * Checks the directives backwards to see if the encountered `eslint-enable` is used by the previous `eslint-disable`,
|
---|
| 225 | * and if so, stores the `eslint-enable` in `usedEnableDirectives`.
|
---|
| 226 | */
|
---|
| 227 | for (let index = directives.length - 1; index >= 0; index--) {
|
---|
| 228 | const directive = directives[index];
|
---|
| 229 |
|
---|
| 230 | if (directive.type === "disable") {
|
---|
| 231 | if (enabledRules.size === 0) {
|
---|
| 232 | continue;
|
---|
| 233 | }
|
---|
| 234 | if (directive.ruleId === null) {
|
---|
| 235 |
|
---|
| 236 | // If encounter `eslint-disable` without ruleId,
|
---|
| 237 | // mark all `eslint-enable` currently held in enabledRules as used.
|
---|
| 238 | // e.g.
|
---|
| 239 | // /* eslint-disable */ <- current directive
|
---|
| 240 | // /* eslint-enable rule-id1 */ <- used
|
---|
| 241 | // /* eslint-enable rule-id2 */ <- used
|
---|
| 242 | // /* eslint-enable */ <- used
|
---|
| 243 | for (const enableDirective of enabledRules.values()) {
|
---|
| 244 | usedEnableDirectives.add(enableDirective);
|
---|
| 245 | }
|
---|
| 246 | enabledRules.clear();
|
---|
| 247 | } else {
|
---|
| 248 | const enableDirective = enabledRules.get(directive.ruleId);
|
---|
| 249 |
|
---|
| 250 | if (enableDirective) {
|
---|
| 251 |
|
---|
| 252 | // If encounter `eslint-disable` with ruleId, and there is an `eslint-enable` with the same ruleId in enabledRules,
|
---|
| 253 | // mark `eslint-enable` with ruleId as used.
|
---|
| 254 | // e.g.
|
---|
| 255 | // /* eslint-disable rule-id */ <- current directive
|
---|
| 256 | // /* eslint-enable rule-id */ <- used
|
---|
| 257 | usedEnableDirectives.add(enableDirective);
|
---|
| 258 | } else {
|
---|
| 259 | const enabledDirectiveWithoutRuleId = enabledRules.get(null);
|
---|
| 260 |
|
---|
| 261 | if (enabledDirectiveWithoutRuleId) {
|
---|
| 262 |
|
---|
| 263 | // If encounter `eslint-disable` with ruleId, and there is no `eslint-enable` with the same ruleId in enabledRules,
|
---|
| 264 | // mark `eslint-enable` without ruleId as used.
|
---|
| 265 | // e.g.
|
---|
| 266 | // /* eslint-disable rule-id */ <- current directive
|
---|
| 267 | // /* eslint-enable */ <- used
|
---|
| 268 | usedEnableDirectives.add(enabledDirectiveWithoutRuleId);
|
---|
| 269 | }
|
---|
| 270 | }
|
---|
| 271 | }
|
---|
| 272 | } else if (directive.type === "enable") {
|
---|
| 273 | if (directive.ruleId === null) {
|
---|
| 274 |
|
---|
| 275 | // If encounter `eslint-enable` without ruleId, the `eslint-enable` that follows it are unused.
|
---|
| 276 | // So clear enabledRules.
|
---|
| 277 | // e.g.
|
---|
| 278 | // /* eslint-enable */ <- current directive
|
---|
| 279 | // /* eslint-enable rule-id *// <- unused
|
---|
| 280 | // /* eslint-enable */ <- unused
|
---|
| 281 | enabledRules.clear();
|
---|
| 282 | enabledRules.set(null, directive);
|
---|
| 283 | } else {
|
---|
| 284 | enabledRules.set(directive.ruleId, directive);
|
---|
| 285 | }
|
---|
| 286 | }
|
---|
| 287 | }
|
---|
| 288 | return usedEnableDirectives;
|
---|
| 289 | }
|
---|
| 290 |
|
---|
| 291 | /**
|
---|
| 292 | * This is the same as the exported function, except that it
|
---|
| 293 | * doesn't handle disable-line and disable-next-line directives, and it always reports unused
|
---|
| 294 | * disable directives.
|
---|
| 295 | * @param {Object} options options for applying directives. This is the same as the options
|
---|
| 296 | * for the exported function, except that `reportUnusedDisableDirectives` is not supported
|
---|
| 297 | * (this function always reports unused disable directives).
|
---|
| 298 | * @returns {{problems: LintMessage[], unusedDirectives: LintMessage[]}} An object with a list
|
---|
| 299 | * of problems (including suppressed ones) and unused eslint-disable directives
|
---|
| 300 | */
|
---|
| 301 | function applyDirectives(options) {
|
---|
| 302 | const problems = [];
|
---|
| 303 | const usedDisableDirectives = new Set();
|
---|
| 304 |
|
---|
| 305 | for (const problem of options.problems) {
|
---|
| 306 | let disableDirectivesForProblem = [];
|
---|
| 307 | let nextDirectiveIndex = 0;
|
---|
| 308 |
|
---|
| 309 | while (
|
---|
| 310 | nextDirectiveIndex < options.directives.length &&
|
---|
| 311 | compareLocations(options.directives[nextDirectiveIndex], problem) <= 0
|
---|
| 312 | ) {
|
---|
| 313 | const directive = options.directives[nextDirectiveIndex++];
|
---|
| 314 |
|
---|
| 315 | if (directive.ruleId === null || directive.ruleId === problem.ruleId) {
|
---|
| 316 | switch (directive.type) {
|
---|
| 317 | case "disable":
|
---|
| 318 | disableDirectivesForProblem.push(directive);
|
---|
| 319 | break;
|
---|
| 320 |
|
---|
| 321 | case "enable":
|
---|
| 322 | disableDirectivesForProblem = [];
|
---|
| 323 | break;
|
---|
| 324 |
|
---|
| 325 | // no default
|
---|
| 326 | }
|
---|
| 327 | }
|
---|
| 328 | }
|
---|
| 329 |
|
---|
| 330 | if (disableDirectivesForProblem.length > 0) {
|
---|
| 331 | const suppressions = disableDirectivesForProblem.map(directive => ({
|
---|
| 332 | kind: "directive",
|
---|
| 333 | justification: directive.unprocessedDirective.justification
|
---|
| 334 | }));
|
---|
| 335 |
|
---|
| 336 | if (problem.suppressions) {
|
---|
| 337 | problem.suppressions = problem.suppressions.concat(suppressions);
|
---|
| 338 | } else {
|
---|
| 339 | problem.suppressions = suppressions;
|
---|
| 340 | usedDisableDirectives.add(disableDirectivesForProblem[disableDirectivesForProblem.length - 1]);
|
---|
| 341 | }
|
---|
| 342 | }
|
---|
| 343 |
|
---|
| 344 | problems.push(problem);
|
---|
| 345 | }
|
---|
| 346 |
|
---|
| 347 | const unusedDisableDirectivesToReport = options.directives
|
---|
| 348 | .filter(directive => directive.type === "disable" && !usedDisableDirectives.has(directive));
|
---|
| 349 |
|
---|
| 350 |
|
---|
| 351 | const unusedEnableDirectivesToReport = new Set(
|
---|
| 352 | options.directives.filter(directive => directive.unprocessedDirective.type === "enable")
|
---|
| 353 | );
|
---|
| 354 |
|
---|
| 355 | /*
|
---|
| 356 | * If directives has the eslint-enable directive,
|
---|
| 357 | * check whether the eslint-enable comment is used.
|
---|
| 358 | */
|
---|
| 359 | if (unusedEnableDirectivesToReport.size > 0) {
|
---|
| 360 | for (const directive of collectUsedEnableDirectives(options.directives)) {
|
---|
| 361 | unusedEnableDirectivesToReport.delete(directive);
|
---|
| 362 | }
|
---|
| 363 | }
|
---|
| 364 |
|
---|
| 365 | const processed = processUnusedDirectives(unusedDisableDirectivesToReport)
|
---|
| 366 | .concat(processUnusedDirectives(unusedEnableDirectivesToReport));
|
---|
| 367 |
|
---|
| 368 | const unusedDirectives = processed
|
---|
| 369 | .map(({ description, fix, unprocessedDirective }) => {
|
---|
| 370 | const { parentComment, type, line, column } = unprocessedDirective;
|
---|
| 371 |
|
---|
| 372 | let message;
|
---|
| 373 |
|
---|
| 374 | if (type === "enable") {
|
---|
| 375 | message = description
|
---|
| 376 | ? `Unused eslint-enable directive (no matching eslint-disable directives were found for ${description}).`
|
---|
| 377 | : "Unused eslint-enable directive (no matching eslint-disable directives were found).";
|
---|
| 378 | } else {
|
---|
| 379 | message = description
|
---|
| 380 | ? `Unused eslint-disable directive (no problems were reported from ${description}).`
|
---|
| 381 | : "Unused eslint-disable directive (no problems were reported).";
|
---|
| 382 | }
|
---|
| 383 | return {
|
---|
| 384 | ruleId: null,
|
---|
| 385 | message,
|
---|
| 386 | line: type === "disable-next-line" ? parentComment.commentToken.loc.start.line : line,
|
---|
| 387 | column: type === "disable-next-line" ? parentComment.commentToken.loc.start.column + 1 : column,
|
---|
| 388 | severity: options.reportUnusedDisableDirectives === "warn" ? 1 : 2,
|
---|
| 389 | nodeType: null,
|
---|
| 390 | ...options.disableFixes ? {} : { fix }
|
---|
| 391 | };
|
---|
| 392 | });
|
---|
| 393 |
|
---|
| 394 | return { problems, unusedDirectives };
|
---|
| 395 | }
|
---|
| 396 |
|
---|
| 397 | /**
|
---|
| 398 | * Given a list of directive comments (i.e. metadata about eslint-disable and eslint-enable comments) and a list
|
---|
| 399 | * of reported problems, adds the suppression information to the problems.
|
---|
| 400 | * @param {Object} options Information about directives and problems
|
---|
| 401 | * @param {{
|
---|
| 402 | * type: ("disable"|"enable"|"disable-line"|"disable-next-line"),
|
---|
| 403 | * ruleId: (string|null),
|
---|
| 404 | * line: number,
|
---|
| 405 | * column: number,
|
---|
| 406 | * justification: string
|
---|
| 407 | * }} options.directives Directive comments found in the file, with one-based columns.
|
---|
| 408 | * Two directive comments can only have the same location if they also have the same type (e.g. a single eslint-disable
|
---|
| 409 | * comment for two different rules is represented as two directives).
|
---|
| 410 | * @param {{ruleId: (string|null), line: number, column: number}[]} options.problems
|
---|
| 411 | * A list of problems reported by rules, sorted by increasing location in the file, with one-based columns.
|
---|
| 412 | * @param {"off" | "warn" | "error"} options.reportUnusedDisableDirectives If `"warn"` or `"error"`, adds additional problems for unused directives
|
---|
| 413 | * @param {boolean} options.disableFixes If true, it doesn't make `fix` properties.
|
---|
| 414 | * @returns {{ruleId: (string|null), line: number, column: number, suppressions?: {kind: string, justification: string}}[]}
|
---|
| 415 | * An object with a list of reported problems, the suppressed of which contain the suppression information.
|
---|
| 416 | */
|
---|
| 417 | module.exports = ({ directives, disableFixes, problems, reportUnusedDisableDirectives = "off" }) => {
|
---|
| 418 | const blockDirectives = directives
|
---|
| 419 | .filter(directive => directive.type === "disable" || directive.type === "enable")
|
---|
| 420 | .map(directive => Object.assign({}, directive, { unprocessedDirective: directive }))
|
---|
| 421 | .sort(compareLocations);
|
---|
| 422 |
|
---|
| 423 | const lineDirectives = directives.flatMap(directive => {
|
---|
| 424 | switch (directive.type) {
|
---|
| 425 | case "disable":
|
---|
| 426 | case "enable":
|
---|
| 427 | return [];
|
---|
| 428 |
|
---|
| 429 | case "disable-line":
|
---|
| 430 | return [
|
---|
| 431 | { type: "disable", line: directive.line, column: 1, ruleId: directive.ruleId, unprocessedDirective: directive },
|
---|
| 432 | { type: "enable", line: directive.line + 1, column: 0, ruleId: directive.ruleId, unprocessedDirective: directive }
|
---|
| 433 | ];
|
---|
| 434 |
|
---|
| 435 | case "disable-next-line":
|
---|
| 436 | return [
|
---|
| 437 | { type: "disable", line: directive.line + 1, column: 1, ruleId: directive.ruleId, unprocessedDirective: directive },
|
---|
| 438 | { type: "enable", line: directive.line + 2, column: 0, ruleId: directive.ruleId, unprocessedDirective: directive }
|
---|
| 439 | ];
|
---|
| 440 |
|
---|
| 441 | default:
|
---|
| 442 | throw new TypeError(`Unrecognized directive type '${directive.type}'`);
|
---|
| 443 | }
|
---|
| 444 | }).sort(compareLocations);
|
---|
| 445 |
|
---|
| 446 | const blockDirectivesResult = applyDirectives({
|
---|
| 447 | problems,
|
---|
| 448 | directives: blockDirectives,
|
---|
| 449 | disableFixes,
|
---|
| 450 | reportUnusedDisableDirectives
|
---|
| 451 | });
|
---|
| 452 | const lineDirectivesResult = applyDirectives({
|
---|
| 453 | problems: blockDirectivesResult.problems,
|
---|
| 454 | directives: lineDirectives,
|
---|
| 455 | disableFixes,
|
---|
| 456 | reportUnusedDisableDirectives
|
---|
| 457 | });
|
---|
| 458 |
|
---|
| 459 | return reportUnusedDisableDirectives !== "off"
|
---|
| 460 | ? lineDirectivesResult.problems
|
---|
| 461 | .concat(blockDirectivesResult.unusedDirectives)
|
---|
| 462 | .concat(lineDirectivesResult.unusedDirectives)
|
---|
| 463 | .sort(compareLocations)
|
---|
| 464 | : lineDirectivesResult.problems;
|
---|
| 465 | };
|
---|