From 25107bc09a0d3e0de72b58cc31487f33a4797a0f Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 30 Aug 2023 13:55:21 -0400 Subject: [PATCH 01/31] feat: `no-misleading-character-class` granular errors --- lib/rules/no-misleading-character-class.js | 117 ++-- .../rules/no-misleading-character-class.js | 531 +++++++++++++++--- 2 files changed, 520 insertions(+), 128 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 99aa9404868..90dc29ee76b 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -27,13 +27,13 @@ function *iterateCharacterSequence(nodes) { for (const node of nodes) { switch (node.type) { case "Character": - seq.push(node.value); + seq.push(node); break; case "CharacterClassRange": - seq.push(node.min.value); + seq.push(node.min); yield seq; - seq = [node.max.value]; + seq = [node.max]; break; case "CharacterSet": @@ -55,49 +55,65 @@ function *iterateCharacterSequence(nodes) { } } -const hasCharacterSequence = { - surrogatePairWithoutUFlag(chars) { - return chars.some((c, i) => i !== 0 && isSurrogatePair(chars[i - 1], c)); +const characterSequenceIndexFilters = { + surrogatePairWithoutUFlag(char, index, chars) { + return index !== 0 && isSurrogatePair(chars[index - 1].value, char.value); }, - combiningClass(chars) { - return chars.some((c, i) => ( - i !== 0 && - isCombiningCharacter(c) && - !isCombiningCharacter(chars[i - 1]) - )); + combiningClass(char, index, chars) { + return ( + index !== 0 && + isCombiningCharacter(char.value) && + !isCombiningCharacter(chars[index - 1].value) + ); }, - emojiModifier(chars) { - return chars.some((c, i) => ( - i !== 0 && - isEmojiModifier(c) && - !isEmojiModifier(chars[i - 1]) - )); + emojiModifier(char, index, chars) { + return ( + index !== 0 && + isEmojiModifier(char.value) && + !isEmojiModifier(chars[index - 1].value) + ); }, - regionalIndicatorSymbol(chars) { - return chars.some((c, i) => ( - i !== 0 && - isRegionalIndicatorSymbol(c) && - isRegionalIndicatorSymbol(chars[i - 1]) - )); + regionalIndicatorSymbol(char, index, chars) { + return ( + index !== 0 && + isRegionalIndicatorSymbol(char.value) && + isRegionalIndicatorSymbol(chars[index - 1].value) + ); }, - zwj(chars) { - const lastIndex = chars.length - 1; - - return chars.some((c, i) => ( - i !== 0 && - i !== lastIndex && - c === 0x200d && - chars[i - 1] !== 0x200d && - chars[i + 1] !== 0x200d - )); + zwj(char, index, chars) { + return ( + index !== 0 && + index !== chars.length - 1 && + char.value === 0x200d && + chars[index - 1].value !== 0x200d && + chars[index + 1].value !== 0x200d + ); } }; -const kinds = Object.keys(hasCharacterSequence); +const kinds = Object.keys(characterSequenceIndexFilters); + +/** + * Collects the indices where the filter returns true. + * @param {string[]} chars Characters to run the filter on. + * @param {(char: string) => boolean} filter Determines whether an index should be returned. + * @returns {number[]} Indices where the filter returned true. + */ +function accumulate(chars, filter) { + const matchingChars = []; + + chars.forEach((char, index) => { + if (filter(char, index, chars)) { + matchingChars.push(char); + } + }); + + return matchingChars; +} //------------------------------------------------------------------------------ // Rule Definition @@ -164,15 +180,17 @@ module.exports = { onCharacterClassEnter(ccNode) { for (const chars of iterateCharacterSequence(ccNode.elements)) { for (const kind of kinds) { - if (hasCharacterSequence[kind](chars)) { - foundKinds.add(kind); + const matchingChars = accumulate(chars, characterSequenceIndexFilters[kind]); + + if (matchingChars.length) { + foundKinds.add({ chars: matchingChars, kind }); } } } } }); - for (const kind of foundKinds) { + for (const { chars, kind } of foundKinds) { let suggest; if (kind === "surrogatePairWithoutUFlag") { @@ -182,11 +200,22 @@ module.exports = { }]; } - context.report({ - node, - messageId: kind, - suggest - }); + for (const char of chars) { + context.report({ + loc: { + end: { + line: node.loc.end.line, + column: node.loc.start.column + char.end + }, + start: { + line: node.loc.start.line, + column: node.loc.start.column + char.start + } + }, + messageId: kind, + suggest + }); + } } } @@ -217,7 +246,7 @@ module.exports = { const flags = getStringIfConstant(flagsNode, scope); if (typeof pattern === "string") { - verify(refNode, pattern, flags || "", fixer => { + verify(patternNode, pattern, flags || "", fixer => { if (!isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, pattern)) { return null; diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index 54c97149313..940f5620e0b 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -85,6 +85,9 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[👍]/", errors: [{ + column: 11, + endColumn: 12, + line: 1, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👍]/u" }] }] @@ -92,6 +95,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83D\\uDC4D]/", errors: [{ + column: 16, + endColumn: 22, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[\\uD83D\\uDC4D]/u" }] }] @@ -100,6 +105,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[👍]/", parserOptions: { ecmaVersion: 3 }, errors: [{ + column: 11, + endColumn: 12, messageId: "surrogatePairWithoutUFlag", suggestions: null // ecmaVersion doesn't support the 'u' flag }] @@ -108,6 +115,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[👍]/", parserOptions: { ecmaVersion: 5 }, errors: [{ + column: 11, + endColumn: 12, messageId: "surrogatePairWithoutUFlag", suggestions: null // ecmaVersion doesn't support the 'u' flag }] @@ -115,6 +124,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[👍]\\a/", errors: [{ + column: 11, + endColumn: 12, messageId: "surrogatePairWithoutUFlag", suggestions: null // pattern would be invalid with the 'u' flag }] @@ -123,6 +134,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /(?<=[👍])/", parserOptions: { ecmaVersion: 9 }, errors: [{ + column: 15, + endColumn: 16, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /(?<=[👍])/u" }] }] @@ -131,6 +144,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /(?<=[👍])/", parserOptions: { ecmaVersion: 2018 }, errors: [{ + column: 15, + endColumn: 16, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /(?<=[👍])/u" }] }] @@ -138,6 +153,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[Á]/", errors: [{ + column: 11, + endColumn: 12, messageId: "combiningClass", suggestions: null }] @@ -145,6 +162,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[Á]/u", errors: [{ + column: 11, + endColumn: 12, messageId: "combiningClass", suggestions: null }] @@ -152,6 +171,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u0041\\u0301]/", errors: [{ + column: 16, + endColumn: 22, messageId: "combiningClass", suggestions: null }] @@ -159,6 +180,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u0041\\u0301]/u", errors: [{ + column: 16, + endColumn: 22, messageId: "combiningClass", suggestions: null }] @@ -166,6 +189,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{41}\\u{301}]/u", errors: [{ + column: 16, + endColumn: 23, messageId: "combiningClass", suggestions: null }] @@ -173,6 +198,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[❇️]/", errors: [{ + column: 11, + endColumn: 12, messageId: "combiningClass", suggestions: null }] @@ -180,6 +207,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[❇️]/u", errors: [{ + column: 11, + endColumn: 12, messageId: "combiningClass", suggestions: null }] @@ -187,6 +216,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u2747\\uFE0F]/", errors: [{ + column: 16, + endColumn: 22, messageId: "combiningClass", suggestions: null }] @@ -194,6 +225,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u2747\\uFE0F]/u", errors: [{ + column: 16, + endColumn: 22, messageId: "combiningClass", suggestions: null }] @@ -201,20 +234,34 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{2747}\\u{FE0F}]/u", errors: [{ + column: 18, + endColumn: 26, messageId: "combiningClass", suggestions: null }] }, { code: "var r = /[👶🏻]/", - errors: [{ - messageId: "surrogatePairWithoutUFlag", - suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👶🏻]/u" }] - }] + errors: [ + { + column: 11, + endColumn: 12, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👶🏻]/u" }] + }, + { + column: 13, + endColumn: 14, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👶🏻]/u" }] + } + ] }, { code: "var r = /[👶🏻]/u", errors: [{ + column: 12, + endColumn: 14, messageId: "emojiModifier", suggestions: null }] @@ -222,6 +269,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83D\\uDC76\\uD83C\\uDFFB]/u", errors: [{ + column: 22, + endColumn: 34, messageId: "emojiModifier", suggestions: null }] @@ -229,27 +278,51 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{1F476}\\u{1F3FB}]/u", errors: [{ + column: 19, + endColumn: 28, messageId: "emojiModifier", suggestions: null }] }, { code: "var r = /[🇯🇵]/", - errors: [{ - messageId: "surrogatePairWithoutUFlag", - suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/u" }] - }] + errors: [ + { + column: 11, + endColumn: 12, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/u" }] + }, + { + column: 13, + endColumn: 14, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/u" }] + } + ] }, { code: "var r = /[🇯🇵]/i", - errors: [{ - messageId: "surrogatePairWithoutUFlag", - suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/iu" }] - }] + errors: [ + { + column: 11, + endColumn: 12, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/iu" }] + }, + { + column: 13, + endColumn: 14, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/iu" }] + } + ] }, { code: "var r = /[🇯🇵]/u", errors: [{ + column: 12, + endColumn: 14, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -257,6 +330,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83C\\uDDEF\\uD83C\\uDDF5]/u", errors: [{ + column: 22, + endColumn: 34, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -264,6 +339,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{1F1EF}\\u{1F1F5}]/u", errors: [{ + column: 19, + endColumn: 28, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -272,41 +349,95 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[👨‍👩‍👦]/", errors: [ { + column: 11, + endColumn: 12, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }] + }, + { + column: 12, + endColumn: 13, + messageId: "zwj", + suggestions: null + }, + { + column: 14, + endColumn: 15, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }] }, { + column: 15, + endColumn: 16, messageId: "zwj", suggestions: null + }, + { + column: 17, + endColumn: 18, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }] } ] }, { code: "var r = /[👨‍👩‍👦]/u", - errors: [{ - messageId: "zwj", - suggestions: null - }] + errors: [ + { + column: 12, + endColumn: 13, + messageId: "zwj", + suggestions: null + }, + { + column: 15, + endColumn: 16, + messageId: "zwj", + suggestions: null + } + ] }, { code: "var r = /[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]/u", - errors: [{ - messageId: "zwj", - suggestions: null - }] + errors: [ + { + column: 22, + endColumn: 28, + messageId: "zwj", + suggestions: null + }, + { + column: 40, + endColumn: 46, + messageId: "zwj", + suggestions: null + } + ] }, { code: "var r = /[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]/u", - errors: [{ - messageId: "zwj", - suggestions: null - }] + errors: [ + { + column: 19, + endColumn: 27, + messageId: "zwj", + suggestions: null + }, + { + column: 36, + endColumn: 44, + messageId: "zwj", + suggestions: null + } + ] }, // RegExp constructors. { code: String.raw`var r = new RegExp("[👍]", "")`, errors: [{ + column: 22, + endColumn: 23, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👍]", "u")` }] }] @@ -314,6 +445,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = new RegExp('[👍]', ``)", errors: [{ + column: 22, + endColumn: 23, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[👍]', `u`)" }] }] @@ -321,6 +454,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[👍]", flags)`, errors: [{ + column: 22, + endColumn: 23, messageId: "surrogatePairWithoutUFlag", suggestions: null }] @@ -328,6 +463,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`const flags = ""; var r = new RegExp("[👍]", flags)`, errors: [{ + column: 40, + endColumn: 41, messageId: "surrogatePairWithoutUFlag", suggestions: null }] @@ -335,6 +472,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "")`, errors: [{ + column: 27, + endColumn: 33, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "u")` }] }] @@ -343,6 +482,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[👍]", "")`, parserOptions: { ecmaVersion: 3 }, errors: [{ + column: 22, + endColumn: 23, messageId: "surrogatePairWithoutUFlag", suggestions: null // ecmaVersion doesn't support the 'u' flag }] @@ -351,6 +492,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[👍]", "")`, parserOptions: { ecmaVersion: 5 }, errors: [{ + column: 22, + endColumn: 23, messageId: "surrogatePairWithoutUFlag", suggestions: null // ecmaVersion doesn't support the 'u' flag }] @@ -358,6 +501,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[👍]\\a", "")`, errors: [{ + column: 22, + endColumn: 23, messageId: "surrogatePairWithoutUFlag", suggestions: null // pattern would be invalid with the 'u' flag }] @@ -366,6 +511,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("/(?<=[👍])", "")`, parserOptions: { ecmaVersion: 9 }, errors: [{ + column: 27, + endColumn: 28, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("/(?<=[👍])", "u")` }] }] @@ -374,6 +521,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("/(?<=[👍])", "")`, parserOptions: { ecmaVersion: 2018 }, errors: [{ + column: 27, + endColumn: 28, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("/(?<=[👍])", "u")` }] }] @@ -381,6 +530,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[Á]", "")`, errors: [{ + column: 22, + endColumn: 23, messageId: "combiningClass", suggestions: null }] @@ -388,6 +539,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[Á]", "u")`, errors: [{ + column: 22, + endColumn: 23, messageId: "combiningClass", suggestions: null }] @@ -395,6 +548,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "")`, errors: [{ + column: 27, + endColumn: 33, messageId: "combiningClass", suggestions: null }] @@ -402,6 +557,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "u")`, errors: [{ + column: 27, + endColumn: 33, messageId: "combiningClass", suggestions: null }] @@ -409,6 +566,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{41}\\u{301}]", "u")`, errors: [{ + column: 27, + endColumn: 34, messageId: "combiningClass", suggestions: null }] @@ -416,6 +575,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[❇️]", "")`, errors: [{ + column: 22, + endColumn: 23, messageId: "combiningClass", suggestions: null }] @@ -423,6 +584,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[❇️]", "u")`, errors: [{ + column: 22, + endColumn: 23, messageId: "combiningClass", suggestions: null }] @@ -430,6 +593,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "")`, errors: [{ + column: 27, + endColumn: 33, messageId: "combiningClass", suggestions: null }] @@ -437,6 +602,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "u")`, errors: [{ + column: 27, + endColumn: 33, messageId: "combiningClass", suggestions: null }] @@ -444,20 +611,34 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{2747}\\u{FE0F}]", "u")`, errors: [{ + column: 29, + endColumn: 37, messageId: "combiningClass", suggestions: null }] }, { code: String.raw`var r = new RegExp("[👶🏻]", "")`, - errors: [{ - messageId: "surrogatePairWithoutUFlag", - suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👶🏻]", "u")` }] - }] + errors: [ + { + column: 22, + endColumn: 23, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👶🏻]", "u")` }] + }, + { + column: 24, + endColumn: 25, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👶🏻]", "u")` }] + } + ] }, { code: String.raw`var r = new RegExp("[👶🏻]", "u")`, errors: [{ + column: 23, + endColumn: 25, messageId: "emojiModifier", suggestions: null }] @@ -465,6 +646,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\uD83D\\uDC76\\uD83C\\uDFFB]", "u")`, errors: [{ + column: 33, + endColumn: 45, messageId: "emojiModifier", suggestions: null }] @@ -472,78 +655,172 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{1F476}\\u{1F3FB}]", "u")`, errors: [{ + column: 30, + endColumn: 39, messageId: "emojiModifier", suggestions: null }] }, { code: String.raw`var r = new RegExp("[🇯🇵]", "")`, - errors: [{ - messageId: "surrogatePairWithoutUFlag", - suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u")` }] - }] + errors: [ + { + column: 22, + endColumn: 23, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u")` }] + }, + { + column: 24, + endColumn: 25, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u")` }] + } + ] }, { code: String.raw`var r = new RegExp("[🇯🇵]", "i")`, - errors: [{ - messageId: "surrogatePairWithoutUFlag", - suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "iu")` }] - }] + errors: [ + { + column: 22, + endColumn: 23, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "iu")` }] + }, + { + column: 24, + endColumn: 25, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "iu")` }] + } + ] }, { code: "var r = new RegExp('[🇯🇵]', `i`)", - errors: [{ - messageId: "surrogatePairWithoutUFlag", - suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[🇯🇵]', `iu`)" }] - }] + errors: [ + { + column: 22, + endColumn: 23, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[🇯🇵]', `iu`)" }] + }, + { + column: 24, + endColumn: 25, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[🇯🇵]', `iu`)" }] + } + ] }, { code: "var r = new RegExp('[🇯🇵]', `${foo}`)", - errors: [{ - messageId: "surrogatePairWithoutUFlag", - suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[🇯🇵]', `${foo}u`)" }] - }] + errors: [ + { + column: 22, + endColumn: 23, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[🇯🇵]', `${foo}u`)" }] + }, + { + column: 24, + endColumn: 25, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[🇯🇵]', `${foo}u`)" }] + } + ] }, { code: String.raw`var r = new RegExp("[🇯🇵]")`, - errors: [{ - messageId: "surrogatePairWithoutUFlag", - suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u")` }] - }] + errors: [ + { + column: 22, + endColumn: 23, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u")` }] + }, + { + column: 24, + endColumn: 25, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u")` }] + } + ] }, { code: String.raw`var r = new RegExp("[🇯🇵]",)`, parserOptions: { ecmaVersion: 2017 }, - errors: [{ - messageId: "surrogatePairWithoutUFlag", - suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u",)` }] - }] + errors: [ + { + column: 22, + endColumn: 23, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u",)` }] + }, + { + column: 24, + endColumn: 25, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u",)` }] + } + ] }, { code: String.raw`var r = new RegExp(("[🇯🇵]"))`, - errors: [{ - messageId: "surrogatePairWithoutUFlag", - suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[🇯🇵]"), "u")` }] - }] + errors: [ + { + column: 23, + endColumn: 24, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[🇯🇵]"), "u")` }] + }, + { + column: 25, + endColumn: 26, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[🇯🇵]"), "u")` }] + } + ] }, { code: String.raw`var r = new RegExp((("[🇯🇵]")))`, - errors: [{ - messageId: "surrogatePairWithoutUFlag", - suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp((("[🇯🇵]")), "u")` }] - }] + errors: [ + { + column: 24, + endColumn: 25, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp((("[🇯🇵]")), "u")` }] + }, + { + column: 26, + endColumn: 27, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp((("[🇯🇵]")), "u")` }] + } + ] }, { code: String.raw`var r = new RegExp(("[🇯🇵]"),)`, parserOptions: { ecmaVersion: 2017 }, - errors: [{ - messageId: "surrogatePairWithoutUFlag", - suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[🇯🇵]"), "u",)` }] - }] + errors: [ + { + column: 23, + endColumn: 24, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[🇯🇵]"), "u",)` }] + }, + { + column: 25, + endColumn: 26, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[🇯🇵]"), "u",)` }] + } + ] }, { code: String.raw`var r = new RegExp("[🇯🇵]", "u")`, errors: [{ + column: 23, + endColumn: 25, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -551,6 +828,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\uD83C\\uDDEF\\uD83C\\uDDF5]", "u")`, errors: [{ + column: 33, + endColumn: 45, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -558,6 +837,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{1F1EF}\\u{1F1F5}]", "u")`, errors: [{ + column: 30, + endColumn: 39, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -566,40 +847,94 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[👨‍👩‍👦]", "")`, errors: [ { + column: 22, + endColumn: 23, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👨‍👩‍👦]", "u")` }] + }, + { + column: 23, + endColumn: 24, + messageId: "zwj", + suggestions: null + }, + { + column: 25, + endColumn: 26, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👨‍👩‍👦]", "u")` }] }, { + column: 26, + endColumn: 27, messageId: "zwj", suggestions: null + }, + { + column: 28, + endColumn: 29, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👨‍👩‍👦]", "u")` }] } ] }, { code: String.raw`var r = new RegExp("[👨‍👩‍👦]", "u")`, - errors: [{ - messageId: "zwj", - suggestions: null - }] + errors: [ + { + column: 23, + endColumn: 24, + messageId: "zwj", + suggestions: null + }, + { + column: 26, + endColumn: 27, + messageId: "zwj", + suggestions: null + } + ] }, { code: String.raw`var r = new RegExp("[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]", "u")`, - errors: [{ - messageId: "zwj", - suggestions: null - }] + errors: [ + { + column: 33, + endColumn: 39, + messageId: "zwj", + suggestions: null + }, + { + column: 51, + endColumn: 57, + messageId: "zwj", + suggestions: null + } + ] }, { code: String.raw`var r = new RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`, - errors: [{ - messageId: "zwj", - suggestions: null - }] + errors: [ + { + column: 30, + endColumn: 38, + messageId: "zwj", + suggestions: null + }, + { + column: 47, + endColumn: 55, + messageId: "zwj", + suggestions: null + } + ] }, { code: String.raw`var r = new globalThis.RegExp("[❇️]", "")`, env: { es2020: true }, errors: [{ + column: 33, + endColumn: 34, messageId: "combiningClass", suggestions: null }] @@ -608,6 +943,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new globalThis.RegExp("[👶🏻]", "u")`, env: { es2020: true }, errors: [{ + column: 34, + endColumn: 36, messageId: "emojiModifier", suggestions: null }] @@ -615,18 +952,38 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new globalThis.RegExp("[🇯🇵]", "")`, env: { es2020: true }, - errors: [{ - messageId: "surrogatePairWithoutUFlag", - suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new globalThis.RegExp("[🇯🇵]", "u")` }] - }] + errors: [ + { + column: 33, + endColumn: 34, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new globalThis.RegExp("[🇯🇵]", "u")` }] + }, + { + column: 35, + endColumn: 36, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new globalThis.RegExp("[🇯🇵]", "u")` }] + } + ] }, { code: String.raw`var r = new globalThis.RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`, env: { es2020: true }, - errors: [{ - messageId: "zwj", - suggestions: null - }] + errors: [ + { + column: 41, + endColumn: 49, + messageId: "zwj", + suggestions: null + }, + { + column: 58, + endColumn: 66, + messageId: "zwj", + suggestions: null + } + ] }, @@ -635,6 +992,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[[👶🏻]]/v", parserOptions: { ecmaVersion: 2024 }, errors: [{ + column: 13, + endColumn: 15, messageId: "emojiModifier", suggestions: null }] @@ -655,6 +1014,8 @@ flatRuleTester.run("no-misleading-character-class", rule, { sourceType: "script" }, errors: [{ + column: 11, + endColumn: 12, messageId: "surrogatePairWithoutUFlag", suggestions: null // ecmaVersion doesn't support the 'u' flag }] @@ -665,6 +1026,8 @@ flatRuleTester.run("no-misleading-character-class", rule, { ecmaVersion: 2015 }, errors: [{ + column: 11, + endColumn: 12, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👍]/u" }] }] From abaefa3cd295ba4a14e8572d1081694277027aee Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Mon, 4 Sep 2023 22:21:52 -0400 Subject: [PATCH 02/31] fix: column offsets --- lib/rules/no-misleading-character-class.js | 7 +- .../rules/no-misleading-character-class.js | 128 +++++++++--------- 2 files changed, 69 insertions(+), 66 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 90dc29ee76b..c03fad91a01 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -175,6 +175,7 @@ module.exports = { } const foundKinds = new Set(); + const offsetForLongUnicode = node.parent.type === "NewExpression" ? 2 : 1; visitRegExpAST(patternNode, { onCharacterClassEnter(ccNode) { @@ -201,15 +202,17 @@ module.exports = { } for (const char of chars) { + const offset = char.raw.startsWith("\\u") ? offsetForLongUnicode : 0; + context.report({ loc: { end: { line: node.loc.end.line, - column: node.loc.start.column + char.end + column: node.loc.start.column + char.end + (offset === 2 ? 3 : offset) }, start: { line: node.loc.start.line, - column: node.loc.start.column + char.start + column: node.loc.start.column + char.start + offset } }, messageId: kind, diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index 940f5620e0b..58ebc709bfa 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -95,8 +95,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83D\\uDC4D]/", errors: [{ - column: 16, - endColumn: 22, + column: 17, + endColumn: 23, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[\\uD83D\\uDC4D]/u" }] }] @@ -171,8 +171,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u0041\\u0301]/", errors: [{ - column: 16, - endColumn: 22, + column: 17, + endColumn: 23, messageId: "combiningClass", suggestions: null }] @@ -180,8 +180,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u0041\\u0301]/u", errors: [{ - column: 16, - endColumn: 22, + column: 17, + endColumn: 23, messageId: "combiningClass", suggestions: null }] @@ -189,8 +189,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{41}\\u{301}]/u", errors: [{ - column: 16, - endColumn: 23, + column: 17, + endColumn: 24, messageId: "combiningClass", suggestions: null }] @@ -216,8 +216,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u2747\\uFE0F]/", errors: [{ - column: 16, - endColumn: 22, + column: 17, + endColumn: 23, messageId: "combiningClass", suggestions: null }] @@ -225,8 +225,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u2747\\uFE0F]/u", errors: [{ - column: 16, - endColumn: 22, + column: 17, + endColumn: 23, messageId: "combiningClass", suggestions: null }] @@ -234,8 +234,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{2747}\\u{FE0F}]/u", errors: [{ - column: 18, - endColumn: 26, + column: 19, + endColumn: 27, messageId: "combiningClass", suggestions: null }] @@ -269,8 +269,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83D\\uDC76\\uD83C\\uDFFB]/u", errors: [{ - column: 22, - endColumn: 34, + column: 23, + endColumn: 35, messageId: "emojiModifier", suggestions: null }] @@ -278,8 +278,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{1F476}\\u{1F3FB}]/u", errors: [{ - column: 19, - endColumn: 28, + column: 20, + endColumn: 29, messageId: "emojiModifier", suggestions: null }] @@ -330,8 +330,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83C\\uDDEF\\uD83C\\uDDF5]/u", errors: [{ - column: 22, - endColumn: 34, + column: 23, + endColumn: 35, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -339,8 +339,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{1F1EF}\\u{1F1F5}]/u", errors: [{ - column: 19, - endColumn: 28, + column: 20, + endColumn: 29, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -401,14 +401,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]/u", errors: [ { - column: 22, - endColumn: 28, + column: 23, + endColumn: 29, messageId: "zwj", suggestions: null }, { - column: 40, - endColumn: 46, + column: 41, + endColumn: 47, messageId: "zwj", suggestions: null } @@ -418,14 +418,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]/u", errors: [ { - column: 19, - endColumn: 27, + column: 20, + endColumn: 28, messageId: "zwj", suggestions: null }, { - column: 36, - endColumn: 44, + column: 37, + endColumn: 45, messageId: "zwj", suggestions: null } @@ -472,8 +472,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "")`, errors: [{ - column: 27, - endColumn: 33, + column: 29, + endColumn: 36, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "u")` }] }] @@ -548,8 +548,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "")`, errors: [{ - column: 27, - endColumn: 33, + column: 29, + endColumn: 36, messageId: "combiningClass", suggestions: null }] @@ -557,8 +557,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "u")`, errors: [{ - column: 27, - endColumn: 33, + column: 29, + endColumn: 36, messageId: "combiningClass", suggestions: null }] @@ -566,8 +566,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{41}\\u{301}]", "u")`, errors: [{ - column: 27, - endColumn: 34, + column: 29, + endColumn: 37, messageId: "combiningClass", suggestions: null }] @@ -593,8 +593,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "")`, errors: [{ - column: 27, - endColumn: 33, + column: 29, + endColumn: 36, messageId: "combiningClass", suggestions: null }] @@ -602,8 +602,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "u")`, errors: [{ - column: 27, - endColumn: 33, + column: 29, + endColumn: 36, messageId: "combiningClass", suggestions: null }] @@ -611,8 +611,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{2747}\\u{FE0F}]", "u")`, errors: [{ - column: 29, - endColumn: 37, + column: 31, + endColumn: 40, messageId: "combiningClass", suggestions: null }] @@ -646,8 +646,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\uD83D\\uDC76\\uD83C\\uDFFB]", "u")`, errors: [{ - column: 33, - endColumn: 45, + column: 35, + endColumn: 48, messageId: "emojiModifier", suggestions: null }] @@ -655,8 +655,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{1F476}\\u{1F3FB}]", "u")`, errors: [{ - column: 30, - endColumn: 39, + column: 32, + endColumn: 42, messageId: "emojiModifier", suggestions: null }] @@ -828,8 +828,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\uD83C\\uDDEF\\uD83C\\uDDF5]", "u")`, errors: [{ - column: 33, - endColumn: 45, + column: 35, + endColumn: 48, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -837,8 +837,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{1F1EF}\\u{1F1F5}]", "u")`, errors: [{ - column: 30, - endColumn: 39, + column: 32, + endColumn: 42, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -899,14 +899,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]", "u")`, errors: [ { - column: 33, - endColumn: 39, + column: 35, + endColumn: 42, messageId: "zwj", suggestions: null }, { - column: 51, - endColumn: 57, + column: 53, + endColumn: 60, messageId: "zwj", suggestions: null } @@ -916,14 +916,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`, errors: [ { - column: 30, - endColumn: 38, + column: 32, + endColumn: 41, messageId: "zwj", suggestions: null }, { - column: 47, - endColumn: 55, + column: 49, + endColumn: 58, messageId: "zwj", suggestions: null } @@ -972,14 +972,14 @@ ruleTester.run("no-misleading-character-class", rule, { env: { es2020: true }, errors: [ { - column: 41, - endColumn: 49, + column: 43, + endColumn: 52, messageId: "zwj", suggestions: null }, { - column: 58, - endColumn: 66, + column: 60, + endColumn: 69, messageId: "zwj", suggestions: null } From cf27ff21cb34f3e1b05f1fd911b077c82ddb74ea Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 5 Sep 2023 13:46:54 -0400 Subject: [PATCH 03/31] fix: missing CallExpression --- lib/rules/no-misleading-character-class.js | 2 +- .../rules/no-misleading-character-class.js | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index c03fad91a01..7a8921a76f5 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -175,7 +175,7 @@ module.exports = { } const foundKinds = new Set(); - const offsetForLongUnicode = node.parent.type === "NewExpression" ? 2 : 1; + const offsetForLongUnicode = ["CallExpression", "NewExpression"].includes(node.parent.type) ? 2 : 1; visitRegExpAST(patternNode, { onCharacterClassEnter(ccNode) { diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index 58ebc709bfa..3bc79e026d1 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -67,6 +67,7 @@ ruleTester.run("no-misleading-character-class", rule, { "var r = /[\\u200D]/u", // don't report and don't crash on invalid regex + "new RegExp('[Á] [ ');", "var r = new RegExp('[Á] [ ');", "var r = RegExp('{ [Á]', 'u');", { code: "var r = new globalThis.RegExp('[Á] [ ');", env: { es2020: true } }, @@ -433,6 +434,15 @@ ruleTester.run("no-misleading-character-class", rule, { }, // RegExp constructors. + { + code: String.raw`var r = RegExp("[👍]", "")`, + errors: [{ + column: 18, + endColumn: 19, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("[👍]", "u")` }] + }] + }, { code: String.raw`var r = new RegExp("[👍]", "")`, errors: [{ @@ -469,6 +479,15 @@ ruleTester.run("no-misleading-character-class", rule, { suggestions: null }] }, + { + code: String.raw`var r = RegExp("[\\uD83D\\uDC4D]", "")`, + errors: [{ + column: 25, + endColumn: 32, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("[\\uD83D\\uDC4D]", "u")` }] + }] + }, { code: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "")`, errors: [{ From a822015fb024b4ec4c5c37c4d620106c0e429710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Tue, 5 Sep 2023 14:12:38 -0400 Subject: [PATCH 04/31] Apply suggestions from code review Co-authored-by: Francesco Trotta --- lib/rules/no-misleading-character-class.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 7a8921a76f5..c79ee30f85b 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -174,7 +174,7 @@ module.exports = { return; } - const foundKinds = new Set(); + const foundKinds = []; const offsetForLongUnicode = ["CallExpression", "NewExpression"].includes(node.parent.type) ? 2 : 1; visitRegExpAST(patternNode, { @@ -184,7 +184,7 @@ module.exports = { const matchingChars = accumulate(chars, characterSequenceIndexFilters[kind]); if (matchingChars.length) { - foundKinds.add({ chars: matchingChars, kind }); + foundKinds.push({ chars: matchingChars, kind }); } } } From f31d8e1344646dea7a4744769985671a95be893c Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 11 Oct 2023 16:23:59 -0400 Subject: [PATCH 05/31] All tests passing again --- lib/rules/no-misleading-character-class.js | 67 ++++- .../rules/no-misleading-character-class.js | 265 +++++++++--------- 2 files changed, 187 insertions(+), 145 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index d8d25f42b6b..4170a558aa2 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -195,6 +195,55 @@ module.exports = { const sourceCode = context.sourceCode; const parser = new RegExpParser(); + /** + * Determines what offset to start a granular report, if it's straightforward to do so. + * @param {string} char Individual character being reported on. + * @param {Node} node Parent string node to report within. + * @see https://github.com/eslint/eslint/pull/17515#issuecomment-1707697186s + * @returns {Object | null} Report offset, or null to report on the whole node. + */ + function getReportStartOffset(char, node) { + if (node.type !== "Literal" && (node.type !== "TemplateLiteral" || node.expressions.length)) { + return null; + } + + const offsetStart = node.raw.slice(0, char.start + char.raw.length + 2).indexOf(char.raw, char.start - 2); + + return offsetStart === -1 ? null : offsetStart; + } + + /** + * Generates a granular loc for context.report. + * @param {string} char Individual character being reported on. + * @param {string} kind What kind of character report is being made. + * @param {Node} node Parent string node to report within. + * @param {number} reportStartOffset Starting offset to report within the node. + * @returns {Object} Granular loc for context.report. + * @see https://github.com/eslint/eslint/pull/17515#issuecomment-1707697186s + */ + function generateReportLocation(char, kind, node, reportStartOffset) { + const reportEndOffset = reportStartOffset + char.raw.length; + let reportStartOffsetAdjusted = reportStartOffset; + + if (!char.raw.startsWith("\\u")) { + reportStartOffsetAdjusted -= + ["combiningClass", "surrogatePairWithoutUFlag"].includes(kind) + ? 1 + : 2; + } + + return { + end: { + line: node.loc.end.line, + column: node.loc.start.column + reportEndOffset + }, + start: { + line: node.loc.start.line, + column: node.loc.start.column + reportStartOffsetAdjusted + } + }; + } + /** * Verify a given regular expression. * @param {Node} node The node to report. @@ -223,7 +272,6 @@ module.exports = { } const foundKinds = []; - const offsetForLongUnicode = ["CallExpression", "NewExpression"].includes(node.parent.type) ? 2 : 1; visitRegExpAST(patternNode, { onCharacterClassEnter(ccNode) { @@ -250,19 +298,14 @@ module.exports = { } for (const char of chars) { - const offset = char.raw.startsWith("\\u") ? offsetForLongUnicode : 0; + const reportStartOffset = getReportStartOffset(char, node); context.report({ - loc: { - end: { - line: node.loc.end.line, - column: node.loc.start.column + char.end + (offset === 2 ? 3 : offset) - }, - start: { - line: node.loc.start.line, - column: node.loc.start.column + char.start + offset - } - }, + ...( + reportStartOffset + ? { loc: generateReportLocation(char, kind, node, reportStartOffset) } + : { node } + ), messageId: kind, suggest }); diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index eb112920c44..54a6066546a 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -87,7 +87,7 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[👍]/", errors: [{ column: 11, - endColumn: 12, + endColumn: 13, line: 1, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👍]/u" }] @@ -107,7 +107,7 @@ ruleTester.run("no-misleading-character-class", rule, { parserOptions: { ecmaVersion: 3 }, errors: [{ column: 11, - endColumn: 12, + endColumn: 13, messageId: "surrogatePairWithoutUFlag", suggestions: null // ecmaVersion doesn't support the 'u' flag }] @@ -117,7 +117,7 @@ ruleTester.run("no-misleading-character-class", rule, { parserOptions: { ecmaVersion: 5 }, errors: [{ column: 11, - endColumn: 12, + endColumn: 13, messageId: "surrogatePairWithoutUFlag", suggestions: null // ecmaVersion doesn't support the 'u' flag }] @@ -126,7 +126,7 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[👍]\\a/", errors: [{ column: 11, - endColumn: 12, + endColumn: 13, messageId: "surrogatePairWithoutUFlag", suggestions: null // pattern would be invalid with the 'u' flag }] @@ -136,7 +136,7 @@ ruleTester.run("no-misleading-character-class", rule, { parserOptions: { ecmaVersion: 9 }, errors: [{ column: 15, - endColumn: 16, + endColumn: 17, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /(?<=[👍])/u" }] }] @@ -146,7 +146,7 @@ ruleTester.run("no-misleading-character-class", rule, { parserOptions: { ecmaVersion: 2018 }, errors: [{ column: 15, - endColumn: 16, + endColumn: 17, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /(?<=[👍])/u" }] }] @@ -155,7 +155,7 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[Á]/", errors: [{ column: 11, - endColumn: 12, + endColumn: 13, messageId: "combiningClass", suggestions: null }] @@ -164,7 +164,7 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[Á]/u", errors: [{ column: 11, - endColumn: 12, + endColumn: 13, messageId: "combiningClass", suggestions: null }] @@ -200,7 +200,7 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[❇️]/", errors: [{ column: 11, - endColumn: 12, + endColumn: 13, messageId: "combiningClass", suggestions: null }] @@ -209,7 +209,7 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[❇️]/u", errors: [{ column: 11, - endColumn: 12, + endColumn: 13, messageId: "combiningClass", suggestions: null }] @@ -246,13 +246,13 @@ ruleTester.run("no-misleading-character-class", rule, { errors: [ { column: 11, - endColumn: 12, + endColumn: 13, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👶🏻]/u" }] }, { column: 13, - endColumn: 14, + endColumn: 15, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👶🏻]/u" }] } @@ -261,8 +261,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[👶🏻]/u", errors: [{ - column: 12, - endColumn: 14, + column: 11, + endColumn: 15, messageId: "emojiModifier", suggestions: null }] @@ -290,13 +290,13 @@ ruleTester.run("no-misleading-character-class", rule, { errors: [ { column: 11, - endColumn: 12, + endColumn: 13, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/u" }] }, { column: 13, - endColumn: 14, + endColumn: 15, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/u" }] } @@ -307,13 +307,13 @@ ruleTester.run("no-misleading-character-class", rule, { errors: [ { column: 11, - endColumn: 12, + endColumn: 13, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/iu" }] }, { column: 13, - endColumn: 14, + endColumn: 15, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/iu" }] } @@ -322,8 +322,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[🇯🇵]/u", errors: [{ - column: 12, - endColumn: 14, + column: 11, + endColumn: 15, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -351,31 +351,30 @@ ruleTester.run("no-misleading-character-class", rule, { errors: [ { column: 11, - endColumn: 12, + endColumn: 13, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }] }, { - column: 12, - endColumn: 13, + column: 11, + endColumn: 14, messageId: "zwj", suggestions: null }, { - column: 14, - endColumn: 15, - messageId: "surrogatePairWithoutUFlag", - suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }] + column: 11, + endColumn: 14, + messageId: "zwj" }, { - column: 15, + column: 14, endColumn: 16, - messageId: "zwj", - suggestions: null + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }] }, { column: 17, - endColumn: 18, + endColumn: 19, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }] } @@ -385,14 +384,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[👨‍👩‍👦]/u", errors: [ { - column: 12, - endColumn: 13, + column: 11, + endColumn: 14, messageId: "zwj", suggestions: null }, { - column: 15, - endColumn: 16, + column: 11, + endColumn: 14, messageId: "zwj", suggestions: null } @@ -438,7 +437,7 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = RegExp("[👍]", "")`, errors: [{ column: 18, - endColumn: 19, + endColumn: 20, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("[👍]", "u")` }] }] @@ -447,7 +446,7 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[👍]", "")`, errors: [{ column: 22, - endColumn: 23, + endColumn: 24, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👍]", "u")` }] }] @@ -456,7 +455,7 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = new RegExp('[👍]', ``)", errors: [{ column: 22, - endColumn: 23, + endColumn: 24, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[👍]', `u`)" }] }] @@ -465,7 +464,7 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[👍]", flags)`, errors: [{ column: 22, - endColumn: 23, + endColumn: 24, messageId: "surrogatePairWithoutUFlag", suggestions: null }] @@ -474,7 +473,7 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`const flags = ""; var r = new RegExp("[👍]", flags)`, errors: [{ column: 40, - endColumn: 41, + endColumn: 42, messageId: "surrogatePairWithoutUFlag", suggestions: null }] @@ -482,8 +481,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = RegExp("[\\uD83D\\uDC4D]", "")`, errors: [{ - column: 25, - endColumn: 32, + column: 16, + endColumn: 34, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("[\\uD83D\\uDC4D]", "u")` }] }] @@ -491,8 +490,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "")`, errors: [{ - column: 29, - endColumn: 36, + column: 20, + endColumn: 38, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "u")` }] }] @@ -502,7 +501,7 @@ ruleTester.run("no-misleading-character-class", rule, { parserOptions: { ecmaVersion: 3 }, errors: [{ column: 22, - endColumn: 23, + endColumn: 24, messageId: "surrogatePairWithoutUFlag", suggestions: null // ecmaVersion doesn't support the 'u' flag }] @@ -512,7 +511,7 @@ ruleTester.run("no-misleading-character-class", rule, { parserOptions: { ecmaVersion: 5 }, errors: [{ column: 22, - endColumn: 23, + endColumn: 24, messageId: "surrogatePairWithoutUFlag", suggestions: null // ecmaVersion doesn't support the 'u' flag }] @@ -521,7 +520,7 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[👍]\\a", "")`, errors: [{ column: 22, - endColumn: 23, + endColumn: 24, messageId: "surrogatePairWithoutUFlag", suggestions: null // pattern would be invalid with the 'u' flag }] @@ -531,7 +530,7 @@ ruleTester.run("no-misleading-character-class", rule, { parserOptions: { ecmaVersion: 9 }, errors: [{ column: 27, - endColumn: 28, + endColumn: 29, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("/(?<=[👍])", "u")` }] }] @@ -541,7 +540,7 @@ ruleTester.run("no-misleading-character-class", rule, { parserOptions: { ecmaVersion: 2018 }, errors: [{ column: 27, - endColumn: 28, + endColumn: 29, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("/(?<=[👍])", "u")` }] }] @@ -550,7 +549,7 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[Á]", "")`, errors: [{ column: 22, - endColumn: 23, + endColumn: 24, messageId: "combiningClass", suggestions: null }] @@ -559,7 +558,7 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[Á]", "u")`, errors: [{ column: 22, - endColumn: 23, + endColumn: 24, messageId: "combiningClass", suggestions: null }] @@ -567,8 +566,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "")`, errors: [{ - column: 29, - endColumn: 36, + column: 20, + endColumn: 38, messageId: "combiningClass", suggestions: null }] @@ -576,8 +575,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "u")`, errors: [{ - column: 29, - endColumn: 36, + column: 20, + endColumn: 38, messageId: "combiningClass", suggestions: null }] @@ -585,8 +584,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{41}\\u{301}]", "u")`, errors: [{ - column: 29, - endColumn: 37, + column: 20, + endColumn: 39, messageId: "combiningClass", suggestions: null }] @@ -595,7 +594,7 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[❇️]", "")`, errors: [{ column: 22, - endColumn: 23, + endColumn: 24, messageId: "combiningClass", suggestions: null }] @@ -604,7 +603,7 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[❇️]", "u")`, errors: [{ column: 22, - endColumn: 23, + endColumn: 24, messageId: "combiningClass", suggestions: null }] @@ -612,8 +611,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "")`, errors: [{ - column: 29, - endColumn: 36, + column: 20, + endColumn: 38, messageId: "combiningClass", suggestions: null }] @@ -621,8 +620,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "u")`, errors: [{ - column: 29, - endColumn: 36, + column: 20, + endColumn: 38, messageId: "combiningClass", suggestions: null }] @@ -630,8 +629,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{2747}\\u{FE0F}]", "u")`, errors: [{ - column: 31, - endColumn: 40, + column: 20, + endColumn: 42, messageId: "combiningClass", suggestions: null }] @@ -641,13 +640,13 @@ ruleTester.run("no-misleading-character-class", rule, { errors: [ { column: 22, - endColumn: 23, + endColumn: 24, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👶🏻]", "u")` }] }, { column: 24, - endColumn: 25, + endColumn: 26, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👶🏻]", "u")` }] } @@ -656,8 +655,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[👶🏻]", "u")`, errors: [{ - column: 23, - endColumn: 25, + column: 22, + endColumn: 26, messageId: "emojiModifier", suggestions: null }] @@ -665,8 +664,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\uD83D\\uDC76\\uD83C\\uDFFB]", "u")`, errors: [{ - column: 35, - endColumn: 48, + column: 20, + endColumn: 52, messageId: "emojiModifier", suggestions: null }] @@ -674,8 +673,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{1F476}\\u{1F3FB}]", "u")`, errors: [{ - column: 32, - endColumn: 42, + column: 20, + endColumn: 44, messageId: "emojiModifier", suggestions: null }] @@ -685,13 +684,13 @@ ruleTester.run("no-misleading-character-class", rule, { errors: [ { column: 22, - endColumn: 23, + endColumn: 24, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u")` }] }, { column: 24, - endColumn: 25, + endColumn: 26, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u")` }] } @@ -702,13 +701,13 @@ ruleTester.run("no-misleading-character-class", rule, { errors: [ { column: 22, - endColumn: 23, + endColumn: 24, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "iu")` }] }, { column: 24, - endColumn: 25, + endColumn: 26, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "iu")` }] } @@ -719,13 +718,13 @@ ruleTester.run("no-misleading-character-class", rule, { errors: [ { column: 22, - endColumn: 23, + endColumn: 24, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[🇯🇵]', `iu`)" }] }, { column: 24, - endColumn: 25, + endColumn: 26, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[🇯🇵]', `iu`)" }] } @@ -736,13 +735,13 @@ ruleTester.run("no-misleading-character-class", rule, { errors: [ { column: 22, - endColumn: 23, + endColumn: 24, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[🇯🇵]', `${foo}u`)" }] }, { column: 24, - endColumn: 25, + endColumn: 26, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[🇯🇵]', `${foo}u`)" }] } @@ -753,13 +752,13 @@ ruleTester.run("no-misleading-character-class", rule, { errors: [ { column: 22, - endColumn: 23, + endColumn: 24, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u")` }] }, { column: 24, - endColumn: 25, + endColumn: 26, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u")` }] } @@ -771,13 +770,13 @@ ruleTester.run("no-misleading-character-class", rule, { errors: [ { column: 22, - endColumn: 23, + endColumn: 24, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u",)` }] }, { column: 24, - endColumn: 25, + endColumn: 26, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u",)` }] } @@ -788,13 +787,13 @@ ruleTester.run("no-misleading-character-class", rule, { errors: [ { column: 23, - endColumn: 24, + endColumn: 25, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[🇯🇵]"), "u")` }] }, { column: 25, - endColumn: 26, + endColumn: 27, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[🇯🇵]"), "u")` }] } @@ -805,13 +804,13 @@ ruleTester.run("no-misleading-character-class", rule, { errors: [ { column: 24, - endColumn: 25, + endColumn: 26, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp((("[🇯🇵]")), "u")` }] }, { column: 26, - endColumn: 27, + endColumn: 28, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp((("[🇯🇵]")), "u")` }] } @@ -823,13 +822,13 @@ ruleTester.run("no-misleading-character-class", rule, { errors: [ { column: 23, - endColumn: 24, + endColumn: 25, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[🇯🇵]"), "u",)` }] }, { column: 25, - endColumn: 26, + endColumn: 27, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[🇯🇵]"), "u",)` }] } @@ -838,8 +837,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[🇯🇵]", "u")`, errors: [{ - column: 23, - endColumn: 25, + column: 22, + endColumn: 26, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -847,8 +846,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\uD83C\\uDDEF\\uD83C\\uDDF5]", "u")`, errors: [{ - column: 35, - endColumn: 48, + column: 20, + endColumn: 52, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -856,8 +855,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{1F1EF}\\u{1F1F5}]", "u")`, errors: [{ - column: 32, - endColumn: 42, + column: 20, + endColumn: 44, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -867,31 +866,31 @@ ruleTester.run("no-misleading-character-class", rule, { errors: [ { column: 22, - endColumn: 23, + endColumn: 24, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👨‍👩‍👦]", "u")` }] }, { - column: 23, - endColumn: 24, + column: 22, + endColumn: 25, messageId: "zwj", suggestions: null }, { - column: 25, - endColumn: 26, - messageId: "surrogatePairWithoutUFlag", - suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👨‍👩‍👦]", "u")` }] + column: 22, + endColumn: 25, + messageId: "zwj", + suggestions: null }, { - column: 26, + column: 25, endColumn: 27, - messageId: "zwj", - suggestions: null + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👨‍👩‍👦]", "u")` }] }, { column: 28, - endColumn: 29, + endColumn: 30, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👨‍👩‍👦]", "u")` }] } @@ -901,14 +900,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[👨‍👩‍👦]", "u")`, errors: [ { - column: 23, - endColumn: 24, + column: 22, + endColumn: 25, messageId: "zwj", suggestions: null }, { - column: 26, - endColumn: 27, + column: 22, + endColumn: 25, messageId: "zwj", suggestions: null } @@ -918,14 +917,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]", "u")`, errors: [ { - column: 35, - endColumn: 42, + column: 20, + endColumn: 80, messageId: "zwj", suggestions: null }, { - column: 53, - endColumn: 60, + column: 20, + endColumn: 80, messageId: "zwj", suggestions: null } @@ -935,14 +934,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`, errors: [ { - column: 32, - endColumn: 41, + column: 20, + endColumn: 72, messageId: "zwj", suggestions: null }, { - column: 49, - endColumn: 58, + column: 20, + endColumn: 72, messageId: "zwj", suggestions: null } @@ -953,7 +952,7 @@ ruleTester.run("no-misleading-character-class", rule, { env: { es2020: true }, errors: [{ column: 33, - endColumn: 34, + endColumn: 35, messageId: "combiningClass", suggestions: null }] @@ -962,8 +961,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new globalThis.RegExp("[👶🏻]", "u")`, env: { es2020: true }, errors: [{ - column: 34, - endColumn: 36, + column: 33, + endColumn: 37, messageId: "emojiModifier", suggestions: null }] @@ -974,13 +973,13 @@ ruleTester.run("no-misleading-character-class", rule, { errors: [ { column: 33, - endColumn: 34, + endColumn: 35, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new globalThis.RegExp("[🇯🇵]", "u")` }] }, { column: 35, - endColumn: 36, + endColumn: 37, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new globalThis.RegExp("[🇯🇵]", "u")` }] } @@ -991,14 +990,14 @@ ruleTester.run("no-misleading-character-class", rule, { env: { es2020: true }, errors: [ { - column: 43, - endColumn: 52, + column: 31, + endColumn: 83, messageId: "zwj", suggestions: null }, { - column: 60, - endColumn: 69, + column: 31, + endColumn: 83, messageId: "zwj", suggestions: null } @@ -1039,8 +1038,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[[👶🏻]]/v", parserOptions: { ecmaVersion: 2024 }, errors: [{ - column: 13, - endColumn: 15, + column: 12, + endColumn: 16, messageId: "emojiModifier", suggestions: null }] @@ -1062,7 +1061,7 @@ flatRuleTester.run("no-misleading-character-class", rule, { }, errors: [{ column: 11, - endColumn: 12, + endColumn: 13, messageId: "surrogatePairWithoutUFlag", suggestions: null // ecmaVersion doesn't support the 'u' flag }] @@ -1074,7 +1073,7 @@ flatRuleTester.run("no-misleading-character-class", rule, { }, errors: [{ column: 11, - endColumn: 12, + endColumn: 13, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👍]/u" }] }] From 4472129080404840266e5c6eddc55e288e1453a3 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 11 Oct 2023 16:28:17 -0400 Subject: [PATCH 06/31] Edge case: quadruple back slashes --- lib/rules/no-misleading-character-class.js | 4 ++++ .../lib/rules/no-misleading-character-class.js | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 4170a558aa2..876df6cbf8e 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -232,6 +232,10 @@ module.exports = { : 2; } + if (node.raw.slice(char.start + 1, char.start + 3) === "\\\\") { + reportStartOffsetAdjusted -= 1; + } + return { end: { line: node.loc.end.line, diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index 54a6066546a..b884e4a3dc3 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -608,6 +608,24 @@ ruleTester.run("no-misleading-character-class", rule, { suggestions: null }] }, + { + code: String.raw`new RegExp("[ \\ufe0f]", "")`, + errors: [{ + column: 15, + endColumn: 22, + messageId: "combiningClass", + suggestions: null + }] + }, + { + code: String.raw`new RegExp("[ \\ufe0f]", "u")`, + errors: [{ + column: 15, + endColumn: 22, + messageId: "combiningClass", + suggestions: null + }] + }, { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "")`, errors: [{ From 629fa22e6b8a22593bf1807d0d969a04633450c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Fri, 13 Oct 2023 09:55:59 -0400 Subject: [PATCH 07/31] Apply suggestions from code review Co-authored-by: Francesco Trotta --- lib/rules/no-misleading-character-class.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 876df6cbf8e..afb27ee6faf 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -74,7 +74,7 @@ function isUnicodeCodePointEscape(char) { /** * Each function returns `true` if it detects that kind of problem. - * @type {Record boolean>} + * @type {Record boolean>} */ const characterSequenceIndexFilters = { surrogatePairWithoutUFlag(char, index, chars) { @@ -146,8 +146,8 @@ const kinds = Object.keys(characterSequenceIndexFilters); /** * Collects the indices where the filter returns true. - * @param {string[]} chars Characters to run the filter on. - * @param {(char: string) => boolean} filter Determines whether an index should be returned. + * @param {Character[]} chars Characters to run the filter on. + * @param {(char: Character, index: number, chars: Character[]) => boolean} filter Determines whether an index should be returned. * @returns {number[]} Indices where the filter returned true. */ function accumulate(chars, filter) { @@ -197,9 +197,9 @@ module.exports = { /** * Determines what offset to start a granular report, if it's straightforward to do so. - * @param {string} char Individual character being reported on. + * @param {Character} char Individual character being reported on. * @param {Node} node Parent string node to report within. - * @see https://github.com/eslint/eslint/pull/17515#issuecomment-1707697186s + * @see https://github.com/eslint/eslint/pull/17515#issuecomment-1707697186 * @returns {Object | null} Report offset, or null to report on the whole node. */ function getReportStartOffset(char, node) { @@ -214,7 +214,7 @@ module.exports = { /** * Generates a granular loc for context.report. - * @param {string} char Individual character being reported on. + * @param {Character} char Individual character being reported on. * @param {string} kind What kind of character report is being made. * @param {Node} node Parent string node to report within. * @param {number} reportStartOffset Starting offset to report within the node. From 206f0e209981b59122bcfd6c897a40ea2125a5b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Fri, 13 Oct 2023 09:56:41 -0400 Subject: [PATCH 08/31] Update lib/rules/no-misleading-character-class.js Co-authored-by: Francesco Trotta --- lib/rules/no-misleading-character-class.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index afb27ee6faf..708a870bde9 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -219,7 +219,7 @@ module.exports = { * @param {Node} node Parent string node to report within. * @param {number} reportStartOffset Starting offset to report within the node. * @returns {Object} Granular loc for context.report. - * @see https://github.com/eslint/eslint/pull/17515#issuecomment-1707697186s + * @see https://github.com/eslint/eslint/pull/17515#issuecomment-1707697186 */ function generateReportLocation(char, kind, node, reportStartOffset) { const reportEndOffset = reportStartOffset + char.raw.length; From 5bca6399728fdf696bd0128e1e07c3a731feaa63 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Fri, 13 Oct 2023 11:44:38 -0400 Subject: [PATCH 09/31] Adjusted for repeat characters --- lib/rules/no-misleading-character-class.js | 20 +++- .../rules/no-misleading-character-class.js | 110 ++++++++++-------- 2 files changed, 79 insertions(+), 51 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 708a870bde9..0cdc87a65d1 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -198,16 +198,24 @@ module.exports = { /** * Determines what offset to start a granular report, if it's straightforward to do so. * @param {Character} char Individual character being reported on. + * @param {string} kind What kind of character report is being made. * @param {Node} node Parent string node to report within. - * @see https://github.com/eslint/eslint/pull/17515#issuecomment-1707697186 + * @param {Object} previousReport Kind and start offset for the previous report, if there was one. * @returns {Object | null} Report offset, or null to report on the whole node. + * @see https://github.com/eslint/eslint/pull/17515#issuecomment-1707697186 */ - function getReportStartOffset(char, node) { + function getReportStartOffset(char, kind, node, previousReport = {}) { if (node.type !== "Literal" && (node.type !== "TemplateLiteral" || node.expressions.length)) { return null; } - const offsetStart = node.raw.slice(0, char.start + char.raw.length + 2).indexOf(char.raw, char.start - 2); + // If a problematic character exists twice in the string, start each Nth search after N - 1 + const searchStart = previousReport && previousReport.kind === kind + ? (previousReport.startOffset + 1) + : 0; + + const searchableLength = searchStart + char.start + char.raw.length * 2; + const offsetStart = node.raw.slice(0, searchableLength).indexOf(char.raw, searchStart); return offsetStart === -1 ? null : offsetStart; } @@ -291,6 +299,8 @@ module.exports = { } }); + let previousReport; + for (const { chars, kind } of foundKinds) { let suggest; @@ -302,7 +312,7 @@ module.exports = { } for (const char of chars) { - const reportStartOffset = getReportStartOffset(char, node); + const reportStartOffset = getReportStartOffset(char, kind, node, previousReport); context.report({ ...( @@ -313,6 +323,8 @@ module.exports = { messageId: kind, suggest }); + + previousReport = { kind, startOffset: reportStartOffset }; } } } diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index b884e4a3dc3..9cad7366966 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -361,17 +361,17 @@ ruleTester.run("no-misleading-character-class", rule, { messageId: "zwj", suggestions: null }, - { - column: 11, - endColumn: 14, - messageId: "zwj" - }, { column: 14, endColumn: 16, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }] }, + { + column: 14, + endColumn: 17, + messageId: "zwj" + }, { column: 17, endColumn: 19, @@ -390,8 +390,8 @@ ruleTester.run("no-misleading-character-class", rule, { suggestions: null }, { - column: 11, - endColumn: 14, + column: 14, + endColumn: 17, messageId: "zwj", suggestions: null } @@ -481,8 +481,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = RegExp("[\\uD83D\\uDC4D]", "")`, errors: [{ - column: 16, - endColumn: 34, + column: 26, + endColumn: 32, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("[\\uD83D\\uDC4D]", "u")` }] }] @@ -490,8 +490,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "")`, errors: [{ - column: 20, - endColumn: 38, + column: 30, + endColumn: 36, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "u")` }] }] @@ -566,8 +566,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "")`, errors: [{ - column: 20, - endColumn: 38, + column: 30, + endColumn: 36, messageId: "combiningClass", suggestions: null }] @@ -575,8 +575,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "u")`, errors: [{ - column: 20, - endColumn: 38, + column: 30, + endColumn: 36, messageId: "combiningClass", suggestions: null }] @@ -584,8 +584,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{41}\\u{301}]", "u")`, errors: [{ - column: 20, - endColumn: 39, + column: 30, + endColumn: 37, messageId: "combiningClass", suggestions: null }] @@ -626,11 +626,28 @@ ruleTester.run("no-misleading-character-class", rule, { suggestions: null }] }, + { + code: String.raw`new RegExp("[ \\ufe0f][ \\ufe0f]")`, + errors: [ + { + column: 15, + endColumn: 22, + messageId: "combiningClass", + suggestions: null + }, + { + column: 26, + endColumn: 32, + messageId: "combiningClass", + suggestions: null + } + ] + }, { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "")`, errors: [{ - column: 20, - endColumn: 38, + column: 30, + endColumn: 36, messageId: "combiningClass", suggestions: null }] @@ -638,8 +655,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "u")`, errors: [{ - column: 20, - endColumn: 38, + column: 30, + endColumn: 36, messageId: "combiningClass", suggestions: null }] @@ -647,8 +664,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{2747}\\u{FE0F}]", "u")`, errors: [{ - column: 20, - endColumn: 42, + column: 32, + endColumn: 40, messageId: "combiningClass", suggestions: null }] @@ -691,8 +708,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{1F476}\\u{1F3FB}]", "u")`, errors: [{ - column: 20, - endColumn: 44, + column: 33, + endColumn: 42, messageId: "emojiModifier", suggestions: null }] @@ -873,8 +890,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{1F1EF}\\u{1F1F5}]", "u")`, errors: [{ - column: 20, - endColumn: 44, + column: 33, + endColumn: 42, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -894,18 +911,17 @@ ruleTester.run("no-misleading-character-class", rule, { messageId: "zwj", suggestions: null }, - { - column: 22, - endColumn: 25, - messageId: "zwj", - suggestions: null - }, { column: 25, endColumn: 27, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👨‍👩‍👦]", "u")` }] }, + { + column: 25, + endColumn: 28, + messageId: "zwj" + }, { column: 28, endColumn: 30, @@ -924,8 +940,8 @@ ruleTester.run("no-misleading-character-class", rule, { suggestions: null }, { - column: 22, - endColumn: 25, + column: 25, + endColumn: 28, messageId: "zwj", suggestions: null } @@ -935,14 +951,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]", "u")`, errors: [ { - column: 20, - endColumn: 80, + column: 37, + endColumn: 43, messageId: "zwj", suggestions: null }, { - column: 20, - endColumn: 80, + column: 58, + endColumn: 64, messageId: "zwj", suggestions: null } @@ -952,14 +968,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`, errors: [ { - column: 20, - endColumn: 72, + column: 33, + endColumn: 41, messageId: "zwj", suggestions: null }, { - column: 20, - endColumn: 72, + column: 52, + endColumn: 60, messageId: "zwj", suggestions: null } @@ -1008,14 +1024,14 @@ ruleTester.run("no-misleading-character-class", rule, { env: { es2020: true }, errors: [ { - column: 31, - endColumn: 83, + column: 44, + endColumn: 52, messageId: "zwj", suggestions: null }, { - column: 31, - endColumn: 83, + column: 63, + endColumn: 71, messageId: "zwj", suggestions: null } From c370130e24b8463faf937e907bfe0e976db64012 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Fri, 13 Oct 2023 11:48:08 -0400 Subject: [PATCH 10/31] Separated kinds into a Set --- lib/rules/no-misleading-character-class.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 0cdc87a65d1..b6f48fae7db 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -144,6 +144,8 @@ const characterSequenceIndexFilters = { const kinds = Object.keys(characterSequenceIndexFilters); +const kindsForSingleCharacterAdjustment = new Set(["combiningClass", "surrogatePairWithoutUFlag"]); + /** * Collects the indices where the filter returns true. * @param {Character[]} chars Characters to run the filter on. @@ -234,10 +236,7 @@ module.exports = { let reportStartOffsetAdjusted = reportStartOffset; if (!char.raw.startsWith("\\u")) { - reportStartOffsetAdjusted -= - ["combiningClass", "surrogatePairWithoutUFlag"].includes(kind) - ? 1 - : 2; + reportStartOffsetAdjusted -= kindsForSingleCharacterAdjustment.has(kind) ? 1 : 2; } if (node.raw.slice(char.start + 1, char.start + 3) === "\\\\") { From 66e472d349e16ff5d181d9b0582a28dcd705c1d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Mon, 16 Oct 2023 15:02:24 -0400 Subject: [PATCH 11/31] Update lib/rules/no-misleading-character-class.js Co-authored-by: Francesco Trotta --- lib/rules/no-misleading-character-class.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index b6f48fae7db..6825feb9a53 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -237,9 +237,7 @@ module.exports = { if (!char.raw.startsWith("\\u")) { reportStartOffsetAdjusted -= kindsForSingleCharacterAdjustment.has(kind) ? 1 : 2; - } - - if (node.raw.slice(char.start + 1, char.start + 3) === "\\\\") { + } else if (node.type !== "Literal" || !node.regex) { reportStartOffsetAdjusted -= 1; } From 1df22bc0713396e96fee847628badd56ca05bcd1 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Mon, 16 Oct 2023 15:05:35 -0400 Subject: [PATCH 12/31] Adjust unit tests for accepted changes --- .../rules/no-misleading-character-class.js | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index 9cad7366966..c5d6b3196b7 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -481,7 +481,7 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = RegExp("[\\uD83D\\uDC4D]", "")`, errors: [{ - column: 26, + column: 25, endColumn: 32, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("[\\uD83D\\uDC4D]", "u")` }] @@ -490,7 +490,7 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "")`, errors: [{ - column: 30, + column: 29, endColumn: 36, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "u")` }] @@ -566,7 +566,7 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "")`, errors: [{ - column: 30, + column: 29, endColumn: 36, messageId: "combiningClass", suggestions: null @@ -575,7 +575,7 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "u")`, errors: [{ - column: 30, + column: 29, endColumn: 36, messageId: "combiningClass", suggestions: null @@ -584,7 +584,7 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{41}\\u{301}]", "u")`, errors: [{ - column: 30, + column: 29, endColumn: 37, messageId: "combiningClass", suggestions: null @@ -636,7 +636,7 @@ ruleTester.run("no-misleading-character-class", rule, { suggestions: null }, { - column: 26, + column: 25, endColumn: 32, messageId: "combiningClass", suggestions: null @@ -646,7 +646,7 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "")`, errors: [{ - column: 30, + column: 29, endColumn: 36, messageId: "combiningClass", suggestions: null @@ -655,7 +655,7 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "u")`, errors: [{ - column: 30, + column: 29, endColumn: 36, messageId: "combiningClass", suggestions: null @@ -664,7 +664,7 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{2747}\\u{FE0F}]", "u")`, errors: [{ - column: 32, + column: 31, endColumn: 40, messageId: "combiningClass", suggestions: null @@ -708,7 +708,7 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{1F476}\\u{1F3FB}]", "u")`, errors: [{ - column: 33, + column: 32, endColumn: 42, messageId: "emojiModifier", suggestions: null @@ -890,7 +890,7 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{1F1EF}\\u{1F1F5}]", "u")`, errors: [{ - column: 33, + column: 32, endColumn: 42, messageId: "regionalIndicatorSymbol", suggestions: null @@ -951,13 +951,13 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]", "u")`, errors: [ { - column: 37, + column: 36, endColumn: 43, messageId: "zwj", suggestions: null }, { - column: 58, + column: 57, endColumn: 64, messageId: "zwj", suggestions: null @@ -968,13 +968,13 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`, errors: [ { - column: 33, + column: 32, endColumn: 41, messageId: "zwj", suggestions: null }, { - column: 52, + column: 51, endColumn: 60, messageId: "zwj", suggestions: null @@ -1024,13 +1024,13 @@ ruleTester.run("no-misleading-character-class", rule, { env: { es2020: true }, errors: [ { - column: 44, + column: 43, endColumn: 52, messageId: "zwj", suggestions: null }, { - column: 63, + column: 62, endColumn: 71, messageId: "zwj", suggestions: null From a54104d24d119406e95843378029609c4a8af777 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Mon, 16 Oct 2023 16:19:02 -0400 Subject: [PATCH 13/31] Split into adjustment function for sequence pairs --- lib/rules/no-misleading-character-class.js | 44 +++++++++++--- .../rules/no-misleading-character-class.js | 60 ++++++++++++++++++- 2 files changed, 93 insertions(+), 11 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 6825feb9a53..a2d533d7d32 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -144,6 +144,8 @@ const characterSequenceIndexFilters = { const kinds = Object.keys(characterSequenceIndexFilters); +const kindsForReportingOnPreviousElement = new Set(["surrogatePair", "surrogatePairWithoutUFlag"]); + const kindsForSingleCharacterAdjustment = new Set(["combiningClass", "surrogatePairWithoutUFlag"]); /** @@ -214,7 +216,7 @@ module.exports = { // If a problematic character exists twice in the string, start each Nth search after N - 1 const searchStart = previousReport && previousReport.kind === kind ? (previousReport.startOffset + 1) - : 0; + : char.start - char.raw.length + 1; const searchableLength = searchStart + char.start + char.raw.length * 2; const offsetStart = node.raw.slice(0, searchableLength).indexOf(char.raw, searchStart); @@ -222,6 +224,38 @@ module.exports = { return offsetStart === -1 ? null : offsetStart; } + /** + * Determines where a report on a character should truly start. + * @param {number} reportStartOffset Original estimate for where the character is. + * @param {Character} char Individual character being reported on. + * @param {string} kind What kind of character report is being made. + * @param {Node} node Parent string node to report within. + * @returns {number} Adjusted start location for a character's report. + */ + function getReportStartOffsetAdjusted(reportStartOffset, char, kind, node) { + + // Characters that aren't started by manual \u encodings are only adjusted by 1 or 2 + if (!char.raw.startsWith("\\u")) { + return reportStartOffset - (kindsForSingleCharacterAdjustment.has(kind) ? 1 : 2); + } + + // If the character is the second of a joined pair, report on the previous character's start + if (kindsForReportingOnPreviousElement.has(kind) && char.raw.startsWith("\\u")) { + const previousElement = char.parent.elements[char.parent.elements.indexOf(char) - 1]; + const doubleBackslashMatches = node.raw.slice(0, char.start).match(/\\\\/gu); + + return previousElement.start + (doubleBackslashMatches ? doubleBackslashMatches.length : 1); + } + + // Otherwise, regex literal nodes require no offsets + if (node.type === "Literal" && node.regex) { + return reportStartOffset; + } + + // Other nodes are quoted strings: offset by 1 for their starting quote or backtick + return reportStartOffset - 1; + } + /** * Generates a granular loc for context.report. * @param {Character} char Individual character being reported on. @@ -233,13 +267,7 @@ module.exports = { */ function generateReportLocation(char, kind, node, reportStartOffset) { const reportEndOffset = reportStartOffset + char.raw.length; - let reportStartOffsetAdjusted = reportStartOffset; - - if (!char.raw.startsWith("\\u")) { - reportStartOffsetAdjusted -= kindsForSingleCharacterAdjustment.has(kind) ? 1 : 2; - } else if (node.type !== "Literal" || !node.regex) { - reportStartOffsetAdjusted -= 1; - } + const reportStartOffsetAdjusted = getReportStartOffsetAdjusted(reportStartOffset, char, kind, node); return { end: { diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index c5d6b3196b7..67f7f818e9f 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -96,12 +96,39 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83D\\uDC4D]/", errors: [{ - column: 17, + column: 11, endColumn: 23, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[\\uD83D\\uDC4D]/u" }] }] }, + { + code: "var r = /before[\\uD83D\\uDC4D]after/", + errors: [{ + column: 17, + endColumn: 29, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /before[\\uD83D\\uDC4D]after/u" }] + }] + }, + { + code: "var r = /[before\\uD83D\\uDC4Dafter]/", + errors: [{ + column: 17, + endColumn: 29, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[before\\uD83D\\uDC4Dafter]/u" }] + }] + }, + { + code: "var r = /\\uDC4D[\\uD83D\\uDC4D]/", + errors: [{ + column: 17, + endColumn: 29, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /\\uDC4D[\\uD83D\\uDC4D]/u" }] + }] + }, { code: "var r = /[👍]/", parserOptions: { ecmaVersion: 3 }, @@ -481,21 +508,48 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = RegExp("[\\uD83D\\uDC4D]", "")`, errors: [{ - column: 25, + column: 18, endColumn: 32, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("[\\uD83D\\uDC4D]", "u")` }] }] }, + { + code: String.raw`var r = RegExp("before[\\uD83D\\uDC4D]after", "")`, + errors: [{ + column: 24, + endColumn: 38, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("before[\\uD83D\\uDC4D]after", "u")` }] + }] + }, + { + code: String.raw`var r = RegExp("[before\\uD83D\\uDC4Dafter]", "")`, + errors: [{ + column: 24, + endColumn: 38, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("[before\\uD83D\\uDC4Dafter]", "u")` }] + }] + }, { code: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "")`, errors: [{ - column: 29, + column: 22, endColumn: 36, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "u")` }] }] }, + { + code: String.raw`var r = new RegExp("\\uDC4D[\\uD83D\\uDC4D]", "")`, + errors: [{ + column: 29, + endColumn: 43, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("\\uDC4D[\\uD83D\\uDC4D]", "u")` }] + }] + }, { code: String.raw`var r = new RegExp("[👍]", "")`, parserOptions: { ecmaVersion: 3 }, From 11b0abf7f8ea70d0e1fdeb1d3f231ff7ff50b492 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 1 Nov 2023 08:58:30 -0400 Subject: [PATCH 14/31] Reduced complexity because of edge cases, as reqiested --- lib/rules/no-misleading-character-class.js | 92 ++------ .../rules/no-misleading-character-class.js | 208 ++++++++++-------- 2 files changed, 136 insertions(+), 164 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index a2d533d7d32..c514ec7a277 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -144,10 +144,6 @@ const characterSequenceIndexFilters = { const kinds = Object.keys(characterSequenceIndexFilters); -const kindsForReportingOnPreviousElement = new Set(["surrogatePair", "surrogatePairWithoutUFlag"]); - -const kindsForSingleCharacterAdjustment = new Set(["combiningClass", "surrogatePairWithoutUFlag"]); - /** * Collects the indices where the filter returns true. * @param {Character[]} chars Characters to run the filter on. @@ -199,84 +195,34 @@ module.exports = { const sourceCode = context.sourceCode; const parser = new RegExpParser(); - /** - * Determines what offset to start a granular report, if it's straightforward to do so. - * @param {Character} char Individual character being reported on. - * @param {string} kind What kind of character report is being made. - * @param {Node} node Parent string node to report within. - * @param {Object} previousReport Kind and start offset for the previous report, if there was one. - * @returns {Object | null} Report offset, or null to report on the whole node. - * @see https://github.com/eslint/eslint/pull/17515#issuecomment-1707697186 - */ - function getReportStartOffset(char, kind, node, previousReport = {}) { - if (node.type !== "Literal" && (node.type !== "TemplateLiteral" || node.expressions.length)) { - return null; - } - - // If a problematic character exists twice in the string, start each Nth search after N - 1 - const searchStart = previousReport && previousReport.kind === kind - ? (previousReport.startOffset + 1) - : char.start - char.raw.length + 1; - - const searchableLength = searchStart + char.start + char.raw.length * 2; - const offsetStart = node.raw.slice(0, searchableLength).indexOf(char.raw, searchStart); - - return offsetStart === -1 ? null : offsetStart; - } - - /** - * Determines where a report on a character should truly start. - * @param {number} reportStartOffset Original estimate for where the character is. - * @param {Character} char Individual character being reported on. - * @param {string} kind What kind of character report is being made. - * @param {Node} node Parent string node to report within. - * @returns {number} Adjusted start location for a character's report. - */ - function getReportStartOffsetAdjusted(reportStartOffset, char, kind, node) { - - // Characters that aren't started by manual \u encodings are only adjusted by 1 or 2 - if (!char.raw.startsWith("\\u")) { - return reportStartOffset - (kindsForSingleCharacterAdjustment.has(kind) ? 1 : 2); - } - - // If the character is the second of a joined pair, report on the previous character's start - if (kindsForReportingOnPreviousElement.has(kind) && char.raw.startsWith("\\u")) { - const previousElement = char.parent.elements[char.parent.elements.indexOf(char) - 1]; - const doubleBackslashMatches = node.raw.slice(0, char.start).match(/\\\\/gu); - - return previousElement.start + (doubleBackslashMatches ? doubleBackslashMatches.length : 1); - } - - // Otherwise, regex literal nodes require no offsets - if (node.type === "Literal" && node.regex) { - return reportStartOffset; - } - - // Other nodes are quoted strings: offset by 1 for their starting quote or backtick - return reportStartOffset - 1; - } - /** * Generates a granular loc for context.report. * @param {Character} char Individual character being reported on. * @param {string} kind What kind of character report is being made. * @param {Node} node Parent string node to report within. - * @param {number} reportStartOffset Starting offset to report within the node. * @returns {Object} Granular loc for context.report. * @see https://github.com/eslint/eslint/pull/17515#issuecomment-1707697186 */ - function generateReportLocation(char, kind, node, reportStartOffset) { - const reportEndOffset = reportStartOffset + char.raw.length; - const reportStartOffsetAdjusted = getReportStartOffsetAdjusted(reportStartOffset, char, kind, node); + function generateReportLocation(char, kind, node) { + if (node.type === "TemplateLiteral" && node.expressions.length || sourceCode.getText(node).includes("\\")) { + return node.loc; + } + + const kindsForEarlierReport = new Set([ + "emojiModifier", + "zwj", + node.type === "Literal" && "regionalIndicatorSymbol" + ]); + const reportStartOffset = kindsForEarlierReport.has(kind) ? -1 : 0; return { end: { line: node.loc.end.line, - column: node.loc.start.column + reportEndOffset + column: node.loc.start.column + char.end + 1 }, start: { line: node.loc.start.line, - column: node.loc.start.column + reportStartOffsetAdjusted + column: node.loc.start.column + char.start + reportStartOffset } }; } @@ -324,8 +270,6 @@ module.exports = { } }); - let previousReport; - for (const { chars, kind } of foundKinds) { let suggest; @@ -337,19 +281,11 @@ module.exports = { } for (const char of chars) { - const reportStartOffset = getReportStartOffset(char, kind, node, previousReport); - context.report({ - ...( - reportStartOffset - ? { loc: generateReportLocation(char, kind, node, reportStartOffset) } - : { node } - ), + loc: generateReportLocation(char, kind, node), messageId: kind, suggest }); - - previousReport = { kind, startOffset: reportStartOffset }; } } } diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index 67f7f818e9f..40e20726b87 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -96,17 +96,26 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83D\\uDC4D]/", errors: [{ - column: 11, - endColumn: 23, + column: 9, + endColumn: 25, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[\\uD83D\\uDC4D]/u" }] }] }, + { + code: "var r = /[\\uD83D\\uDC4D-\\uffff]/", + errors: [{ + column: 9, + endColumn: 32, + messageId: "surrogatePairWithoutUFlag", + suggestions: null // pattern would be invalid with the 'u' flag + }] + }, { code: "var r = /before[\\uD83D\\uDC4D]after/", errors: [{ - column: 17, - endColumn: 29, + column: 9, + endColumn: 36, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /before[\\uD83D\\uDC4D]after/u" }] }] @@ -114,8 +123,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[before\\uD83D\\uDC4Dafter]/", errors: [{ - column: 17, - endColumn: 29, + column: 9, + endColumn: 36, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[before\\uD83D\\uDC4Dafter]/u" }] }] @@ -123,8 +132,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /\\uDC4D[\\uD83D\\uDC4D]/", errors: [{ - column: 17, - endColumn: 29, + column: 9, + endColumn: 31, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /\\uDC4D[\\uD83D\\uDC4D]/u" }] }] @@ -152,8 +161,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[👍]\\a/", errors: [{ - column: 11, - endColumn: 13, + column: 9, + endColumn: 17, messageId: "surrogatePairWithoutUFlag", suggestions: null // pattern would be invalid with the 'u' flag }] @@ -199,8 +208,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u0041\\u0301]/", errors: [{ - column: 17, - endColumn: 23, + column: 9, + endColumn: 25, messageId: "combiningClass", suggestions: null }] @@ -208,8 +217,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u0041\\u0301]/u", errors: [{ - column: 17, - endColumn: 23, + column: 9, + endColumn: 26, messageId: "combiningClass", suggestions: null }] @@ -217,8 +226,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{41}\\u{301}]/u", errors: [{ - column: 17, - endColumn: 24, + column: 9, + endColumn: 27, messageId: "combiningClass", suggestions: null }] @@ -244,8 +253,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u2747\\uFE0F]/", errors: [{ - column: 17, - endColumn: 23, + column: 9, + endColumn: 25, messageId: "combiningClass", suggestions: null }] @@ -253,8 +262,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u2747\\uFE0F]/u", errors: [{ - column: 17, - endColumn: 23, + column: 9, + endColumn: 26, messageId: "combiningClass", suggestions: null }] @@ -262,8 +271,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{2747}\\u{FE0F}]/u", errors: [{ - column: 19, - endColumn: 27, + column: 9, + endColumn: 30, messageId: "combiningClass", suggestions: null }] @@ -297,8 +306,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83D\\uDC76\\uD83C\\uDFFB]/u", errors: [{ - column: 23, - endColumn: 35, + column: 9, + endColumn: 38, messageId: "emojiModifier", suggestions: null }] @@ -306,8 +315,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{1F476}\\u{1F3FB}]/u", errors: [{ - column: 20, - endColumn: 29, + column: 9, + endColumn: 32, messageId: "emojiModifier", suggestions: null }] @@ -358,8 +367,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83C\\uDDEF\\uD83C\\uDDF5]/u", errors: [{ - column: 23, - endColumn: 35, + column: 9, + endColumn: 38, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -367,8 +376,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{1F1EF}\\u{1F1F5}]/u", errors: [{ - column: 20, - endColumn: 29, + column: 9, + endColumn: 32, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -428,14 +437,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]/u", errors: [ { - column: 23, - endColumn: 29, + column: 9, + endColumn: 62, messageId: "zwj", suggestions: null }, { - column: 41, - endColumn: 47, + column: 9, + endColumn: 62, messageId: "zwj", suggestions: null } @@ -445,14 +454,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]/u", errors: [ { - column: 20, - endColumn: 28, + column: 9, + endColumn: 57, messageId: "zwj", suggestions: null }, { - column: 37, - endColumn: 45, + column: 9, + endColumn: 57, messageId: "zwj", suggestions: null } @@ -508,8 +517,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = RegExp("[\\uD83D\\uDC4D]", "")`, errors: [{ - column: 18, - endColumn: 32, + column: 16, + endColumn: 34, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("[\\uD83D\\uDC4D]", "u")` }] }] @@ -517,8 +526,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = RegExp("before[\\uD83D\\uDC4D]after", "")`, errors: [{ - column: 24, - endColumn: 38, + column: 16, + endColumn: 45, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("before[\\uD83D\\uDC4D]after", "u")` }] }] @@ -526,8 +535,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = RegExp("[before\\uD83D\\uDC4Dafter]", "")`, errors: [{ - column: 24, - endColumn: 38, + column: 16, + endColumn: 45, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("[before\\uD83D\\uDC4Dafter]", "u")` }] }] @@ -535,8 +544,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "")`, errors: [{ - column: 22, - endColumn: 36, + column: 20, + endColumn: 38, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "u")` }] }] @@ -544,12 +553,39 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("\\uDC4D[\\uD83D\\uDC4D]", "")`, errors: [{ - column: 29, - endColumn: 43, + column: 20, + endColumn: 45, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("\\uDC4D[\\uD83D\\uDC4D]", "u")` }] }] }, + { + code: String.raw`var r = new RegExp("\u1234[\\uD83D\\uDC4D]")`, + errors: [{ + column: 20, + endColumn: 44, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("\u1234[\\uD83D\\uDC4D]", "u")` }] + }] + }, + { + code: String.raw`var r = new RegExp("\\u1234\\u5678👎[👍]")`, + errors: [{ + column: 20, + endColumn: 42, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("\\u1234\\u5678👎[👍]", "u")` }] + }] + }, + { + code: String.raw`var r = new RegExp("\\u1234\\u5678👍[👍]")`, + errors: [{ + column: 20, + endColumn: 42, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("\\u1234\\u5678👍[👍]", "u")` }] + }] + }, { code: String.raw`var r = new RegExp("[👍]", "")`, parserOptions: { ecmaVersion: 3 }, @@ -573,8 +609,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[👍]\\a", "")`, errors: [{ - column: 22, - endColumn: 24, + column: 20, + endColumn: 29, messageId: "surrogatePairWithoutUFlag", suggestions: null // pattern would be invalid with the 'u' flag }] @@ -620,8 +656,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "")`, errors: [{ - column: 29, - endColumn: 36, + column: 20, + endColumn: 38, messageId: "combiningClass", suggestions: null }] @@ -629,8 +665,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "u")`, errors: [{ - column: 29, - endColumn: 36, + column: 20, + endColumn: 38, messageId: "combiningClass", suggestions: null }] @@ -638,8 +674,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{41}\\u{301}]", "u")`, errors: [{ - column: 29, - endColumn: 37, + column: 20, + endColumn: 39, messageId: "combiningClass", suggestions: null }] @@ -665,8 +701,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`new RegExp("[ \\ufe0f]", "")`, errors: [{ - column: 15, - endColumn: 22, + column: 12, + endColumn: 24, messageId: "combiningClass", suggestions: null }] @@ -674,8 +710,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`new RegExp("[ \\ufe0f]", "u")`, errors: [{ - column: 15, - endColumn: 22, + column: 12, + endColumn: 24, messageId: "combiningClass", suggestions: null }] @@ -684,14 +720,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`new RegExp("[ \\ufe0f][ \\ufe0f]")`, errors: [ { - column: 15, - endColumn: 22, + column: 12, + endColumn: 34, messageId: "combiningClass", suggestions: null }, { - column: 25, - endColumn: 32, + column: 12, + endColumn: 34, messageId: "combiningClass", suggestions: null } @@ -700,8 +736,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "")`, errors: [{ - column: 29, - endColumn: 36, + column: 20, + endColumn: 38, messageId: "combiningClass", suggestions: null }] @@ -709,8 +745,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "u")`, errors: [{ - column: 29, - endColumn: 36, + column: 20, + endColumn: 38, messageId: "combiningClass", suggestions: null }] @@ -718,8 +754,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{2747}\\u{FE0F}]", "u")`, errors: [{ - column: 31, - endColumn: 40, + column: 20, + endColumn: 42, messageId: "combiningClass", suggestions: null }] @@ -762,8 +798,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{1F476}\\u{1F3FB}]", "u")`, errors: [{ - column: 32, - endColumn: 42, + column: 20, + endColumn: 44, messageId: "emojiModifier", suggestions: null }] @@ -944,8 +980,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{1F1EF}\\u{1F1F5}]", "u")`, errors: [{ - column: 32, - endColumn: 42, + column: 20, + endColumn: 44, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -1005,14 +1041,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]", "u")`, errors: [ { - column: 36, - endColumn: 43, + column: 20, + endColumn: 80, messageId: "zwj", suggestions: null }, { - column: 57, - endColumn: 64, + column: 20, + endColumn: 80, messageId: "zwj", suggestions: null } @@ -1022,14 +1058,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`, errors: [ { - column: 32, - endColumn: 41, + column: 20, + endColumn: 72, messageId: "zwj", suggestions: null }, { - column: 51, - endColumn: 60, + column: 20, + endColumn: 72, messageId: "zwj", suggestions: null } @@ -1078,14 +1114,14 @@ ruleTester.run("no-misleading-character-class", rule, { env: { es2020: true }, errors: [ { - column: 43, - endColumn: 52, + column: 31, + endColumn: 83, messageId: "zwj", suggestions: null }, { - column: 62, - endColumn: 71, + column: 31, + endColumn: 83, messageId: "zwj", suggestions: null } From 59845bdb89eaf23ec028b2309fb6ef1dc1d895da Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 4 Nov 2023 16:09:52 -0400 Subject: [PATCH 15/31] Use chars array as suggested - mostly there --- lib/rules/no-misleading-character-class.js | 137 ++++++++----- .../rules/no-misleading-character-class.js | 188 +++++++++--------- 2 files changed, 183 insertions(+), 142 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index c514ec7a277..2bf5f909340 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -62,6 +62,16 @@ function *iterateCharacterSequence(nodes) { } } +/** + * Counts how many \\\\s appear in text. + * @param {string} text Text to count within. + * @returns {number} How many \\\\s were found. + */ +function countDoubleEscapedBackslashes(text) { + const matches = text.match(/\\\\/gu); + + return matches ? matches.length : 0; +} /** * Checks whether the given character node is a Unicode code point escape or not. @@ -73,72 +83,96 @@ function isUnicodeCodePointEscape(char) { } /** - * Each function returns `true` if it detects that kind of problem. - * @type {Record boolean>} + * Each function returns matched characters if it detects that kind of problem. + * @type {Record Character[] | null>} */ const characterSequenceIndexFilters = { surrogatePairWithoutUFlag(char, index, chars) { if (index === 0) { - return false; + return null; } - const c1 = chars[index - 1]; - return ( - isSurrogatePair(c1.value, char.value) && - !isUnicodeCodePointEscape(c1) && + const previous = chars[index - 1]; + + if ( + isSurrogatePair(previous.value, char.value) && + !isUnicodeCodePointEscape(previous) && !isUnicodeCodePointEscape(char) - ); + ) { + return [previous, char]; + } + + return null; }, surrogatePair(char, index, chars) { if (index === 0) { - return false; + return null; } - const c1 = chars[index - 1]; - return ( - isSurrogatePair(c1.value, char.value) && + const previous = chars[index - 1]; + + if ( + isSurrogatePair(previous.value, char.value) && ( - isUnicodeCodePointEscape(c1) || + isUnicodeCodePointEscape(previous) || isUnicodeCodePointEscape(char) ) - ); + ) { + return [previous, char]; + } + + return null; }, combiningClass(char, index, chars) { - return ( + if ( index !== 0 && isCombiningCharacter(char.value) && !isCombiningCharacter(chars[index - 1].value) - ); + ) { + return [chars[index - 1], char]; + } + + return null; }, emojiModifier(char, index, chars) { - return ( + if ( index !== 0 && isEmojiModifier(char.value) && !isEmojiModifier(chars[index - 1].value) - ); + ) { + return [chars[index - 1], char]; + } + + return null; }, regionalIndicatorSymbol(char, index, chars) { - return ( + if ( index !== 0 && isRegionalIndicatorSymbol(char.value) && isRegionalIndicatorSymbol(chars[index - 1].value) - ); + ) { + return [chars[index - 1], char]; + } + + return null; }, zwj(char, index, chars) { - const lastIndex = chars.length - 1; - - return ( + if ( index !== 0 && - index !== lastIndex && + index !== chars.length - 1 && char.value === 0x200d && chars[index - 1].value !== 0x200d && chars[index + 1].value !== 0x200d - ); + ) { + return chars.slice(index - 1, index + 1); + } + + return null; } }; @@ -147,15 +181,17 @@ const kinds = Object.keys(characterSequenceIndexFilters); /** * Collects the indices where the filter returns true. * @param {Character[]} chars Characters to run the filter on. - * @param {(char: Character, index: number, chars: Character[]) => boolean} filter Determines whether an index should be returned. - * @returns {number[]} Indices where the filter returned true. + * @param {(char: Character, index: number, chars: Character[]) => Character[] | null} filter Finds matches for an index. + * @returns {Character[][]} Indices where the filter returned true. */ function accumulate(chars, filter) { const matchingChars = []; chars.forEach((char, index) => { - if (filter(char, index, chars)) { - matchingChars.push(char); + const matches = filter(char, index, chars); + + if (matches) { + matchingChars.push(matches); } }); @@ -197,32 +233,37 @@ module.exports = { /** * Generates a granular loc for context.report. - * @param {Character} char Individual character being reported on. - * @param {string} kind What kind of character report is being made. + * @param {Character[]} chars Individual characters being reported on. * @param {Node} node Parent string node to report within. * @returns {Object} Granular loc for context.report. * @see https://github.com/eslint/eslint/pull/17515#issuecomment-1707697186 */ - function generateReportLocation(char, kind, node) { - if (node.type === "TemplateLiteral" && node.expressions.length || sourceCode.getText(node).includes("\\")) { + function generateReportLocation(chars, node) { + if (node.type !== "Literal" && (node.type !== "TemplateLiteral" || node.expressions.length)) { return node.loc; } - const kindsForEarlierReport = new Set([ - "emojiModifier", - "zwj", - node.type === "Literal" && "regionalIndicatorSymbol" - ]); - const reportStartOffset = kindsForEarlierReport.has(kind) ? -1 : 0; + const first = chars[0]; + const last = chars.at(-1); + + const reportStartOffset = countDoubleEscapedBackslashes(node.raw.slice(0, first.start)); + const reportEndOffset = countDoubleEscapedBackslashes(node.raw.slice(first.start, last.end)); + + // eslint-disable-next-line unicorn/prefer-set-has -- bug in the lint rule, this is a string not an array + const approximateRegion = node.raw.slice(first.start, last.end + reportEndOffset + last.parent.raw.length); + + if (!approximateRegion.includes(first.raw) || !approximateRegion.includes(last.raw)) { + return node.loc; + } return { end: { line: node.loc.end.line, - column: node.loc.start.column + char.end + 1 + column: node.loc.start.column + last.end + 1 + reportEndOffset + reportStartOffset }, start: { line: node.loc.start.line, - column: node.loc.start.column + char.start + reportStartOffset + column: node.loc.start.column + first.start + 1 + reportStartOffset } }; } @@ -254,23 +295,23 @@ module.exports = { return; } - const foundKinds = []; + const foundKindMatches = []; visitRegExpAST(patternNode, { onCharacterClassEnter(ccNode) { for (const chars of iterateCharacterSequence(ccNode.elements)) { for (const kind of kinds) { - const matchingChars = accumulate(chars, characterSequenceIndexFilters[kind]); + const matches = accumulate(chars, characterSequenceIndexFilters[kind]); - if (matchingChars.length) { - foundKinds.push({ chars: matchingChars, kind }); + if (matches.length) { + foundKindMatches.push({ kind, matches }); } } } } }); - for (const { chars, kind } of foundKinds) { + for (const { kind, matches } of foundKindMatches) { let suggest; if (kind === "surrogatePairWithoutUFlag") { @@ -280,9 +321,9 @@ module.exports = { }]; } - for (const char of chars) { + for (const chars of matches) { context.report({ - loc: generateReportLocation(char, kind, node), + loc: generateReportLocation(chars, node), messageId: kind, suggest }); diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index 40e20726b87..d3f5830fad9 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -96,8 +96,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83D\\uDC4D]/", errors: [{ - column: 9, - endColumn: 25, + column: 11, + endColumn: 23, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[\\uD83D\\uDC4D]/u" }] }] @@ -105,8 +105,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83D\\uDC4D-\\uffff]/", errors: [{ - column: 9, - endColumn: 32, + column: 11, + endColumn: 23, messageId: "surrogatePairWithoutUFlag", suggestions: null // pattern would be invalid with the 'u' flag }] @@ -114,8 +114,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /before[\\uD83D\\uDC4D]after/", errors: [{ - column: 9, - endColumn: 36, + column: 17, + endColumn: 29, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /before[\\uD83D\\uDC4D]after/u" }] }] @@ -123,8 +123,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[before\\uD83D\\uDC4Dafter]/", errors: [{ - column: 9, - endColumn: 36, + column: 17, + endColumn: 29, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[before\\uD83D\\uDC4Dafter]/u" }] }] @@ -132,8 +132,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /\\uDC4D[\\uD83D\\uDC4D]/", errors: [{ - column: 9, - endColumn: 31, + column: 17, + endColumn: 29, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /\\uDC4D[\\uD83D\\uDC4D]/u" }] }] @@ -161,8 +161,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[👍]\\a/", errors: [{ - column: 9, - endColumn: 17, + column: 11, + endColumn: 13, messageId: "surrogatePairWithoutUFlag", suggestions: null // pattern would be invalid with the 'u' flag }] @@ -208,8 +208,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u0041\\u0301]/", errors: [{ - column: 9, - endColumn: 25, + column: 11, + endColumn: 23, messageId: "combiningClass", suggestions: null }] @@ -217,8 +217,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u0041\\u0301]/u", errors: [{ - column: 9, - endColumn: 26, + column: 11, + endColumn: 23, messageId: "combiningClass", suggestions: null }] @@ -226,8 +226,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{41}\\u{301}]/u", errors: [{ - column: 9, - endColumn: 27, + column: 11, + endColumn: 24, messageId: "combiningClass", suggestions: null }] @@ -253,8 +253,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u2747\\uFE0F]/", errors: [{ - column: 9, - endColumn: 25, + column: 11, + endColumn: 23, messageId: "combiningClass", suggestions: null }] @@ -262,8 +262,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u2747\\uFE0F]/u", errors: [{ - column: 9, - endColumn: 26, + column: 11, + endColumn: 23, messageId: "combiningClass", suggestions: null }] @@ -271,8 +271,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{2747}\\u{FE0F}]/u", errors: [{ - column: 9, - endColumn: 30, + column: 11, + endColumn: 27, messageId: "combiningClass", suggestions: null }] @@ -306,8 +306,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83D\\uDC76\\uD83C\\uDFFB]/u", errors: [{ - column: 9, - endColumn: 38, + column: 11, + endColumn: 35, messageId: "emojiModifier", suggestions: null }] @@ -315,8 +315,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{1F476}\\u{1F3FB}]/u", errors: [{ - column: 9, - endColumn: 32, + column: 11, + endColumn: 29, messageId: "emojiModifier", suggestions: null }] @@ -367,8 +367,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83C\\uDDEF\\uD83C\\uDDF5]/u", errors: [{ - column: 9, - endColumn: 38, + column: 11, + endColumn: 35, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -376,8 +376,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{1F1EF}\\u{1F1F5}]/u", errors: [{ - column: 9, - endColumn: 32, + column: 11, + endColumn: 29, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -392,7 +392,7 @@ ruleTester.run("no-misleading-character-class", rule, { suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }] }, { - column: 11, + column: 12, endColumn: 14, messageId: "zwj", suggestions: null @@ -404,7 +404,7 @@ ruleTester.run("no-misleading-character-class", rule, { suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }] }, { - column: 14, + column: 15, endColumn: 17, messageId: "zwj" }, @@ -437,14 +437,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]/u", errors: [ { - column: 9, - endColumn: 62, + column: 11, + endColumn: 29, messageId: "zwj", suggestions: null }, { - column: 9, - endColumn: 62, + column: 29, + endColumn: 47, messageId: "zwj", suggestions: null } @@ -454,14 +454,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]/u", errors: [ { - column: 9, - endColumn: 57, + column: 11, + endColumn: 28, messageId: "zwj", suggestions: null }, { - column: 9, - endColumn: 57, + column: 28, + endColumn: 45, messageId: "zwj", suggestions: null } @@ -517,8 +517,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = RegExp("[\\uD83D\\uDC4D]", "")`, errors: [{ - column: 16, - endColumn: 34, + column: 18, + endColumn: 32, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("[\\uD83D\\uDC4D]", "u")` }] }] @@ -526,8 +526,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = RegExp("before[\\uD83D\\uDC4D]after", "")`, errors: [{ - column: 16, - endColumn: 45, + column: 24, + endColumn: 38, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("before[\\uD83D\\uDC4D]after", "u")` }] }] @@ -535,8 +535,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = RegExp("[before\\uD83D\\uDC4Dafter]", "")`, errors: [{ - column: 16, - endColumn: 45, + column: 24, + endColumn: 38, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("[before\\uD83D\\uDC4Dafter]", "u")` }] }] @@ -544,8 +544,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "")`, errors: [{ - column: 20, - endColumn: 38, + column: 22, + endColumn: 36, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "u")` }] }] @@ -553,8 +553,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("\\uDC4D[\\uD83D\\uDC4D]", "")`, errors: [{ - column: 20, - endColumn: 45, + column: 29, + endColumn: 43, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("\\uDC4D[\\uD83D\\uDC4D]", "u")` }] }] @@ -562,8 +562,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("\u1234[\\uD83D\\uDC4D]")`, errors: [{ - column: 20, - endColumn: 44, + column: 23, + endColumn: 36, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("\u1234[\\uD83D\\uDC4D]", "u")` }] }] @@ -571,8 +571,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("\\u1234\\u5678👎[👍]")`, errors: [{ - column: 20, - endColumn: 42, + column: 38, + endColumn: 40, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("\\u1234\\u5678👎[👍]", "u")` }] }] @@ -580,8 +580,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("\\u1234\\u5678👍[👍]")`, errors: [{ - column: 20, - endColumn: 42, + column: 38, + endColumn: 40, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("\\u1234\\u5678👍[👍]", "u")` }] }] @@ -609,8 +609,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[👍]\\a", "")`, errors: [{ - column: 20, - endColumn: 29, + column: 22, + endColumn: 24, messageId: "surrogatePairWithoutUFlag", suggestions: null // pattern would be invalid with the 'u' flag }] @@ -656,8 +656,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "")`, errors: [{ - column: 20, - endColumn: 38, + column: 22, + endColumn: 36, messageId: "combiningClass", suggestions: null }] @@ -665,8 +665,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "u")`, errors: [{ - column: 20, - endColumn: 38, + column: 22, + endColumn: 36, messageId: "combiningClass", suggestions: null }] @@ -674,8 +674,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{41}\\u{301}]", "u")`, errors: [{ - column: 20, - endColumn: 39, + column: 22, + endColumn: 37, messageId: "combiningClass", suggestions: null }] @@ -701,8 +701,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`new RegExp("[ \\ufe0f]", "")`, errors: [{ - column: 12, - endColumn: 24, + column: 14, + endColumn: 22, messageId: "combiningClass", suggestions: null }] @@ -710,8 +710,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`new RegExp("[ \\ufe0f]", "u")`, errors: [{ - column: 12, - endColumn: 24, + column: 14, + endColumn: 22, messageId: "combiningClass", suggestions: null }] @@ -720,14 +720,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`new RegExp("[ \\ufe0f][ \\ufe0f]")`, errors: [ { - column: 12, - endColumn: 34, + column: 14, + endColumn: 22, messageId: "combiningClass", suggestions: null }, { - column: 12, - endColumn: 34, + column: 24, + endColumn: 32, messageId: "combiningClass", suggestions: null } @@ -736,8 +736,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "")`, errors: [{ - column: 20, - endColumn: 38, + column: 22, + endColumn: 36, messageId: "combiningClass", suggestions: null }] @@ -745,8 +745,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "u")`, errors: [{ - column: 20, - endColumn: 38, + column: 22, + endColumn: 36, messageId: "combiningClass", suggestions: null }] @@ -754,8 +754,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{2747}\\u{FE0F}]", "u")`, errors: [{ - column: 20, - endColumn: 42, + column: 22, + endColumn: 40, messageId: "combiningClass", suggestions: null }] @@ -798,8 +798,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{1F476}\\u{1F3FB}]", "u")`, errors: [{ - column: 20, - endColumn: 44, + column: 22, + endColumn: 42, messageId: "emojiModifier", suggestions: null }] @@ -980,8 +980,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{1F1EF}\\u{1F1F5}]", "u")`, errors: [{ - column: 20, - endColumn: 44, + column: 22, + endColumn: 42, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -996,7 +996,7 @@ ruleTester.run("no-misleading-character-class", rule, { suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👨‍👩‍👦]", "u")` }] }, { - column: 22, + column: 23, endColumn: 25, messageId: "zwj", suggestions: null @@ -1008,7 +1008,7 @@ ruleTester.run("no-misleading-character-class", rule, { suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👨‍👩‍👦]", "u")` }] }, { - column: 25, + column: 26, endColumn: 28, messageId: "zwj" }, @@ -1058,14 +1058,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`, errors: [ { - column: 20, - endColumn: 72, + column: 22, + endColumn: 41, messageId: "zwj", suggestions: null }, { - column: 20, - endColumn: 72, + column: 41, + endColumn: 60, messageId: "zwj", suggestions: null } @@ -1114,14 +1114,14 @@ ruleTester.run("no-misleading-character-class", rule, { env: { es2020: true }, errors: [ { - column: 31, - endColumn: 83, + column: 33, + endColumn: 52, messageId: "zwj", suggestions: null }, { - column: 31, - endColumn: 83, + column: 52, + endColumn: 71, messageId: "zwj", suggestions: null } From a7c210722b672ea0373d386176857830233bcdd0 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 4 Nov 2023 16:27:33 -0400 Subject: [PATCH 16/31] Checked that slices include first and last --- lib/rules/no-misleading-character-class.js | 14 ++-- .../rules/no-misleading-character-class.js | 72 +++++++++---------- 2 files changed, 44 insertions(+), 42 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 2bf5f909340..999b3f1ac65 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -236,7 +236,7 @@ module.exports = { * @param {Character[]} chars Individual characters being reported on. * @param {Node} node Parent string node to report within. * @returns {Object} Granular loc for context.report. - * @see https://github.com/eslint/eslint/pull/17515#issuecomment-1707697186 + * @see https://github.com/eslint/eslint/pull/17515 */ function generateReportLocation(chars, node) { if (node.type !== "Literal" && (node.type !== "TemplateLiteral" || node.expressions.length)) { @@ -244,15 +244,17 @@ module.exports = { } const first = chars[0]; - const last = chars.at(-1); + const last = chars[chars.length - 1]; const reportStartOffset = countDoubleEscapedBackslashes(node.raw.slice(0, first.start)); - const reportEndOffset = countDoubleEscapedBackslashes(node.raw.slice(first.start, last.end)); - // eslint-disable-next-line unicorn/prefer-set-has -- bug in the lint rule, this is a string not an array - const approximateRegion = node.raw.slice(first.start, last.end + reportEndOffset + last.parent.raw.length); + if (!node.raw.slice(first.start, first.end + 1 + reportStartOffset).includes(first.raw)) { + return node.loc; + } + + const reportEndOffset = countDoubleEscapedBackslashes(node.raw.slice(first.start, last.end)); - if (!approximateRegion.includes(first.raw) || !approximateRegion.includes(last.raw)) { + if (!node.raw.slice(last.start, last.end + last.parent.raw.length + reportEndOffset).includes(last.raw)) { return node.loc; } diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index d3f5830fad9..100f1cb184b 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -517,8 +517,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = RegExp("[\\uD83D\\uDC4D]", "")`, errors: [{ - column: 18, - endColumn: 32, + column: 16, + endColumn: 34, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("[\\uD83D\\uDC4D]", "u")` }] }] @@ -526,8 +526,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = RegExp("before[\\uD83D\\uDC4D]after", "")`, errors: [{ - column: 24, - endColumn: 38, + column: 16, + endColumn: 45, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("before[\\uD83D\\uDC4D]after", "u")` }] }] @@ -535,8 +535,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = RegExp("[before\\uD83D\\uDC4Dafter]", "")`, errors: [{ - column: 24, - endColumn: 38, + column: 16, + endColumn: 45, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("[before\\uD83D\\uDC4Dafter]", "u")` }] }] @@ -544,8 +544,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "")`, errors: [{ - column: 22, - endColumn: 36, + column: 20, + endColumn: 38, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "u")` }] }] @@ -553,8 +553,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("\\uDC4D[\\uD83D\\uDC4D]", "")`, errors: [{ - column: 29, - endColumn: 43, + column: 20, + endColumn: 45, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("\\uDC4D[\\uD83D\\uDC4D]", "u")` }] }] @@ -562,8 +562,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("\u1234[\\uD83D\\uDC4D]")`, errors: [{ - column: 23, - endColumn: 36, + column: 20, + endColumn: 44, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("\u1234[\\uD83D\\uDC4D]", "u")` }] }] @@ -656,8 +656,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "")`, errors: [{ - column: 22, - endColumn: 36, + column: 20, + endColumn: 38, messageId: "combiningClass", suggestions: null }] @@ -665,8 +665,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "u")`, errors: [{ - column: 22, - endColumn: 36, + column: 20, + endColumn: 38, messageId: "combiningClass", suggestions: null }] @@ -674,8 +674,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{41}\\u{301}]", "u")`, errors: [{ - column: 22, - endColumn: 37, + column: 20, + endColumn: 39, messageId: "combiningClass", suggestions: null }] @@ -736,8 +736,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "")`, errors: [{ - column: 22, - endColumn: 36, + column: 20, + endColumn: 38, messageId: "combiningClass", suggestions: null }] @@ -745,8 +745,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "u")`, errors: [{ - column: 22, - endColumn: 36, + column: 20, + endColumn: 38, messageId: "combiningClass", suggestions: null }] @@ -754,8 +754,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{2747}\\u{FE0F}]", "u")`, errors: [{ - column: 22, - endColumn: 40, + column: 20, + endColumn: 42, messageId: "combiningClass", suggestions: null }] @@ -798,8 +798,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{1F476}\\u{1F3FB}]", "u")`, errors: [{ - column: 22, - endColumn: 42, + column: 20, + endColumn: 44, messageId: "emojiModifier", suggestions: null }] @@ -980,8 +980,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{1F1EF}\\u{1F1F5}]", "u")`, errors: [{ - column: 22, - endColumn: 42, + column: 20, + endColumn: 44, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -1058,14 +1058,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`, errors: [ { - column: 22, - endColumn: 41, + column: 20, + endColumn: 72, messageId: "zwj", suggestions: null }, { - column: 41, - endColumn: 60, + column: 20, + endColumn: 72, messageId: "zwj", suggestions: null } @@ -1114,14 +1114,14 @@ ruleTester.run("no-misleading-character-class", rule, { env: { es2020: true }, errors: [ { - column: 33, - endColumn: 52, + column: 31, + endColumn: 83, messageId: "zwj", suggestions: null }, { - column: 52, - endColumn: 71, + column: 31, + endColumn: 83, messageId: "zwj", suggestions: null } From 06c23c784245331659204f674dac339ba7fc3e24 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 11 Nov 2023 15:38:25 -0500 Subject: [PATCH 17/31] Used sourceCode.getText(node) over node.raw, and added comments --- lib/rules/no-misleading-character-class.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 999b3f1ac65..d0c03a9730c 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -239,22 +239,28 @@ module.exports = { * @see https://github.com/eslint/eslint/pull/17515 */ function generateReportLocation(chars, node) { + + // We only attempt to use a granular location for literals without dynamic expressions if (node.type !== "Literal" && (node.type !== "TemplateLiteral" || node.expressions.length)) { return node.loc; } - const first = chars[0]; - const last = chars[chars.length - 1]; + const rawText = sourceCode.getText(node); - const reportStartOffset = countDoubleEscapedBackslashes(node.raw.slice(0, first.start)); + // Factoring in backslash-escaped characters, bail if the first character doesn't match real text + const first = chars[0]; + const reportStartOffset = countDoubleEscapedBackslashes(rawText.slice(0, first.start)); - if (!node.raw.slice(first.start, first.end + 1 + reportStartOffset).includes(first.raw)) { + if (!rawText.slice(first.start, first.end + 1 + reportStartOffset).includes(first.raw)) { return node.loc; } - const reportEndOffset = countDoubleEscapedBackslashes(node.raw.slice(first.start, last.end)); - if (!node.raw.slice(last.start, last.end + last.parent.raw.length + reportEndOffset).includes(last.raw)) { + // Factoring in backslash-escaped characters, also bail if the last character doesn't match real text + const last = chars[chars.length - 1]; + const reportEndOffset = countDoubleEscapedBackslashes(rawText.slice(first.start, last.end)); + + if (!rawText.slice(last.start, last.end + last.parent.raw.length + reportEndOffset).includes(last.raw)) { return node.loc; } From 482949d1f678704facf9f9f89ca4bf16699ad8ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Sat, 11 Nov 2023 16:12:38 -0500 Subject: [PATCH 18/31] Update lib/rules/no-misleading-character-class.js --- lib/rules/no-misleading-character-class.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index d0c03a9730c..84c59a5aeba 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -255,7 +255,6 @@ module.exports = { return node.loc; } - // Factoring in backslash-escaped characters, also bail if the last character doesn't match real text const last = chars[chars.length - 1]; const reportEndOffset = countDoubleEscapedBackslashes(rawText.slice(first.start, last.end)); From a2c192a8d776f09bab9c521ca2ec7f2eb0a6afbb Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sun, 12 Nov 2023 21:10:39 -0500 Subject: [PATCH 19/31] Drastically limit applicability --- lib/rules/no-misleading-character-class.js | 54 ++-- .../rules/no-misleading-character-class.js | 253 ++++++++++-------- 2 files changed, 159 insertions(+), 148 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 84c59a5aeba..95f08cb21c2 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -62,17 +62,6 @@ function *iterateCharacterSequence(nodes) { } } -/** - * Counts how many \\\\s appear in text. - * @param {string} text Text to count within. - * @returns {number} How many \\\\s were found. - */ -function countDoubleEscapedBackslashes(text) { - const matches = text.match(/\\\\/gu); - - return matches ? matches.length : 0; -} - /** * Checks whether the given character node is a Unicode code point escape or not. * @param {Character} char the character node to check. @@ -232,45 +221,40 @@ module.exports = { const parser = new RegExpParser(); /** - * Generates a granular loc for context.report. + * Generates a granular loc for context.report, if directly calculable. * @param {Character[]} chars Individual characters being reported on. * @param {Node} node Parent string node to report within. - * @returns {Object} Granular loc for context.report. + * @returns {Object | null} Granular loc for context.report, if directly calculable. * @see https://github.com/eslint/eslint/pull/17515 */ function generateReportLocation(chars, node) { - // We only attempt to use a granular location for literals without dynamic expressions - if (node.type !== "Literal" && (node.type !== "TemplateLiteral" || node.expressions.length)) { - return node.loc; - } - - const rawText = sourceCode.getText(node); - - // Factoring in backslash-escaped characters, bail if the first character doesn't match real text - const first = chars[0]; - const reportStartOffset = countDoubleEscapedBackslashes(rawText.slice(0, first.start)); - - if (!rawText.slice(first.start, first.end + 1 + reportStartOffset).includes(first.raw)) { - return node.loc; - } + // Limit to to literals and expression-less templates with raw values === their value. + switch (node.type) { + case "TemplateLiteral": + if (node.expressions.length || node.quasis[0].value.raw !== node.quasis[0].value.cooked) { + return null; + } + break; - // Factoring in backslash-escaped characters, also bail if the last character doesn't match real text - const last = chars[chars.length - 1]; - const reportEndOffset = countDoubleEscapedBackslashes(rawText.slice(first.start, last.end)); + case "Literal": + if (node.value !== node.raw.slice(1, -1)) { + return null; + } + break; - if (!rawText.slice(last.start, last.end + last.parent.raw.length + reportEndOffset).includes(last.raw)) { - return node.loc; + default: + return null; } return { end: { line: node.loc.end.line, - column: node.loc.start.column + last.end + 1 + reportEndOffset + reportStartOffset + column: node.loc.start.column + chars[chars.length - 1].end + 1 }, start: { line: node.loc.start.line, - column: node.loc.start.column + first.start + 1 + reportStartOffset + column: node.loc.start.column + chars[0].start + 1 } }; } @@ -330,7 +314,7 @@ module.exports = { for (const chars of matches) { context.report({ - loc: generateReportLocation(chars, node), + loc: generateReportLocation(chars, node) || node.loc, messageId: kind, suggest }); diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index 100f1cb184b..271ae280652 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -86,8 +86,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[👍]/", errors: [{ - column: 11, - endColumn: 13, + column: 9, + endColumn: 15, line: 1, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👍]/u" }] @@ -96,8 +96,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83D\\uDC4D]/", errors: [{ - column: 11, - endColumn: 23, + column: 9, + endColumn: 25, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[\\uD83D\\uDC4D]/u" }] }] @@ -105,8 +105,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83D\\uDC4D-\\uffff]/", errors: [{ - column: 11, - endColumn: 23, + column: 9, + endColumn: 32, messageId: "surrogatePairWithoutUFlag", suggestions: null // pattern would be invalid with the 'u' flag }] @@ -114,8 +114,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /before[\\uD83D\\uDC4D]after/", errors: [{ - column: 17, - endColumn: 29, + column: 9, + endColumn: 36, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /before[\\uD83D\\uDC4D]after/u" }] }] @@ -123,8 +123,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[before\\uD83D\\uDC4Dafter]/", errors: [{ - column: 17, - endColumn: 29, + column: 9, + endColumn: 36, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[before\\uD83D\\uDC4Dafter]/u" }] }] @@ -132,8 +132,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /\\uDC4D[\\uD83D\\uDC4D]/", errors: [{ - column: 17, - endColumn: 29, + column: 9, + endColumn: 31, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /\\uDC4D[\\uD83D\\uDC4D]/u" }] }] @@ -142,8 +142,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[👍]/", parserOptions: { ecmaVersion: 3 }, errors: [{ - column: 11, - endColumn: 13, + column: 9, + endColumn: 15, messageId: "surrogatePairWithoutUFlag", suggestions: null // ecmaVersion doesn't support the 'u' flag }] @@ -152,8 +152,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[👍]/", parserOptions: { ecmaVersion: 5 }, errors: [{ - column: 11, - endColumn: 13, + column: 9, + endColumn: 15, messageId: "surrogatePairWithoutUFlag", suggestions: null // ecmaVersion doesn't support the 'u' flag }] @@ -161,8 +161,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[👍]\\a/", errors: [{ - column: 11, - endColumn: 13, + column: 9, + endColumn: 17, messageId: "surrogatePairWithoutUFlag", suggestions: null // pattern would be invalid with the 'u' flag }] @@ -171,8 +171,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /(?<=[👍])/", parserOptions: { ecmaVersion: 9 }, errors: [{ - column: 15, - endColumn: 17, + column: 9, + endColumn: 20, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /(?<=[👍])/u" }] }] @@ -181,8 +181,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /(?<=[👍])/", parserOptions: { ecmaVersion: 2018 }, errors: [{ - column: 15, - endColumn: 17, + column: 9, + endColumn: 20, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /(?<=[👍])/u" }] }] @@ -190,8 +190,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[Á]/", errors: [{ - column: 11, - endColumn: 13, + column: 9, + endColumn: 15, messageId: "combiningClass", suggestions: null }] @@ -199,8 +199,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[Á]/u", errors: [{ - column: 11, - endColumn: 13, + column: 9, + endColumn: 16, messageId: "combiningClass", suggestions: null }] @@ -208,8 +208,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u0041\\u0301]/", errors: [{ - column: 11, - endColumn: 23, + column: 9, + endColumn: 25, messageId: "combiningClass", suggestions: null }] @@ -217,8 +217,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u0041\\u0301]/u", errors: [{ - column: 11, - endColumn: 23, + column: 9, + endColumn: 26, messageId: "combiningClass", suggestions: null }] @@ -226,8 +226,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{41}\\u{301}]/u", errors: [{ - column: 11, - endColumn: 24, + column: 9, + endColumn: 27, messageId: "combiningClass", suggestions: null }] @@ -235,8 +235,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[❇️]/", errors: [{ - column: 11, - endColumn: 13, + column: 9, + endColumn: 15, messageId: "combiningClass", suggestions: null }] @@ -244,8 +244,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[❇️]/u", errors: [{ - column: 11, - endColumn: 13, + column: 9, + endColumn: 16, messageId: "combiningClass", suggestions: null }] @@ -253,8 +253,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u2747\\uFE0F]/", errors: [{ - column: 11, - endColumn: 23, + column: 9, + endColumn: 25, messageId: "combiningClass", suggestions: null }] @@ -262,8 +262,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u2747\\uFE0F]/u", errors: [{ - column: 11, - endColumn: 23, + column: 9, + endColumn: 26, messageId: "combiningClass", suggestions: null }] @@ -271,8 +271,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{2747}\\u{FE0F}]/u", errors: [{ - column: 11, - endColumn: 27, + column: 9, + endColumn: 30, messageId: "combiningClass", suggestions: null }] @@ -281,14 +281,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[👶🏻]/", errors: [ { - column: 11, - endColumn: 13, + column: 9, + endColumn: 17, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👶🏻]/u" }] }, { - column: 13, - endColumn: 15, + column: 9, + endColumn: 17, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👶🏻]/u" }] } @@ -297,8 +297,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[👶🏻]/u", errors: [{ - column: 11, - endColumn: 15, + column: 9, + endColumn: 18, messageId: "emojiModifier", suggestions: null }] @@ -306,8 +306,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83D\\uDC76\\uD83C\\uDFFB]/u", errors: [{ - column: 11, - endColumn: 35, + column: 9, + endColumn: 38, messageId: "emojiModifier", suggestions: null }] @@ -315,8 +315,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{1F476}\\u{1F3FB}]/u", errors: [{ - column: 11, - endColumn: 29, + column: 9, + endColumn: 32, messageId: "emojiModifier", suggestions: null }] @@ -325,14 +325,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[🇯🇵]/", errors: [ { - column: 11, - endColumn: 13, + column: 9, + endColumn: 17, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/u" }] }, { - column: 13, - endColumn: 15, + column: 9, + endColumn: 17, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/u" }] } @@ -342,14 +342,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[🇯🇵]/i", errors: [ { - column: 11, - endColumn: 13, + column: 9, + endColumn: 18, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/iu" }] }, { - column: 13, - endColumn: 15, + column: 9, + endColumn: 18, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/iu" }] } @@ -358,8 +358,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[🇯🇵]/u", errors: [{ - column: 11, - endColumn: 15, + column: 9, + endColumn: 18, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -367,8 +367,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83C\\uDDEF\\uD83C\\uDDF5]/u", errors: [{ - column: 11, - endColumn: 35, + column: 9, + endColumn: 38, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -376,8 +376,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{1F1EF}\\u{1F1F5}]/u", errors: [{ - column: 11, - endColumn: 29, + column: 9, + endColumn: 32, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -386,33 +386,33 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[👨‍👩‍👦]/", errors: [ { - column: 11, - endColumn: 13, + column: 9, + endColumn: 21, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }] }, { - column: 12, - endColumn: 14, - messageId: "zwj", - suggestions: null + column: 9, + endColumn: 21, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }] }, { - column: 14, - endColumn: 16, + column: 9, + endColumn: 21, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }] }, { - column: 15, - endColumn: 17, - messageId: "zwj" + column: 9, + endColumn: 21, + messageId: "zwj", + suggestions: null }, { - column: 17, - endColumn: 19, - messageId: "surrogatePairWithoutUFlag", - suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }] + column: 9, + endColumn: 21, + messageId: "zwj" } ] }, @@ -420,14 +420,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[👨‍👩‍👦]/u", errors: [ { - column: 11, - endColumn: 14, + column: 9, + endColumn: 22, messageId: "zwj", suggestions: null }, { - column: 14, - endColumn: 17, + column: 9, + endColumn: 22, messageId: "zwj", suggestions: null } @@ -437,14 +437,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]/u", errors: [ { - column: 11, - endColumn: 29, + column: 9, + endColumn: 62, messageId: "zwj", suggestions: null }, { - column: 29, - endColumn: 47, + column: 9, + endColumn: 62, messageId: "zwj", suggestions: null } @@ -454,14 +454,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]/u", errors: [ { - column: 11, - endColumn: 28, + column: 9, + endColumn: 57, messageId: "zwj", suggestions: null }, { - column: 28, - endColumn: 45, + column: 9, + endColumn: 57, messageId: "zwj", suggestions: null } @@ -541,6 +541,15 @@ ruleTester.run("no-misleading-character-class", rule, { suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("[before\\uD83D\\uDC4Dafter]", "u")` }] }] }, + { + code: String.raw`var r = RegExp("\t\t\t👍[👍]")`, + errors: [{ + column: 16, + endColumn: 30, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = RegExp("\t\t\t👍[👍]", "u")` }] + }] + }, { code: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "")`, errors: [{ @@ -571,8 +580,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("\\u1234\\u5678👎[👍]")`, errors: [{ - column: 38, - endColumn: 40, + column: 20, + endColumn: 42, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("\\u1234\\u5678👎[👍]", "u")` }] }] @@ -580,8 +589,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("\\u1234\\u5678👍[👍]")`, errors: [{ - column: 38, - endColumn: 40, + column: 20, + endColumn: 42, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("\\u1234\\u5678👍[👍]", "u")` }] }] @@ -609,8 +618,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[👍]\\a", "")`, errors: [{ - column: 22, - endColumn: 24, + column: 20, + endColumn: 29, messageId: "surrogatePairWithoutUFlag", suggestions: null // pattern would be invalid with the 'u' flag }] @@ -701,8 +710,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`new RegExp("[ \\ufe0f]", "")`, errors: [{ - column: 14, - endColumn: 22, + column: 12, + endColumn: 24, messageId: "combiningClass", suggestions: null }] @@ -710,8 +719,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`new RegExp("[ \\ufe0f]", "u")`, errors: [{ - column: 14, - endColumn: 22, + column: 12, + endColumn: 24, messageId: "combiningClass", suggestions: null }] @@ -720,14 +729,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`new RegExp("[ \\ufe0f][ \\ufe0f]")`, errors: [ { - column: 14, - endColumn: 22, + column: 12, + endColumn: 34, messageId: "combiningClass", suggestions: null }, { - column: 24, - endColumn: 32, + column: 12, + endColumn: 34, messageId: "combiningClass", suggestions: null } @@ -804,6 +813,24 @@ ruleTester.run("no-misleading-character-class", rule, { suggestions: null }] }, + { + code: "var r = RegExp(`\t\t\t👍[👍]`)", + errors: [{ + column: 23, + endColumn: 25, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = RegExp(`\t\t\t👍[👍]`, \"u\")" }] + }] + }, + { + code: "var r = RegExp(`\\t\\t\\t👍[👍]`)", + errors: [{ + column: 16, + endColumn: 30, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = RegExp(`\\t\\t\\t👍[👍]`, \"u\")" }] + }] + }, { code: String.raw`var r = new RegExp("[🇯🇵]", "")`, errors: [ @@ -1162,8 +1189,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[[👶🏻]]/v", parserOptions: { ecmaVersion: 2024 }, errors: [{ - column: 12, - endColumn: 16, + column: 9, + endColumn: 20, messageId: "emojiModifier", suggestions: null }] @@ -1184,8 +1211,8 @@ flatRuleTester.run("no-misleading-character-class", rule, { sourceType: "script" }, errors: [{ - column: 11, - endColumn: 13, + column: 9, + endColumn: 15, messageId: "surrogatePairWithoutUFlag", suggestions: null // ecmaVersion doesn't support the 'u' flag }] @@ -1196,8 +1223,8 @@ flatRuleTester.run("no-misleading-character-class", rule, { ecmaVersion: 2015 }, errors: [{ - column: 11, - endColumn: 13, + column: 9, + endColumn: 15, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👍]/u" }] }] From d0313a48a50588492e68b5173fd077983d1e694e Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sun, 19 Nov 2023 15:22:25 +0100 Subject: [PATCH 20/31] Restricted regexp literals on \u --- lib/rules/no-misleading-character-class.js | 2 +- .../rules/no-misleading-character-class.js | 131 ++++++++++-------- 2 files changed, 71 insertions(+), 62 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 95f08cb21c2..c97f54cbb26 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -238,7 +238,7 @@ module.exports = { break; case "Literal": - if (node.value !== node.raw.slice(1, -1)) { + if (node.raw.includes("\\u") || (typeof node.value === "string" && node.value !== node.raw.slice(1, -1))) { return null; } break; diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index 271ae280652..fd894e19f1e 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -86,8 +86,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[👍]/", errors: [{ - column: 9, - endColumn: 15, + column: 11, + endColumn: 13, line: 1, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👍]/u" }] @@ -142,8 +142,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[👍]/", parserOptions: { ecmaVersion: 3 }, errors: [{ - column: 9, - endColumn: 15, + column: 11, + endColumn: 13, messageId: "surrogatePairWithoutUFlag", suggestions: null // ecmaVersion doesn't support the 'u' flag }] @@ -152,8 +152,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[👍]/", parserOptions: { ecmaVersion: 5 }, errors: [{ - column: 9, - endColumn: 15, + column: 11, + endColumn: 13, messageId: "surrogatePairWithoutUFlag", suggestions: null // ecmaVersion doesn't support the 'u' flag }] @@ -161,8 +161,17 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[👍]\\a/", errors: [{ - column: 9, - endColumn: 17, + column: 11, + endColumn: 13, + messageId: "surrogatePairWithoutUFlag", + suggestions: null // pattern would be invalid with the 'u' flag + }] + }, + { + code: "var r = /\\a[👍]\\a/", + errors: [{ + column: 13, + endColumn: 15, messageId: "surrogatePairWithoutUFlag", suggestions: null // pattern would be invalid with the 'u' flag }] @@ -171,8 +180,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /(?<=[👍])/", parserOptions: { ecmaVersion: 9 }, errors: [{ - column: 9, - endColumn: 20, + column: 15, + endColumn: 17, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /(?<=[👍])/u" }] }] @@ -181,8 +190,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /(?<=[👍])/", parserOptions: { ecmaVersion: 2018 }, errors: [{ - column: 9, - endColumn: 20, + column: 15, + endColumn: 17, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /(?<=[👍])/u" }] }] @@ -190,8 +199,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[Á]/", errors: [{ - column: 9, - endColumn: 15, + column: 11, + endColumn: 13, messageId: "combiningClass", suggestions: null }] @@ -199,8 +208,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[Á]/u", errors: [{ - column: 9, - endColumn: 16, + column: 11, + endColumn: 13, messageId: "combiningClass", suggestions: null }] @@ -235,8 +244,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[❇️]/", errors: [{ - column: 9, - endColumn: 15, + column: 11, + endColumn: 13, messageId: "combiningClass", suggestions: null }] @@ -244,8 +253,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[❇️]/u", errors: [{ - column: 9, - endColumn: 16, + column: 11, + endColumn: 13, messageId: "combiningClass", suggestions: null }] @@ -281,14 +290,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[👶🏻]/", errors: [ { - column: 9, - endColumn: 17, + column: 11, + endColumn: 13, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👶🏻]/u" }] }, { - column: 9, - endColumn: 17, + column: 13, + endColumn: 15, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👶🏻]/u" }] } @@ -297,8 +306,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[👶🏻]/u", errors: [{ - column: 9, - endColumn: 18, + column: 11, + endColumn: 15, messageId: "emojiModifier", suggestions: null }] @@ -325,14 +334,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[🇯🇵]/", errors: [ { - column: 9, - endColumn: 17, + column: 11, + endColumn: 13, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/u" }] }, { - column: 9, - endColumn: 17, + column: 13, + endColumn: 15, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/u" }] } @@ -342,14 +351,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[🇯🇵]/i", errors: [ { - column: 9, - endColumn: 18, + column: 11, + endColumn: 13, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/iu" }] }, { - column: 9, - endColumn: 18, + column: 13, + endColumn: 15, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/iu" }] } @@ -358,8 +367,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[🇯🇵]/u", errors: [{ - column: 9, - endColumn: 18, + column: 11, + endColumn: 15, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -386,33 +395,33 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[👨‍👩‍👦]/", errors: [ { - column: 9, - endColumn: 21, + column: 11, + endColumn: 13, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }] }, { - column: 9, - endColumn: 21, - messageId: "surrogatePairWithoutUFlag", - suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }] + column: 12, + endColumn: 14, + messageId: "zwj", + suggestions: null }, { - column: 9, - endColumn: 21, + column: 14, + endColumn: 16, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }] }, { - column: 9, - endColumn: 21, - messageId: "zwj", - suggestions: null + column: 15, + endColumn: 17, + messageId: "zwj" }, { - column: 9, - endColumn: 21, - messageId: "zwj" + column: 17, + endColumn: 19, + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }] } ] }, @@ -420,14 +429,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[👨‍👩‍👦]/u", errors: [ { - column: 9, - endColumn: 22, + column: 11, + endColumn: 14, messageId: "zwj", suggestions: null }, { - column: 9, - endColumn: 22, + column: 14, + endColumn: 17, messageId: "zwj", suggestions: null } @@ -1189,8 +1198,8 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[[👶🏻]]/v", parserOptions: { ecmaVersion: 2024 }, errors: [{ - column: 9, - endColumn: 20, + column: 12, + endColumn: 16, messageId: "emojiModifier", suggestions: null }] @@ -1211,8 +1220,8 @@ flatRuleTester.run("no-misleading-character-class", rule, { sourceType: "script" }, errors: [{ - column: 9, - endColumn: 15, + column: 11, + endColumn: 13, messageId: "surrogatePairWithoutUFlag", suggestions: null // ecmaVersion doesn't support the 'u' flag }] @@ -1223,8 +1232,8 @@ flatRuleTester.run("no-misleading-character-class", rule, { ecmaVersion: 2015 }, errors: [{ - column: 9, - endColumn: 15, + column: 11, + endColumn: 13, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👍]/u" }] }] From 6e21dd2772e4c96aa420fb9a4c6e3d8e7f0e1e95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Fri, 24 Nov 2023 17:27:46 +0100 Subject: [PATCH 21/31] Update lib/rules/no-misleading-character-class.js Co-authored-by: Milos Djermanovic --- lib/rules/no-misleading-character-class.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index c97f54cbb26..9d2a1eb2ae2 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -158,7 +158,7 @@ const characterSequenceIndexFilters = { chars[index - 1].value !== 0x200d && chars[index + 1].value !== 0x200d ) { - return chars.slice(index - 1, index + 1); + return chars.slice(index - 1, index + 2); } return null; From 27358ab499fa982d604440f00ce32438648c51ba Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Mon, 27 Nov 2023 02:23:03 +0100 Subject: [PATCH 22/31] Corrected erroneous escaped u exclusion --- lib/rules/no-misleading-character-class.js | 2 +- .../rules/no-misleading-character-class.js | 100 ++++++++++-------- 2 files changed, 55 insertions(+), 47 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 9d2a1eb2ae2..67f136209fb 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -238,7 +238,7 @@ module.exports = { break; case "Literal": - if (node.raw.includes("\\u") || (typeof node.value === "string" && node.value !== node.raw.slice(1, -1))) { + if (typeof node.value === "string" && node.value !== node.raw.slice(1, -1)) { return null; } break; diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index fd894e19f1e..2c8c8aa06e4 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -96,8 +96,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83D\\uDC4D]/", errors: [{ - column: 9, - endColumn: 25, + column: 11, + endColumn: 23, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[\\uD83D\\uDC4D]/u" }] }] @@ -105,8 +105,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83D\\uDC4D-\\uffff]/", errors: [{ - column: 9, - endColumn: 32, + column: 11, + endColumn: 23, messageId: "surrogatePairWithoutUFlag", suggestions: null // pattern would be invalid with the 'u' flag }] @@ -114,8 +114,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /before[\\uD83D\\uDC4D]after/", errors: [{ - column: 9, - endColumn: 36, + column: 17, + endColumn: 29, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /before[\\uD83D\\uDC4D]after/u" }] }] @@ -123,8 +123,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[before\\uD83D\\uDC4Dafter]/", errors: [{ - column: 9, - endColumn: 36, + column: 17, + endColumn: 29, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[before\\uD83D\\uDC4Dafter]/u" }] }] @@ -132,8 +132,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /\\uDC4D[\\uD83D\\uDC4D]/", errors: [{ - column: 9, - endColumn: 31, + column: 17, + endColumn: 29, messageId: "surrogatePairWithoutUFlag", suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /\\uDC4D[\\uD83D\\uDC4D]/u" }] }] @@ -217,8 +217,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u0041\\u0301]/", errors: [{ - column: 9, - endColumn: 25, + column: 11, + endColumn: 23, messageId: "combiningClass", suggestions: null }] @@ -226,8 +226,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u0041\\u0301]/u", errors: [{ - column: 9, - endColumn: 26, + column: 11, + endColumn: 23, messageId: "combiningClass", suggestions: null }] @@ -235,8 +235,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{41}\\u{301}]/u", errors: [{ - column: 9, - endColumn: 27, + column: 11, + endColumn: 24, messageId: "combiningClass", suggestions: null }] @@ -262,8 +262,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u2747\\uFE0F]/", errors: [{ - column: 9, - endColumn: 25, + column: 11, + endColumn: 23, messageId: "combiningClass", suggestions: null }] @@ -271,8 +271,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u2747\\uFE0F]/u", errors: [{ - column: 9, - endColumn: 26, + column: 11, + endColumn: 23, messageId: "combiningClass", suggestions: null }] @@ -280,8 +280,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{2747}\\u{FE0F}]/u", errors: [{ - column: 9, - endColumn: 30, + column: 11, + endColumn: 27, messageId: "combiningClass", suggestions: null }] @@ -312,11 +312,19 @@ ruleTester.run("no-misleading-character-class", rule, { suggestions: null }] }, + { + code: "var r = /[a\\uD83C\\uDFFB]/u", + errors: [{ + column: 11, + endColumn: 24, + messageId: "emojiModifier" + }] + }, { code: "var r = /[\\uD83D\\uDC76\\uD83C\\uDFFB]/u", errors: [{ - column: 9, - endColumn: 38, + column: 11, + endColumn: 35, messageId: "emojiModifier", suggestions: null }] @@ -324,8 +332,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{1F476}\\u{1F3FB}]/u", errors: [{ - column: 9, - endColumn: 32, + column: 11, + endColumn: 29, messageId: "emojiModifier", suggestions: null }] @@ -376,8 +384,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\uD83C\\uDDEF\\uD83C\\uDDF5]/u", errors: [{ - column: 9, - endColumn: 38, + column: 11, + endColumn: 35, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -385,8 +393,8 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[\\u{1F1EF}\\u{1F1F5}]/u", errors: [{ - column: 9, - endColumn: 32, + column: 11, + endColumn: 29, messageId: "regionalIndicatorSymbol", suggestions: null }] @@ -402,7 +410,7 @@ ruleTester.run("no-misleading-character-class", rule, { }, { column: 12, - endColumn: 14, + endColumn: 15, messageId: "zwj", suggestions: null }, @@ -414,7 +422,7 @@ ruleTester.run("no-misleading-character-class", rule, { }, { column: 15, - endColumn: 17, + endColumn: 18, messageId: "zwj" }, { @@ -430,13 +438,13 @@ ruleTester.run("no-misleading-character-class", rule, { errors: [ { column: 11, - endColumn: 14, + endColumn: 16, messageId: "zwj", suggestions: null }, { column: 14, - endColumn: 17, + endColumn: 19, messageId: "zwj", suggestions: null } @@ -446,14 +454,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]/u", errors: [ { - column: 9, - endColumn: 62, + column: 11, + endColumn: 41, messageId: "zwj", suggestions: null }, { - column: 9, - endColumn: 62, + column: 29, + endColumn: 59, messageId: "zwj", suggestions: null } @@ -463,14 +471,14 @@ ruleTester.run("no-misleading-character-class", rule, { code: "var r = /[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]/u", errors: [ { - column: 9, - endColumn: 57, + column: 11, + endColumn: 37, messageId: "zwj", suggestions: null }, { - column: 9, - endColumn: 57, + column: 28, + endColumn: 54, messageId: "zwj", suggestions: null } @@ -1033,7 +1041,7 @@ ruleTester.run("no-misleading-character-class", rule, { }, { column: 23, - endColumn: 25, + endColumn: 26, messageId: "zwj", suggestions: null }, @@ -1045,7 +1053,7 @@ ruleTester.run("no-misleading-character-class", rule, { }, { column: 26, - endColumn: 28, + endColumn: 29, messageId: "zwj" }, { @@ -1061,13 +1069,13 @@ ruleTester.run("no-misleading-character-class", rule, { errors: [ { column: 22, - endColumn: 25, + endColumn: 27, messageId: "zwj", suggestions: null }, { column: 25, - endColumn: 28, + endColumn: 30, messageId: "zwj", suggestions: null } From fb94bb0f60e03a79b06fdae1186d1afc2f3cea6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Mon, 11 Dec 2023 23:59:10 -0500 Subject: [PATCH 23/31] Update lib/rules/no-misleading-character-class.js Co-authored-by: Francesco Trotta --- lib/rules/no-misleading-character-class.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 67f136209fb..bb90d9252eb 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -168,7 +168,7 @@ const characterSequenceIndexFilters = { const kinds = Object.keys(characterSequenceIndexFilters); /** - * Collects the indices where the filter returns true. + * Collects the indices where the filter returns an array. * @param {Character[]} chars Characters to run the filter on. * @param {(char: Character, index: number, chars: Character[]) => Character[] | null} filter Finds matches for an index. * @returns {Character[][]} Indices where the filter returned true. From 46b43685f9ae7b3d42ccbc5bd3e2d9d2a7958d84 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 13 Dec 2023 12:08:18 -0500 Subject: [PATCH 24/31] If a report range is missing, skip other reports --- lib/rules/no-misleading-character-class.js | 27 +++++++++++++++++-- .../rules/no-misleading-character-class.js | 18 ------------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index bb90d9252eb..9ca5bcad696 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -259,6 +259,29 @@ module.exports = { }; } + /** + * Finds the report loc(s) for a range of matches. + * @param {Character[][]} matches Characters that should trigger a report. + * @param {Node} node The node to report. + * @returns {Object | null} Node loc(s) for context.report. + */ + function getNodeReportLocations(matches, node) { + const locs = []; + + for (const chars of matches) { + const loc = generateReportLocation(chars, node); + + // If a report can't match to a range, don't report any others + if (!loc) { + return [node.loc]; + } + + locs.push(loc); + } + + return locs; + } + /** * Verify a given regular expression. * @param {Node} node The node to report. @@ -312,9 +335,9 @@ module.exports = { }]; } - for (const chars of matches) { + for (const loc of getNodeReportLocations(matches, node)) { context.report({ - loc: generateReportLocation(chars, node) || node.loc, + loc, messageId: kind, suggest }); diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index 2c8c8aa06e4..a2c64f1b2b6 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -1084,12 +1084,6 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]", "u")`, errors: [ - { - column: 20, - endColumn: 80, - messageId: "zwj", - suggestions: null - }, { column: 20, endColumn: 80, @@ -1101,12 +1095,6 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`, errors: [ - { - column: 20, - endColumn: 72, - messageId: "zwj", - suggestions: null - }, { column: 20, endColumn: 72, @@ -1157,12 +1145,6 @@ ruleTester.run("no-misleading-character-class", rule, { code: String.raw`var r = new globalThis.RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`, env: { es2020: true }, errors: [ - { - column: 31, - endColumn: 83, - messageId: "zwj", - suggestions: null - }, { column: 31, endColumn: 83, From ed5d5f2316912e58af75448739d62e73dab6b414 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Fri, 15 Dec 2023 17:39:04 -0500 Subject: [PATCH 25/31] Update lib/rules/no-misleading-character-class.js Co-authored-by: Milos Djermanovic --- lib/rules/no-misleading-character-class.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 9ca5bcad696..9de91a67fb7 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -248,14 +248,8 @@ module.exports = { } return { - end: { - line: node.loc.end.line, - column: node.loc.start.column + chars[chars.length - 1].end + 1 - }, - start: { - line: node.loc.start.line, - column: node.loc.start.column + chars[0].start + 1 - } + start: sourceCode.getLocFromIndex(node.range[0] + 1 + chars[0].start), + end: sourceCode.getLocFromIndex(node.range[0] + 1 + chars[chars.length - 1].end) }; } From 9b2b365ea2b54bafa35c2ead2f36d328e3d61ebb Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Fri, 15 Dec 2023 17:47:12 -0500 Subject: [PATCH 26/31] Merge branch 'main' --- .github/ISSUE_TEMPLATE/change.yml | 2 +- .github/ISSUE_TEMPLATE/docs.yml | 46 + .github/ISSUE_TEMPLATE/new-rule.yml | 1 - .github/ISSUE_TEMPLATE/rule-change.yml | 2 +- .github/renovate.json5 | 42 + .github/workflows/ci.yml | 10 +- .github/workflows/update-readme.yml | 2 +- CHANGELOG.md | 103 + Makefile.js | 37 +- README.md | 31 +- bin/eslint.js | 43 +- conf/rule-type-list.json | 58 +- docs/.eleventy.js | 92 +- docs/package.json | 10 +- docs/src/_data/further_reading_links.json | 7 + docs/src/_data/rules.json | 4042 ++++++++--------- docs/src/_data/rules_categories.js | 26 + docs/src/_data/rules_meta.json | 143 +- .../components/rule-categories.macro.html | 2 +- docs/src/_includes/components/rule.macro.html | 10 +- .../_includes/components/social-icons.html | 7 +- docs/src/_includes/layouts/base.html | 12 +- docs/src/assets/scss/docs.scss | 35 +- docs/src/assets/scss/tokens/themes.scss | 2 + docs/src/contribute/pull-requests.md | 29 +- .../extend/plugin-migration-flat-config.md | 293 ++ docs/src/extend/ways-to-extend.md | 2 +- docs/src/integrate/nodejs-api.md | 4 +- docs/src/library/link-card.md | 3 +- docs/src/pages/rules.md | 35 +- docs/src/rules/array-bracket-newline.md | 2 +- docs/src/rules/array-bracket-spacing.md | 2 +- docs/src/rules/array-element-newline.md | 2 +- docs/src/rules/arrow-body-style.md | 32 +- docs/src/rules/arrow-parens.md | 2 +- docs/src/rules/arrow-spacing.md | 2 +- docs/src/rules/block-spacing.md | 2 +- docs/src/rules/brace-style.md | 2 +- docs/src/rules/camelcase.md | 20 +- docs/src/rules/capitalized-comments.md | 4 + docs/src/rules/class-methods-use-this.md | 4 +- docs/src/rules/comma-dangle.md | 2 +- docs/src/rules/comma-spacing.md | 10 +- docs/src/rules/comma-style.md | 10 +- docs/src/rules/computed-property-spacing.md | 2 +- docs/src/rules/consistent-return.md | 2 +- docs/src/rules/constructor-super.md | 22 +- docs/src/rules/default-param-last.md | 2 +- docs/src/rules/dot-location.md | 2 +- docs/src/rules/dot-notation.md | 19 +- docs/src/rules/eol-last.md | 5 +- docs/src/rules/for-direction.md | 6 + docs/src/rules/func-call-spacing.md | 2 +- .../rules/function-call-argument-newline.md | 2 +- docs/src/rules/function-paren-newline.md | 64 +- docs/src/rules/generator-star-spacing.md | 2 +- docs/src/rules/generator-star.md | 4 +- docs/src/rules/global-require.md | 4 +- docs/src/rules/global-strict.md | 4 +- docs/src/rules/id-denylist.md | 28 +- docs/src/rules/id-length.md | 52 +- docs/src/rules/id-match.md | 20 +- docs/src/rules/implicit-arrow-linebreak.md | 2 +- docs/src/rules/indent-legacy.md | 131 +- docs/src/rules/indent.md | 97 +- docs/src/rules/jsx-quotes.md | 26 +- docs/src/rules/key-spacing.md | 2 +- docs/src/rules/keyword-spacing.md | 36 +- docs/src/rules/linebreak-style.md | 2 +- docs/src/rules/lines-around-comment.md | 4 +- docs/src/rules/lines-around-directive.md | 172 +- docs/src/rules/lines-between-class-members.md | 18 +- .../src/rules/logical-assignment-operators.md | 10 +- docs/src/rules/max-len.md | 20 +- docs/src/rules/max-lines-per-function.md | 12 +- docs/src/rules/max-params.md | 8 +- docs/src/rules/max-statements-per-line.md | 9 +- docs/src/rules/max-statements.md | 14 +- docs/src/rules/multiline-ternary.md | 19 +- docs/src/rules/new-parens.md | 2 +- docs/src/rules/newline-after-var.md | 24 +- docs/src/rules/newline-before-return.md | 20 +- docs/src/rules/newline-per-chained-call.md | 2 +- docs/src/rules/no-array-constructor.md | 16 +- docs/src/rules/no-arrow-condition.md | 4 +- docs/src/rules/no-async-promise-executor.md | 4 + docs/src/rules/no-buffer-constructor.md | 4 + docs/src/rules/no-catch-shadow.md | 6 +- docs/src/rules/no-comma-dangle.md | 4 +- docs/src/rules/no-cond-assign.md | 18 +- docs/src/rules/no-confusing-arrow.md | 2 +- docs/src/rules/no-continue.md | 8 +- docs/src/rules/no-delete-var.md | 2 +- docs/src/rules/no-dupe-args.md | 4 +- docs/src/rules/no-dupe-class-members.md | 20 +- docs/src/rules/no-else-return.md | 22 +- docs/src/rules/no-empty-class.md | 4 +- docs/src/rules/no-empty-function.md | 20 +- docs/src/rules/no-empty-label.md | 4 +- docs/src/rules/no-empty-pattern.md | 24 +- docs/src/rules/no-empty-static-block.md | 2 +- docs/src/rules/no-eval.md | 12 +- docs/src/rules/no-extra-boolean-cast.md | 2 +- docs/src/rules/no-extra-parens.md | 28 +- docs/src/rules/no-extra-semi.md | 4 +- docs/src/rules/no-extra-strict.md | 4 +- docs/src/rules/no-floating-decimal.md | 2 +- docs/src/rules/no-func-assign.md | 12 +- docs/src/rules/no-implicit-globals.md | 3 +- docs/src/rules/no-implied-eval.md | 1 + docs/src/rules/no-inner-declarations.md | 8 +- docs/src/rules/no-invalid-this.md | 12 +- docs/src/rules/no-irregular-whitespace.md | 41 +- docs/src/rules/no-loop-func.md | 10 +- docs/src/rules/no-loss-of-precision.md | 26 +- .../rules/no-misleading-character-class.md | 34 +- docs/src/rules/no-mixed-operators.md | 1 + docs/src/rules/no-mixed-spaces-and-tabs.md | 32 +- docs/src/rules/no-multi-assign.md | 10 +- docs/src/rules/no-multi-spaces.md | 2 +- docs/src/rules/no-multiple-empty-lines.md | 55 +- docs/src/rules/no-nonoctal-decimal-escape.md | 4 +- docs/src/rules/no-octal-escape.md | 4 +- docs/src/rules/no-octal.md | 4 +- docs/src/rules/no-param-reassign.md | 50 +- docs/src/rules/no-plusplus.md | 4 +- docs/src/rules/no-redeclare.md | 4 +- docs/src/rules/no-reserved-keys.md | 4 +- docs/src/rules/no-restricted-exports.md | 27 + docs/src/rules/no-restricted-imports.md | 69 +- docs/src/rules/no-restricted-properties.md | 6 +- docs/src/rules/no-restricted-syntax.md | 4 +- docs/src/rules/no-return-assign.md | 16 +- docs/src/rules/no-return-await.md | 8 +- docs/src/rules/no-sequences.md | 14 +- docs/src/rules/no-shadow-restricted-names.md | 4 +- docs/src/rules/no-shadow.md | 6 +- docs/src/rules/no-space-before-semi.md | 4 +- docs/src/rules/no-tabs.md | 21 +- docs/src/rules/no-this-before-super.md | 14 +- docs/src/rules/no-throw-literal.md | 4 +- docs/src/rules/no-trailing-spaces.md | 21 +- docs/src/rules/no-undef-init.md | 2 + docs/src/rules/no-undefined.md | 2 +- docs/src/rules/no-underscore-dangle.md | 52 +- docs/src/rules/no-unexpected-multiline.md | 6 +- .../src/rules/no-unmodified-loop-condition.md | 4 +- docs/src/rules/no-unsafe-optional-chaining.md | 2 +- docs/src/rules/no-unused-expressions.md | 2 +- .../rules/no-unused-private-class-members.md | 16 +- docs/src/rules/no-unused-vars.md | 21 +- docs/src/rules/no-useless-constructor.md | 6 +- docs/src/rules/no-useless-escape.md | 2 +- docs/src/rules/no-useless-rename.md | 43 +- docs/src/rules/no-useless-return.md | 18 +- .../rules/no-whitespace-before-property.md | 2 +- docs/src/rules/no-with.md | 4 +- docs/src/rules/no-wrap-func.md | 4 +- .../rules/nonblock-statement-body-position.md | 2 +- docs/src/rules/object-curly-newline.md | 16 +- docs/src/rules/object-curly-spacing.md | 2 +- docs/src/rules/object-property-newline.md | 2 +- docs/src/rules/object-shorthand.md | 8 +- .../src/rules/one-var-declaration-per-line.md | 22 +- docs/src/rules/one-var.md | 69 +- docs/src/rules/operator-linebreak.md | 2 +- docs/src/rules/padded-blocks.md | 2 +- .../rules/padding-line-between-statements.md | 18 +- docs/src/rules/prefer-const.md | 52 +- docs/src/rules/prefer-destructuring.md | 13 +- docs/src/rules/prefer-reflect.md | 4 +- docs/src/rules/prefer-regex-literals.md | 10 +- docs/src/rules/prefer-rest-params.md | 4 +- docs/src/rules/quote-props.md | 2 +- docs/src/rules/quotes.md | 2 +- docs/src/rules/require-await.md | 2 +- docs/src/rules/require-jsdoc.md | 16 +- docs/src/rules/require-unicode-regexp.md | 2 +- docs/src/rules/require-yield.md | 4 +- docs/src/rules/rest-spread-spacing.md | 18 +- docs/src/rules/semi-spacing.md | 2 +- docs/src/rules/semi-style.md | 2 +- docs/src/rules/semi.md | 2 +- docs/src/rules/sort-imports.md | 56 +- docs/src/rules/sort-keys.md | 80 +- docs/src/rules/space-after-function-name.md | 4 +- docs/src/rules/space-after-keywords.md | 6 +- docs/src/rules/space-before-blocks.md | 2 +- docs/src/rules/space-before-function-paren.md | 34 +- .../space-before-function-parentheses.md | 20 +- docs/src/rules/space-before-keywords.md | 10 +- docs/src/rules/space-in-brackets.md | 4 +- docs/src/rules/space-in-parens.md | 2 +- docs/src/rules/space-infix-ops.md | 6 +- docs/src/rules/space-return-throw-case.md | 4 +- docs/src/rules/space-unary-ops.md | 2 +- docs/src/rules/space-unary-word-ops.md | 6 +- docs/src/rules/spaced-comment.md | 2 +- docs/src/rules/spaced-line-comment.md | 4 +- docs/src/rules/strict.md | 42 +- docs/src/rules/switch-colon-spacing.md | 2 +- docs/src/rules/template-curly-spacing.md | 2 +- docs/src/rules/template-tag-spacing.md | 4 +- docs/src/rules/unicode-bom.md | 6 +- docs/src/rules/vars-on-top.md | 4 +- docs/src/rules/wrap-iife.md | 2 +- docs/src/rules/wrap-regex.md | 2 +- docs/src/rules/yield-star-spacing.md | 2 +- docs/src/use/command-line-interface.md | 49 +- .../use/configure/configuration-files-new.md | 10 +- docs/src/use/configure/ignore.md | 2 +- docs/src/use/configure/migration-guide.md | 8 +- docs/src/use/configure/rules.md | 2 +- docs/src/use/core-concepts.md | 17 + .../formatters/html-formatter-example.html | 4 +- docs/src/use/formatters/index.md | 845 +++- docs/tools/markdown-it-rule-example.js | 90 + eslint.config.js | 13 +- lib/cli-engine/cli-engine.js | 4 +- lib/cli-engine/lint-result-cache.js | 24 +- lib/cli.js | 42 +- lib/config/flat-config-schema.js | 118 +- lib/eslint/eslint-helpers.js | 20 +- lib/eslint/flat-eslint.js | 30 +- lib/linter/apply-disable-directives.js | 140 +- .../code-path-analysis/code-path-state.js | 1351 +++++- lib/linter/config-comment-parser.js | 38 +- lib/linter/linter.js | 37 +- lib/options.js | 25 +- lib/rules/array-bracket-newline.js | 3 + lib/rules/array-bracket-spacing.js | 3 + lib/rules/array-element-newline.js | 3 + lib/rules/arrow-parens.js | 3 + lib/rules/arrow-spacing.js | 3 + lib/rules/block-spacing.js | 3 + lib/rules/brace-style.js | 3 + lib/rules/comma-dangle.js | 3 + lib/rules/comma-spacing.js | 3 + lib/rules/comma-style.js | 3 + lib/rules/computed-property-spacing.js | 3 + lib/rules/dot-location.js | 3 + lib/rules/eol-last.js | 3 + lib/rules/for-direction.js | 39 +- lib/rules/func-call-spacing.js | 3 + lib/rules/function-call-argument-newline.js | 3 + lib/rules/function-paren-newline.js | 3 + lib/rules/generator-star-spacing.js | 3 + lib/rules/implicit-arrow-linebreak.js | 3 + lib/rules/indent.js | 3 + lib/rules/jsx-quotes.js | 3 + lib/rules/key-spacing.js | 3 + lib/rules/keyword-spacing.js | 3 + lib/rules/linebreak-style.js | 3 + lib/rules/lines-around-comment.js | 3 + lib/rules/lines-between-class-members.js | 3 + lib/rules/logical-assignment-operators.js | 34 +- lib/rules/max-len.js | 3 + lib/rules/max-statements-per-line.js | 3 + lib/rules/multiline-ternary.js | 3 + lib/rules/new-parens.js | 3 + lib/rules/newline-per-chained-call.js | 3 + lib/rules/no-array-constructor.js | 91 +- lib/rules/no-confusing-arrow.js | 3 + lib/rules/no-console.js | 76 +- lib/rules/no-extra-parens.js | 3 + lib/rules/no-extra-semi.js | 3 + lib/rules/no-floating-decimal.js | 3 + lib/rules/no-invalid-this.js | 2 +- lib/rules/no-mixed-operators.js | 3 + lib/rules/no-mixed-spaces-and-tabs.js | 3 + lib/rules/no-multi-spaces.js | 3 + lib/rules/no-multiple-empty-lines.js | 3 + lib/rules/no-object-constructor.js | 51 +- lib/rules/no-promise-executor-return.js | 15 +- lib/rules/no-prototype-builtins.js | 92 +- lib/rules/no-restricted-imports.js | 85 +- lib/rules/no-restricted-properties.js | 43 +- lib/rules/no-tabs.js | 3 + lib/rules/no-trailing-spaces.js | 3 + lib/rules/no-whitespace-before-property.js | 3 + lib/rules/nonblock-statement-body-position.js | 3 + lib/rules/object-curly-newline.js | 3 + lib/rules/object-curly-spacing.js | 3 + lib/rules/object-property-newline.js | 3 + lib/rules/one-var-declaration-per-line.js | 3 + lib/rules/operator-linebreak.js | 3 + lib/rules/padded-blocks.js | 3 + lib/rules/padding-line-between-statements.js | 3 + lib/rules/quote-props.js | 3 + lib/rules/quotes.js | 3 + lib/rules/rest-spread-spacing.js | 3 + lib/rules/semi-spacing.js | 3 + lib/rules/semi-style.js | 3 + lib/rules/semi.js | 3 + lib/rules/space-before-blocks.js | 3 + lib/rules/space-before-function-paren.js | 3 + lib/rules/space-in-parens.js | 3 + lib/rules/space-infix-ops.js | 3 + lib/rules/space-unary-ops.js | 3 + lib/rules/spaced-comment.js | 3 + lib/rules/switch-colon-spacing.js | 3 + lib/rules/template-curly-spacing.js | 3 + lib/rules/template-tag-spacing.js | 3 + lib/rules/utils/ast-utils.js | 112 +- lib/rules/wrap-iife.js | 3 + lib/rules/wrap-regex.js | 3 + lib/rules/yield-star-spacing.js | 3 + lib/shared/severity.js | 49 + lib/source-code/source-code.js | 25 +- package.json | 29 +- packages/eslint-config-eslint/base.js | 2 +- packages/eslint-config-eslint/package.json | 4 +- packages/js/package.json | 2 +- packages/js/src/configs/eslint-all.js | 67 - templates/formatter-examples.md.ejs | 29 +- templates/rule-proposal.md | 1 - tests/bin/eslint.js | 55 +- tests/conf/eslint-all.js | 2 +- tests/fixtures/bad-examples.md | 34 + tests/fixtures/bin/empty.js | 0 tests/fixtures/bin/eslint.config-invalid.js | 3 + .../bin/eslint.config-promise-tick-throws.js | 22 + .../fixtures/bin/eslint.config-tick-throws.js | 24 + .../assignment--nested-and-3.js | 10 +- .../code-path-analysis/logical--and-qq.js | 22 + .../logical--if-mix-and-qq-1.js | 10 +- tests/fixtures/config-extends/.eslintrc | 12 - tests/fixtures/config-extends/array/.eslintrc | 14 - .../fixtures/config-extends/array/.eslintrc1 | 12 - .../fixtures/config-extends/array/.eslintrc2 | 9 - tests/fixtures/config-extends/deep.json | 11 - tests/fixtures/config-extends/error.json | 3 - tests/fixtures/config-extends/js/.eslintrc | 12 - tests/fixtures/config-extends/package.json | 7 - .../fixtures/config-extends/package/.eslintrc | 12 - .../config-extends/package2/.eslintrc | 12 - .../config-extends/package2/subdir/foo.js | 1 - .../config-extends/package3/.eslintrc | 12 - .../config-extends/package4/.eslintrc | 12 - .../resolving-relatively/.eslintrc.json | 3 - .../node_modules/a/index.js | 8 - .../node_modules/a/node_modules/b/index.js | 7 - .../config-extends/scoped-package/.eslintrc | 12 - .../config-extends/scoped-package2/.eslintrc | 12 - .../config-extends/scoped-package3/.eslintrc | 12 - .../config-extends/scoped-package3/foo.js | 1 - .../config-extends/scoped-package4/.eslintrc | 12 - .../config-extends/scoped-package5/.eslintrc | 12 - .../config-extends/scoped-package6/.eslintrc | 12 - .../config-extends/scoped-package7/.eslintrc | 12 - .../config-extends/scoped-package8/.eslintrc | 12 - .../config-extends/scoped-package9/.eslintrc | 12 - .../fixtures/config-extends/subdir/.eslintrc | 10 - .../subdir/subsubdir/deeper.json | 11 - .../subsubdir/subsubsubdir/deepest.json | 10 - tests/fixtures/good-examples.md | 33 + tests/lib/cli-engine/cli-engine.js | 11 +- tests/lib/cli.js | 162 +- tests/lib/config/flat-config-array.js | 92 +- tests/lib/eslint/eslint.js | 605 ++- tests/lib/eslint/flat-eslint.js | 874 +++- tests/lib/linter/apply-disable-directives.js | 1807 ++++++-- tests/lib/linter/config-comment-parser.js | 46 + tests/lib/linter/linter.js | 1907 +++++++- tests/lib/options.js | 14 + tests/lib/rules/accessor-pairs.js | 428 +- tests/lib/rules/array-bracket-newline.js | 100 +- tests/lib/rules/array-bracket-spacing.js | 92 +- tests/lib/rules/array-callback-return.js | 122 +- tests/lib/rules/array-element-newline.js | 44 +- tests/lib/rules/arrow-body-style.js | 4 +- tests/lib/rules/arrow-parens.js | 95 +- tests/lib/rules/arrow-spacing.js | 4 +- tests/lib/rules/block-scoped-var.js | 92 +- tests/lib/rules/block-spacing.js | 70 +- tests/lib/rules/brace-style.js | 62 +- tests/lib/rules/callback-return.js | 24 +- tests/lib/rules/camelcase.js | 302 +- tests/lib/rules/capitalized-comments.js | 2 +- tests/lib/rules/class-methods-use-this.js | 92 +- tests/lib/rules/comma-dangle.js | 478 +- tests/lib/rules/comma-spacing.js | 62 +- tests/lib/rules/comma-style.js | 50 +- tests/lib/rules/complexity.js | 166 +- tests/lib/rules/computed-property-spacing.js | 166 +- tests/lib/rules/consistent-return.js | 35 +- tests/lib/rules/consistent-this.js | 12 +- tests/lib/rules/constructor-super.js | 4 +- tests/lib/rules/curly.js | 83 +- tests/lib/rules/default-case-last.js | 2 +- tests/lib/rules/default-case.js | 2 +- tests/lib/rules/default-param-last.js | 4 +- tests/lib/rules/dot-location.js | 59 +- tests/lib/rules/dot-notation.js | 39 +- tests/lib/rules/eol-last.js | 2 +- tests/lib/rules/eqeqeq.js | 6 +- tests/lib/rules/for-direction.js | 24 +- tests/lib/rules/func-call-spacing.js | 46 +- tests/lib/rules/func-name-matching.js | 290 +- tests/lib/rules/func-names.js | 160 +- tests/lib/rules/func-style.js | 16 +- .../rules/function-call-argument-newline.js | 32 +- tests/lib/rules/function-paren-newline.js | 126 +- tests/lib/rules/generator-star-spacing.js | 16 +- tests/lib/rules/getter-return.js | 14 +- tests/lib/rules/global-require.js | 15 +- tests/lib/rules/grouped-accessor-pairs.js | 6 +- tests/lib/rules/guard-for-in.js | 2 +- tests/lib/rules/handle-callback-err.js | 12 +- tests/lib/rules/id-blacklist.js | 177 +- tests/lib/rules/id-denylist.js | 187 +- tests/lib/rules/id-length.js | 224 +- tests/lib/rules/id-match.js | 92 +- tests/lib/rules/implicit-arrow-linebreak.js | 14 +- tests/lib/rules/indent-legacy.js | 109 +- tests/lib/rules/indent.js | 242 +- tests/lib/rules/init-declarations.js | 60 +- tests/lib/rules/jsx-quotes.js | 11 +- tests/lib/rules/key-spacing.js | 102 +- tests/lib/rules/keyword-spacing.js | 1227 ++--- tests/lib/rules/line-comment-position.js | 2 +- tests/lib/rules/linebreak-style.js | 2 +- tests/lib/rules/lines-around-comment.js | 128 +- tests/lib/rules/lines-around-directive.js | 122 +- .../lib/rules/lines-between-class-members.js | 12 +- .../lib/rules/logical-assignment-operators.js | 198 +- tests/lib/rules/max-classes-per-file.js | 4 +- tests/lib/rules/max-depth.js | 28 +- tests/lib/rules/max-len.js | 90 +- tests/lib/rules/max-lines-per-function.js | 4 +- tests/lib/rules/max-lines.js | 2 +- tests/lib/rules/max-nested-callbacks.js | 8 +- tests/lib/rules/max-params.js | 6 +- tests/lib/rules/max-statements-per-line.js | 50 +- tests/lib/rules/max-statements.js | 40 +- tests/lib/rules/multiline-comment-style.js | 2 +- tests/lib/rules/multiline-ternary.js | 2 +- tests/lib/rules/new-cap.js | 28 +- tests/lib/rules/new-parens.js | 6 +- tests/lib/rules/newline-after-var.js | 68 +- tests/lib/rules/newline-before-return.js | 33 +- tests/lib/rules/newline-per-chained-call.js | 14 +- tests/lib/rules/no-alert.js | 27 +- tests/lib/rules/no-array-constructor.js | 391 +- tests/lib/rules/no-async-promise-executor.js | 4 +- tests/lib/rules/no-await-in-loop.js | 4 +- tests/lib/rules/no-bitwise.js | 8 +- tests/lib/rules/no-buffer-constructor.js | 2 +- tests/lib/rules/no-caller.js | 2 +- tests/lib/rules/no-case-declarations.js | 54 +- tests/lib/rules/no-catch-shadow.js | 11 +- tests/lib/rules/no-class-assign.js | 4 +- tests/lib/rules/no-compare-neg-zero.js | 2 +- tests/lib/rules/no-cond-assign.js | 6 +- tests/lib/rules/no-confusing-arrow.js | 4 +- tests/lib/rules/no-console.js | 396 +- tests/lib/rules/no-const-assign.js | 4 +- .../rules/no-constant-binary-expression.js | 18 +- tests/lib/rules/no-constant-condition.js | 32 +- tests/lib/rules/no-constructor-return.js | 6 +- tests/lib/rules/no-continue.js | 2 +- tests/lib/rules/no-control-regex.js | 22 +- tests/lib/rules/no-debugger.js | 2 +- tests/lib/rules/no-delete-var.js | 9 +- tests/lib/rules/no-div-regex.js | 2 +- tests/lib/rules/no-dupe-args.js | 15 +- tests/lib/rules/no-dupe-class-members.js | 6 +- tests/lib/rules/no-dupe-else-if.js | 2 +- tests/lib/rules/no-dupe-keys.js | 49 +- tests/lib/rules/no-duplicate-case.js | 2 +- tests/lib/rules/no-duplicate-imports.js | 4 +- tests/lib/rules/no-else-return.js | 115 +- tests/lib/rules/no-empty-character-class.js | 44 +- tests/lib/rules/no-empty-function.js | 36 +- tests/lib/rules/no-empty-pattern.js | 64 +- tests/lib/rules/no-empty-static-block.js | 4 +- tests/lib/rules/no-empty.js | 4 +- tests/lib/rules/no-eq-null.js | 2 +- tests/lib/rules/no-eval.js | 134 +- tests/lib/rules/no-ex-assign.js | 10 +- tests/lib/rules/no-extend-native.js | 33 +- tests/lib/rules/no-extra-bind.js | 28 +- tests/lib/rules/no-extra-boolean-cast.js | 54 +- tests/lib/rules/no-extra-label.js | 6 +- tests/lib/rules/no-extra-parens.js | 130 +- tests/lib/rules/no-extra-semi.js | 57 +- tests/lib/rules/no-fallthrough.js | 4 +- tests/lib/rules/no-floating-decimal.js | 4 +- tests/lib/rules/no-func-assign.js | 14 +- tests/lib/rules/no-global-assign.js | 22 +- tests/lib/rules/no-implicit-coercion.js | 62 +- tests/lib/rules/no-implicit-globals.js | 330 +- tests/lib/rules/no-implied-eval.js | 188 +- tests/lib/rules/no-import-assign.js | 18 +- tests/lib/rules/no-inline-comments.js | 14 +- tests/lib/rules/no-inner-declarations.js | 49 +- tests/lib/rules/no-invalid-regexp.js | 2 +- tests/lib/rules/no-invalid-this.js | 225 +- tests/lib/rules/no-irregular-whitespace.js | 288 +- tests/lib/rules/no-iterator.js | 10 +- tests/lib/rules/no-label-var.js | 2 +- tests/lib/rules/no-labels.js | 2 +- tests/lib/rules/no-lone-blocks.js | 73 +- tests/lib/rules/no-lonely-if.js | 4 +- tests/lib/rules/no-loop-func.js | 80 +- tests/lib/rules/no-loss-of-precision.js | 103 +- tests/lib/rules/no-magic-numbers.js | 151 +- .../rules/no-misleading-character-class.js | 109 +- tests/lib/rules/no-mixed-operators.js | 4 +- tests/lib/rules/no-mixed-requires.js | 2 +- tests/lib/rules/no-mixed-spaces-and-tabs.js | 26 +- tests/lib/rules/no-multi-assign.js | 34 +- tests/lib/rules/no-multi-spaces.js | 6 +- tests/lib/rules/no-multi-str.js | 12 +- tests/lib/rules/no-multiple-empty-lines.js | 14 +- tests/lib/rules/no-native-reassign.js | 15 +- tests/lib/rules/no-negated-condition.js | 2 +- tests/lib/rules/no-negated-in-lhs.js | 2 +- tests/lib/rules/no-nested-ternary.js | 2 +- tests/lib/rules/no-new-func.js | 10 +- .../lib/rules/no-new-native-nonconstructor.js | 4 +- tests/lib/rules/no-new-object.js | 8 +- tests/lib/rules/no-new-require.js | 2 +- tests/lib/rules/no-new-symbol.js | 4 +- tests/lib/rules/no-new-wrappers.js | 12 +- tests/lib/rules/no-new.js | 2 +- tests/lib/rules/no-nonoctal-decimal-escape.js | 9 +- tests/lib/rules/no-obj-calls.js | 155 +- tests/lib/rules/no-object-constructor.js | 268 +- tests/lib/rules/no-octal-escape.js | 9 +- tests/lib/rules/no-octal.js | 9 +- tests/lib/rules/no-param-reassign.js | 72 +- tests/lib/rules/no-path-concat.js | 2 +- tests/lib/rules/no-plusplus.js | 2 +- tests/lib/rules/no-process-env.js | 2 +- tests/lib/rules/no-process-exit.js | 2 +- tests/lib/rules/no-promise-executor-return.js | 715 ++- tests/lib/rules/no-proto.js | 12 +- tests/lib/rules/no-prototype-builtins.js | 158 +- tests/lib/rules/no-redeclare.js | 101 +- tests/lib/rules/no-regex-spaces.js | 10 +- tests/lib/rules/no-restricted-exports.js | 4 +- tests/lib/rules/no-restricted-globals.js | 39 +- tests/lib/rules/no-restricted-imports.js | 444 +- tests/lib/rules/no-restricted-modules.js | 10 +- tests/lib/rules/no-restricted-properties.js | 198 +- tests/lib/rules/no-restricted-syntax.js | 14 +- tests/lib/rules/no-return-assign.js | 14 +- tests/lib/rules/no-return-await.js | 4 +- tests/lib/rules/no-script-url.js | 12 +- tests/lib/rules/no-self-assign.js | 106 +- tests/lib/rules/no-self-compare.js | 8 +- tests/lib/rules/no-sequences.js | 19 +- tests/lib/rules/no-setter-return.js | 57 +- tests/lib/rules/no-shadow-restricted-names.js | 21 +- tests/lib/rules/no-shadow.js | 263 +- tests/lib/rules/no-spaced-func.js | 2 +- tests/lib/rules/no-sparse-arrays.js | 2 +- tests/lib/rules/no-sync.js | 2 +- tests/lib/rules/no-tabs.js | 2 +- .../lib/rules/no-template-curly-in-string.js | 40 +- tests/lib/rules/no-ternary.js | 2 +- tests/lib/rules/no-this-before-super.js | 10 +- tests/lib/rules/no-throw-literal.js | 22 +- tests/lib/rules/no-trailing-spaces.js | 16 +- tests/lib/rules/no-undef-init.js | 40 +- tests/lib/rules/no-undef.js | 146 +- tests/lib/rules/no-undefined.js | 22 +- tests/lib/rules/no-underscore-dangle.js | 164 +- tests/lib/rules/no-unexpected-multiline.js | 58 +- .../lib/rules/no-unmodified-loop-condition.js | 10 +- tests/lib/rules/no-unneeded-ternary.js | 32 +- tests/lib/rules/no-unreachable-loop.js | 4 +- tests/lib/rules/no-unreachable.js | 51 +- tests/lib/rules/no-unsafe-finally.js | 8 +- tests/lib/rules/no-unsafe-negation.js | 2 +- .../lib/rules/no-unsafe-optional-chaining.js | 10 +- tests/lib/rules/no-unused-expressions.js | 60 +- tests/lib/rules/no-unused-labels.js | 4 +- .../rules/no-unused-private-class-members.js | 4 +- tests/lib/rules/no-unused-vars.js | 351 +- tests/lib/rules/no-use-before-define.js | 385 +- tests/lib/rules/no-useless-backreference.js | 8 +- tests/lib/rules/no-useless-call.js | 26 +- tests/lib/rules/no-useless-catch.js | 12 +- tests/lib/rules/no-useless-computed-key.js | 22 +- tests/lib/rules/no-useless-concat.js | 14 +- tests/lib/rules/no-useless-constructor.js | 8 +- tests/lib/rules/no-useless-escape.js | 301 +- tests/lib/rules/no-useless-rename.js | 50 +- tests/lib/rules/no-useless-return.js | 25 +- tests/lib/rules/no-var.js | 89 +- tests/lib/rules/no-void.js | 8 +- tests/lib/rules/no-warning-comments.js | 2 +- .../rules/no-whitespace-before-property.js | 47 +- tests/lib/rules/no-with.js | 9 +- .../rules/nonblock-statement-body-position.js | 4 +- tests/lib/rules/object-curly-newline.js | 80 +- tests/lib/rules/object-curly-spacing.js | 262 +- tests/lib/rules/object-property-newline.js | 70 +- tests/lib/rules/object-shorthand.js | 54 +- .../lib/rules/one-var-declaration-per-line.js | 32 +- tests/lib/rules/one-var.js | 246 +- tests/lib/rules/operator-assignment.js | 4 +- tests/lib/rules/operator-linebreak.js | 62 +- tests/lib/rules/padded-blocks.js | 114 +- .../rules/padding-line-between-statements.js | 134 +- tests/lib/rules/prefer-arrow-callback.js | 4 +- tests/lib/rules/prefer-const.js | 127 +- tests/lib/rules/prefer-destructuring.js | 10 +- .../rules/prefer-exponentiation-operator.js | 32 +- tests/lib/rules/prefer-named-capture-group.js | 24 +- tests/lib/rules/prefer-numeric-literals.js | 28 +- tests/lib/rules/prefer-object-has-own.js | 6 +- tests/lib/rules/prefer-object-spread.js | 32 +- .../lib/rules/prefer-promise-reject-errors.js | 4 +- tests/lib/rules/prefer-reflect.js | 9 +- tests/lib/rules/prefer-regex-literals.js | 195 +- tests/lib/rules/prefer-rest-params.js | 4 +- tests/lib/rules/prefer-spread.js | 4 +- tests/lib/rules/prefer-template.js | 4 +- tests/lib/rules/quote-props.js | 76 +- tests/lib/rules/quotes.js | 191 +- tests/lib/rules/radix.js | 24 +- tests/lib/rules/require-atomic-updates.js | 6 +- tests/lib/rules/require-await.js | 24 +- tests/lib/rules/require-jsdoc.js | 38 +- tests/lib/rules/require-unicode-regexp.js | 49 +- tests/lib/rules/require-yield.js | 4 +- tests/lib/rules/rest-spread-spacing.js | 64 +- tests/lib/rules/semi-spacing.js | 32 +- tests/lib/rules/semi-style.js | 56 +- tests/lib/rules/semi.js | 412 +- tests/lib/rules/sort-imports.js | 4 +- tests/lib/rules/sort-keys.js | 114 +- tests/lib/rules/sort-vars.js | 69 +- tests/lib/rules/space-before-blocks.js | 92 +- .../lib/rules/space-before-function-paren.js | 74 +- tests/lib/rules/space-in-parens.js | 18 +- tests/lib/rules/space-infix-ops.js | 64 +- tests/lib/rules/space-unary-ops.js | 54 +- tests/lib/rules/spaced-comment.js | 2 +- tests/lib/rules/strict.js | 199 +- tests/lib/rules/switch-colon-spacing.js | 2 +- tests/lib/rules/symbol-description.js | 9 +- tests/lib/rules/template-curly-spacing.js | 4 +- tests/lib/rules/template-tag-spacing.js | 4 +- tests/lib/rules/unicode-bom.js | 2 +- tests/lib/rules/use-isnan.js | 20 +- tests/lib/rules/valid-jsdoc.js | 60 +- tests/lib/rules/valid-typeof.js | 14 +- tests/lib/rules/vars-on-top.js | 60 +- tests/lib/rules/wrap-iife.js | 33 +- tests/lib/rules/wrap-regex.js | 2 +- tests/lib/rules/yield-star-spacing.js | 4 +- tests/lib/rules/yoda.js | 126 +- tests/tools/check-rule-examples.js | 96 + tools/check-rule-examples.js | 184 + tools/update-readme.js | 2 +- 659 files changed, 23588 insertions(+), 13104 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/docs.yml create mode 100644 .github/renovate.json5 create mode 100644 docs/src/_data/rules_categories.js create mode 100644 docs/src/extend/plugin-migration-flat-config.md create mode 100644 docs/tools/markdown-it-rule-example.js create mode 100644 lib/shared/severity.js create mode 100644 tests/fixtures/bad-examples.md create mode 100644 tests/fixtures/bin/empty.js create mode 100644 tests/fixtures/bin/eslint.config-invalid.js create mode 100644 tests/fixtures/bin/eslint.config-promise-tick-throws.js create mode 100644 tests/fixtures/bin/eslint.config-tick-throws.js create mode 100644 tests/fixtures/code-path-analysis/logical--and-qq.js delete mode 100644 tests/fixtures/config-extends/.eslintrc delete mode 100644 tests/fixtures/config-extends/array/.eslintrc delete mode 100644 tests/fixtures/config-extends/array/.eslintrc1 delete mode 100644 tests/fixtures/config-extends/array/.eslintrc2 delete mode 100644 tests/fixtures/config-extends/deep.json delete mode 100644 tests/fixtures/config-extends/error.json delete mode 100644 tests/fixtures/config-extends/js/.eslintrc delete mode 100644 tests/fixtures/config-extends/package.json delete mode 100644 tests/fixtures/config-extends/package/.eslintrc delete mode 100644 tests/fixtures/config-extends/package2/.eslintrc delete mode 100644 tests/fixtures/config-extends/package2/subdir/foo.js delete mode 100644 tests/fixtures/config-extends/package3/.eslintrc delete mode 100644 tests/fixtures/config-extends/package4/.eslintrc delete mode 100644 tests/fixtures/config-extends/resolving-relatively/.eslintrc.json delete mode 100644 tests/fixtures/config-extends/resolving-relatively/node_modules/a/index.js delete mode 100644 tests/fixtures/config-extends/resolving-relatively/node_modules/a/node_modules/b/index.js delete mode 100644 tests/fixtures/config-extends/scoped-package/.eslintrc delete mode 100644 tests/fixtures/config-extends/scoped-package2/.eslintrc delete mode 100644 tests/fixtures/config-extends/scoped-package3/.eslintrc delete mode 100644 tests/fixtures/config-extends/scoped-package3/foo.js delete mode 100644 tests/fixtures/config-extends/scoped-package4/.eslintrc delete mode 100644 tests/fixtures/config-extends/scoped-package5/.eslintrc delete mode 100644 tests/fixtures/config-extends/scoped-package6/.eslintrc delete mode 100644 tests/fixtures/config-extends/scoped-package7/.eslintrc delete mode 100644 tests/fixtures/config-extends/scoped-package8/.eslintrc delete mode 100644 tests/fixtures/config-extends/scoped-package9/.eslintrc delete mode 100644 tests/fixtures/config-extends/subdir/.eslintrc delete mode 100644 tests/fixtures/config-extends/subdir/subsubdir/deeper.json delete mode 100644 tests/fixtures/config-extends/subdir/subsubdir/subsubsubdir/deepest.json create mode 100644 tests/fixtures/good-examples.md create mode 100644 tests/tools/check-rule-examples.js create mode 100644 tools/check-rule-examples.js diff --git a/.github/ISSUE_TEMPLATE/change.yml b/.github/ISSUE_TEMPLATE/change.yml index c7a21127eba..675d5e4ca23 100644 --- a/.github/ISSUE_TEMPLATE/change.yml +++ b/.github/ISSUE_TEMPLATE/change.yml @@ -1,4 +1,4 @@ -name: "\U0001F4DD Request a change (not rule-related)" +name: "\U0001F680 Request a change (not rule-related)" description: "Request a change that is not a bug fix, rule change, or new rule" title: "Change Request: (fill in)" labels: diff --git a/.github/ISSUE_TEMPLATE/docs.yml b/.github/ISSUE_TEMPLATE/docs.yml new file mode 100644 index 00000000000..88ed3c1440d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/docs.yml @@ -0,0 +1,46 @@ +name: "\U0001F4DD Docs" +description: "Request an improvement to documentation" +title: "Docs: (fill in)" +labels: + - documentation +body: +- type: markdown + attributes: + value: By opening an issue, you agree to abide by the [Open JS Foundation Code of Conduct](https://eslint.org/conduct). +- type: textarea + attributes: + label: Docs page(s) + description: | + What page(s) are you suggesting be changed or created? + placeholder: | + e.g. https://eslint.org/docs/latest/use/getting-started + validations: + required: true +- type: textarea + attributes: + label: What documentation issue do you want to solve? + description: | + Please explain your issue in as much detail as possible. + placeholder: | + The ESLint docs currently... + validations: + required: true +- type: textarea + attributes: + label: What do you think is the correct solution? + description: | + Please explain how you'd like to change the ESLint docs to address the problem. + placeholder: | + I'd like the ESLint docs to... + validations: + required: true +- type: checkboxes + attributes: + label: Participation + options: + - label: I am willing to submit a pull request for this change. + required: false +- type: textarea + attributes: + label: Additional comments + description: Is there anything else that's important for the team to know? diff --git a/.github/ISSUE_TEMPLATE/new-rule.yml b/.github/ISSUE_TEMPLATE/new-rule.yml index 9ef139fbb23..4c4463466ed 100644 --- a/.github/ISSUE_TEMPLATE/new-rule.yml +++ b/.github/ISSUE_TEMPLATE/new-rule.yml @@ -26,7 +26,6 @@ body: options: - Warns about a potential problem - Suggests an alternate way of doing something - - Enforces a formatting/stylistic preference validations: required: true - type: textarea diff --git a/.github/ISSUE_TEMPLATE/rule-change.yml b/.github/ISSUE_TEMPLATE/rule-change.yml index 86758cba555..2706a1180ca 100644 --- a/.github/ISSUE_TEMPLATE/rule-change.yml +++ b/.github/ISSUE_TEMPLATE/rule-change.yml @@ -15,7 +15,7 @@ body: required: true - type: dropdown attributes: - label: What change to do you want to make? + label: What change do you want to make? options: - Generate more warnings - Generate fewer warnings diff --git a/.github/renovate.json5 b/.github/renovate.json5 new file mode 100644 index 00000000000..8163f03867c --- /dev/null +++ b/.github/renovate.json5 @@ -0,0 +1,42 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended", + ":approveMajorUpdates", + ":semanticCommitScopeDisabled" + ], + "ignorePresets": [":semanticPrefixFixDepsChoreOthers"], + "labels": ["dependencies"], + + // Wait well over npm's three day window for any new package as a precaution against malicious publishes + // https://docs.npmjs.com/policies/unpublish/#packages-published-less-than-72-hours-ago + "minimumReleaseAge": "7 days", + + "packageRules": [ + { + "description": "Use the deps:actions label for github-action manager updates (this means Renovate's github-action manager).", + "addLabels": ["deps:actions"], + "matchManagers": ["github-actions"] + }, + { + "description": "Use the deps:npm label for npm manager packages (this means Renovate's npm manager).", + "addLabels": ["deps:npm"], + "matchManagers": ["npm"] + }, + { + "description": "Group Babel packages into a single PR.", + "groupName": "babel", + "matchPackagePrefixes": ["@babel", "babel-"] + }, + { + "description": "Group wdio packages into a single PR.", + "groupName": "wdio", + "matchPackagePrefixes": ["@wdio"] + }, + { + "description": "Group metascraper packages into a single PR.", + "groupName": "metascraper", + "matchPackagePrefixes": ["metascraper"] + } + ] +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7602b838037..ff0f1953dcc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 'lts/*' - name: Install Packages @@ -33,6 +33,8 @@ jobs: run: npm run lint:scss - name: Lint Docs JS Files run: node Makefile lintDocsJS + - name: Check Rule Examples + run: node Makefile checkRuleExamples - name: Build Docs Website working-directory: docs run: npm run build @@ -45,7 +47,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node: [20.x, 19.x, 18.x, 17.x, 16.x, 14.x, 12.x, "12.22.0"] + node: [21.x, 20.x, 19.x, 18.x, 17.x, 16.x, 14.x, 12.x, "12.22.0"] include: - os: windows-latest node: "lts/*" @@ -54,7 +56,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node }} - name: Install Packages @@ -69,7 +71,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: '16' - name: Install Packages diff --git a/.github/workflows/update-readme.yml b/.github/workflows/update-readme.yml index 43b783d3fca..5e3fb97c48d 100644 --- a/.github/workflows/update-readme.yml +++ b/.github/workflows/update-readme.yml @@ -14,7 +14,7 @@ jobs: token: ${{ secrets.WORKFLOW_PUSH_BOT_TOKEN }} - name: Set up Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 - name: Install npm packages run: npm install diff --git a/CHANGELOG.md b/CHANGELOG.md index efb9cc93035..f8b0969f950 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,106 @@ +v8.55.0 - December 1, 2023 + +* [`eb8950c`](https://github.com/eslint/eslint/commit/eb8950c3b811c9163b9aae23af8b6266ad98b295) chore: upgrade @eslint/js@8.55.0 (#17811) (Milos Djermanovic) +* [`93df384`](https://github.com/eslint/eslint/commit/93df3849a7a25ebe0502000bf0bfb80a6613a5ae) chore: package.json update for @eslint/js release (Jenkins) +* [`fe4b954`](https://github.com/eslint/eslint/commit/fe4b9545a83e9aca7ba4bb77bc9c868d57de777f) chore: upgrade @eslint/eslintrc@2.1.4 (#17799) (Milos Djermanovic) +* [`8c9e6c1`](https://github.com/eslint/eslint/commit/8c9e6c100a6eb69da292463293b3b48cff911a01) feat: importNamePattern option in no-restricted-imports (#17721) (Tanuj Kanti) +* [`83ece2a`](https://github.com/eslint/eslint/commit/83ece2afc2dc6c49efe82678663fe4cba590c0e5) docs: fix typo `--rules` -> `--rule` (#17806) (OKURA Masafumi) +* [`bd8911d`](https://github.com/eslint/eslint/commit/bd8911db85c7a1127543c9212c8cea47a5cb687d) ci: pin Node.js 21.2.0 (#17809) (Milos Djermanovic) +* [`b29a16b`](https://github.com/eslint/eslint/commit/b29a16b22f234f6134475efb6c7be5ac946556ee) chore: fix several `cli` tests to run in the intended flat config mode (#17797) (Milos Djermanovic) +* [`fffca5c`](https://github.com/eslint/eslint/commit/fffca5c362bcd205dbf79d1bb52834f8a98fc6bd) docs: remove "Open in Playground" buttons for removed rules (#17791) (Francesco Trotta) +* [`a6d9442`](https://github.com/eslint/eslint/commit/a6d9442a9ab34d5d19f78d8c8fd0767a1237bfe3) docs: fix correct/incorrect examples of rules (#17789) (Tanuj Kanti) +* [`383e999`](https://github.com/eslint/eslint/commit/383e99928d7ce649ec9030c9856b03fbac0c3501) docs: update and fix examples for `no-unused-vars` (#17788) (Tanuj Kanti) +* [`5a8efd5`](https://github.com/eslint/eslint/commit/5a8efd5b7ad13eb320a1f468d1d4ab3c8ab99214) docs: add specific stylistic rule for each deprecated rule (#17778) (Etienne) +* [`de165c1`](https://github.com/eslint/eslint/commit/de165c108203c6703516ac651f5b4cac5b241804) chore: remove unused config-extends fixtures (#17781) (Milos Djermanovic) +* [`d4304b8`](https://github.com/eslint/eslint/commit/d4304b8b66eac870ffbf4840d84add8a123b25fc) chore: remove formatting/stylistic rules from new rule templates (#17780) (Francesco Trotta) +* [`21024fe`](https://github.com/eslint/eslint/commit/21024fe2029420b413bed11d23761c87e9a02a1a) chore: check rule examples for syntax errors (#17718) (Francesco Trotta) + +v8.54.0 - November 17, 2023 + +* [`d644de9`](https://github.com/eslint/eslint/commit/d644de9a4b593b565617303a095bc9aa69e7b768) chore: upgrade @eslint/js@8.54.0 (#17773) (Milos Djermanovic) +* [`1e6e314`](https://github.com/eslint/eslint/commit/1e6e31415cc429a3a9fc64b2ec03df0e0ec0c91b) chore: package.json update for @eslint/js release (Jenkins) +* [`98926e6`](https://github.com/eslint/eslint/commit/98926e6e7323e5dd12a9f016cb558144296665af) fix: Ensure that extra data is not accidentally stored in the cache file (#17760) (Milos Djermanovic) +* [`a7a883b`](https://github.com/eslint/eslint/commit/a7a883bd6ba4f140b60cbbb2be5b53d750f6c8db) feat: for-direction rule add check for condition in reverse order (#17755) (Angelo Annunziata) +* [`1452dc9`](https://github.com/eslint/eslint/commit/1452dc9f12c45c05d7c569f737221f0d988ecef1) feat: Add suggestions to no-console (#17680) (Joel Mathew Koshy) +* [`6fb8805`](https://github.com/eslint/eslint/commit/6fb8805310afe7476d6c404f172177a6d15fcf11) chore: Fixed grammar in issue_templates/rule_change (#17770) (Joel Mathew Koshy) +* [`becfdd3`](https://github.com/eslint/eslint/commit/becfdd39b25d795e56c9a13eb3e77af6b9c86e8a) docs: Make clear when rules are removed (#17728) (Nicholas C. Zakas) +* [`e8cf9f6`](https://github.com/eslint/eslint/commit/e8cf9f6a524332293f8b2c90a2db4a532e47d919) fix: Make dark scroll bar in dark theme (#17753) (Pavel) +* [`85db724`](https://github.com/eslint/eslint/commit/85db7243ddb8706ed60ab64a7ddf604d0d7de493) chore: upgrade `markdownlint` to 0.31.1 (#17754) (Nitin Kumar) +* [`21ebf8a`](https://github.com/eslint/eslint/commit/21ebf8a811be9f4b009cf70a10be5062d4fdc736) feat: update `no-array-constructor` rule (#17711) (Francesco Trotta) +* [`05d6e99`](https://github.com/eslint/eslint/commit/05d6e99153ed6d94eb30f46c57609371918a41f3) docs: update "Submit a Pull Request" page (#17712) (Francesco Trotta) +* [`eb2279e`](https://github.com/eslint/eslint/commit/eb2279e5148cee8fdea7dae614f4f8af7a2d06c3) docs: display info about deprecated rules (#17749) (Percy Ma) +* [`6d470d2`](https://github.com/eslint/eslint/commit/6d470d2e74535761bd56dcb1c021b463ef9e8a9c) chore: update dependency recast to ^0.23.0 (#17736) (renovate[bot]) +* [`b7121b5`](https://github.com/eslint/eslint/commit/b7121b590d578c9c9b38ee481313317f30e54817) chore: update dependency markdownlint-cli to ^0.37.0 (#17735) (renovate[bot]) +* [`633b9a1`](https://github.com/eslint/eslint/commit/633b9a19752b6a22ab4d6c824f27a75ac0e4151b) chore: update dependency regenerator-runtime to ^0.14.0 (#17739) (renovate[bot]) +* [`acac16f`](https://github.com/eslint/eslint/commit/acac16fdf8540f7ba86cf637e3c1b253bd35a268) chore: update dependency vite-plugin-commonjs to ^0.10.0 (#17740) (renovate[bot]) +* [`ba8ca7e`](https://github.com/eslint/eslint/commit/ba8ca7e3debcba68ee7015b9221cf5acd7870206) chore: add .github/renovate.json5 (#17567) (Josh Goldberg ✨) +* [`3cbeaad`](https://github.com/eslint/eslint/commit/3cbeaad7b943c153937ce34365cec2c406f2b98b) fix: Use `cwd` constructor option as config `basePath` in Linter (#17705) (Milos Djermanovic) +* [`d245326`](https://github.com/eslint/eslint/commit/d24532601e64714ac5d08507e05aa5c14ecd1d5a) docs: Correct working in migrating plugin docs (#17722) (Filip Tammergård) +* [`5454c22`](https://github.com/eslint/eslint/commit/5454c22b24f39be2dac7f28cfcfdb6c753faaf4e) Revert "chore: remove metascraper (#17707)" (#17708) (Milos Djermanovic) + +v8.53.0 - November 3, 2023 + +* [`ba4d4d5`](https://github.com/eslint/eslint/commit/ba4d4d567a82554250dd8c7933322824e6a73944) chore: remove metascraper (#17707) (Milos Djermanovic) +* [`0d07338`](https://github.com/eslint/eslint/commit/0d0733882944b4849d71a40723c251213698cef9) chore: Update dependencies (#17706) (Milos Djermanovic) +* [`93256a3`](https://github.com/eslint/eslint/commit/93256a32e312f3f4e5c532762df71bdc06bded20) chore: package.json update for @eslint/js release (ESLint Jenkins) +* [`ab8c60d`](https://github.com/eslint/eslint/commit/ab8c60d4f859cec787b5a12f7271b40e666235f5) docs: change position of return to top button (#17688) (Tanuj Kanti) +* [`528e1c0`](https://github.com/eslint/eslint/commit/528e1c00dc2aa8636e5b706c4270dc655cfa17e3) feat: Deprecate formatting rules (#17696) (Nicholas C. Zakas) +* [`485ec7d`](https://github.com/eslint/eslint/commit/485ec7d08ed2040c292f52bf9b9152f6c8ef4809) test: fix ESLint tests for caching (#17699) (Milos Djermanovic) +* [`c0b11dd`](https://github.com/eslint/eslint/commit/c0b11ddb9f8aacc64c3933b9f278939aa7bea481) feat: Add suggestions for no-prototype-builtins (#17677) (Yonathan Randolph) +* [`4fc44c0`](https://github.com/eslint/eslint/commit/4fc44c0b8c5dca466bffdfe01dfd80794d7762b7) docs: update twitter icon to new X icon (#17687) (Tanuj Kanti) +* [`1ad6257`](https://github.com/eslint/eslint/commit/1ad6257744d63281235fcc33288394b1d69b34ce) fix: ensure that exit code for fatal errors is not overwritten (#17683) (Milos Djermanovic) +* [`4164b2c`](https://github.com/eslint/eslint/commit/4164b2ceec89726b18ea0b0e34fab05735d55a09) docs: Update README (GitHub Actions Bot) +* [`8651895`](https://github.com/eslint/eslint/commit/8651895ca7ae15e13d74c8be67d9eebd63a7ce1f) docs: Fix tabs in rule examples (#17653) (Francesco Trotta) +* [`3aec1c5`](https://github.com/eslint/eslint/commit/3aec1c55ba2c6d2833e1c0afe0a58f0cc6bbc0a4) docs: explained rule fixers and suggestions (#17657) (Josh Goldberg ✨) +* [`db06a7f`](https://github.com/eslint/eslint/commit/db06a7ff7992a74368f03d1f21beb00df0407021) ci: bump actions/setup-node from 3 to 4 (#17676) (dependabot[bot]) +* [`b329ea7`](https://github.com/eslint/eslint/commit/b329ea748dff45f11c7e218208244dc24fcb5c8f) fix: add `;` after JSX nodes in `no-object-constructor` autofix (#17672) (Francesco Trotta) +* [`994596b`](https://github.com/eslint/eslint/commit/994596b07f5ff20a615a4be1ea03e5fd59cdb84b) ci: run tests in Node.js 21 (#17673) (Francesco Trotta) + +v8.52.0 - October 20, 2023 + +* [`6d1f0c2`](https://github.com/eslint/eslint/commit/6d1f0c2da0309c06c21149b8d71a8f439a70d7e8) chore: upgrade @eslint/js@8.52.0 (#17671) (Milos Djermanovic) +* [`d63d4fe`](https://github.com/eslint/eslint/commit/d63d4fe0942e6747ab60e758aa36076f43041a30) chore: package.json update for @eslint/js release (ESLint Jenkins) +* [`476d58a`](https://github.com/eslint/eslint/commit/476d58a584d5d2db003c4c22ffee90e63566164d) docs: Add note about invalid CLI flags when using flat config. (#17664) (Nicholas C. Zakas) +* [`5de9637`](https://github.com/eslint/eslint/commit/5de9637fc925729a83d5a5e9e868a41792a184e3) fix: Ensure shared references in rule configs are separated (#17666) (Nicholas C. Zakas) +* [`f30cefe`](https://github.com/eslint/eslint/commit/f30cefee6bda2789ede18e1664b84c2638ea1bb5) test: fix FlatESLint tests for caching (#17658) (Milos Djermanovic) +* [`ef650cb`](https://github.com/eslint/eslint/commit/ef650cb612510bcfa1379c1f0af56dd563b3a705) test: update tests for no-promise-executor-return (#17661) (Milos Djermanovic) +* [`70648ee`](https://github.com/eslint/eslint/commit/70648ee49c07f7b533d09f6bf8a5291e5a5a8601) feat: report-unused-disable-directive to report unused eslint-enable (#17611) (Yosuke Ota) +* [`dcfe573`](https://github.com/eslint/eslint/commit/dcfe5739c374c9d7ed21f14027870ec0fd453661) fix: add preceding semicolon in suggestions of `no-object-constructor` (#17649) (Francesco Trotta) +* [`660ed3a`](https://github.com/eslint/eslint/commit/660ed3afd128ad529234a855345629982caf1bc7) docs: Plugin flat config migration guide (#17640) (Nicholas C. Zakas) +* [`a58aa20`](https://github.com/eslint/eslint/commit/a58aa200fccedae7e2e9b6129246f2cedab14f8d) docs: fix examples for several rules (#17645) (Milos Djermanovic) +* [`179929b`](https://github.com/eslint/eslint/commit/179929bd46892f18f2aef0c159d5cc361cb69987) docs: Remove trailing newline from the code of Playground links (#17641) (Francesco Trotta) +* [`f8e5c30`](https://github.com/eslint/eslint/commit/f8e5c30636450d4a8baf51f0e227685e6d77ac64) docs: Update README (GitHub Actions Bot) +* [`b7ef2f3`](https://github.com/eslint/eslint/commit/b7ef2f34fe12b68a366e1b4bf5f64d7332c6e72e) docs: Enable pretty code formatter output (#17635) (Nicholas C. Zakas) +* [`0bcb9a8`](https://github.com/eslint/eslint/commit/0bcb9a8db608a3d0bd2645f99e0707b9a9bbaaf0) docs: Fix syntax errors in rule examples (#17633) (Francesco Trotta) +* [`61b9083`](https://github.com/eslint/eslint/commit/61b90839633ef300ac7707a651f65f532e65f42d) docs: Make no-continue example code work (#17643) (Zhongyuan Zhou) +* [`9fafe45`](https://github.com/eslint/eslint/commit/9fafe450c31ed9b6bdd9dcd6c115255943b8c1c2) docs: upgrade to 11ty 2.0 (#17632) (Percy Ma) +* [`ff8e4bf`](https://github.com/eslint/eslint/commit/ff8e4bf327b5c92b0623b0fc5f8f101954f785db) docs: Update README (GitHub Actions Bot) +* [`fab249a`](https://github.com/eslint/eslint/commit/fab249ae6addac2ee18cd81cee80916010bb469e) docs: Update README (GitHub Actions Bot) +* [`392305b`](https://github.com/eslint/eslint/commit/392305bf4797e3ebc696dfca48bd874741fca845) docs: Update `no-irregular-whitespace` and fix examples (#17626) (Francesco Trotta) +* [`6b8acfb`](https://github.com/eslint/eslint/commit/6b8acfb770589f3941df41c3910d3b8ffc3e1e45) docs: Add real whitespace to `no-trailing-spaces` examples (#17630) (Francesco Trotta) +* [`1000187`](https://github.com/eslint/eslint/commit/1000187e00949332babcee4d37d46c96a6a554a8) docs: Fix examples in `unicode-bom` (#17631) (Francesco Trotta) +* [`000290c`](https://github.com/eslint/eslint/commit/000290c4c923cc1473e21b4bdbdc0c42765ef7dd) docs: Update README (GitHub Actions Bot) + +v8.51.0 - October 6, 2023 + +* [`1ef39ea`](https://github.com/eslint/eslint/commit/1ef39ea5b884453be717ebc929155d7eb584dcbf) chore: upgrade @eslint/js@8.51.0 (#17624) (Milos Djermanovic) +* [`f8c7403`](https://github.com/eslint/eslint/commit/f8c7403255c11e99c402860aef3c0179f2b16628) chore: package.json update for @eslint/js release (ESLint Jenkins) +* [`f976b2f`](https://github.com/eslint/eslint/commit/f976b2f7bfe7cc78bb649f8b37e90fd519ff3bcc) fix: make rule severity case-sensitive in flat config (#17619) (Milos Djermanovic) +* [`0edfe36`](https://github.com/eslint/eslint/commit/0edfe369aa5bd80a98053022bb4c6b1ea0155f44) fix: Ensure crash error messages are not duplicated (#17584) (Nicholas C. Zakas) +* [`ee5be81`](https://github.com/eslint/eslint/commit/ee5be81fa3c4fe801c2f653854f098ed6a84dcef) docs: default to `sourceType: "module"` in rule examples (#17615) (Francesco Trotta) +* [`dd79abc`](https://github.com/eslint/eslint/commit/dd79abc0c1857b1d765acc312c0d6518e40d31c9) fix: `eslint-disable` to be able to parse quoted rule names (#17612) (Yosuke Ota) +* [`d2f6801`](https://github.com/eslint/eslint/commit/d2f68019b8882278877801c5ef2f74d55e2a10c1) fix: Ensure correct code path for && followed by ?? (#17618) (Nicholas C. Zakas) +* [`2665552`](https://github.com/eslint/eslint/commit/2665552ba0057e8603f9fbece0fd236f189f5cf3) test: fix flat config linter tests to use Linter in flat config mode (#17616) (Milos Djermanovic) +* [`1aa26df`](https://github.com/eslint/eslint/commit/1aa26df9fbcfdf5b895743c6d2d3a216479544b1) docs: Add more examples for multiline-ternary (#17610) (George Ashiotis) +* [`47d0b44`](https://github.com/eslint/eslint/commit/47d0b446964f44d70b9457ecc368e721e1dc7c11) docs: Update README (GitHub Actions Bot) +* [`dbf831e`](https://github.com/eslint/eslint/commit/dbf831e31f8eea0bc94df96cd33255579324b66e) docs: use generated og image (#17601) (Percy Ma) +* [`0a9c433`](https://github.com/eslint/eslint/commit/0a9c43339a4adef24ef83034d0b078dd279cc977) feat: Add `--no-warn-ignored` CLI option for flat config (#17569) (Domantas Petrauskas) +* [`1866da5`](https://github.com/eslint/eslint/commit/1866da5e1d931787256ecb825a803cac5835b71c) docs: Update README (GitHub Actions Bot) +* [`7b77bcc`](https://github.com/eslint/eslint/commit/7b77bccbb51bd36b2d20fea61bc782545c4029b3) chore: Refactor CodePathState (#17510) (Nicholas C. Zakas) +* [`977e67e`](https://github.com/eslint/eslint/commit/977e67ec274a05cb7391665b5e3453e7f72f72b2) feat: logical-assignment-operators to report expressions with 3 operands (#17600) (Yosuke Ota) +* [`bc77c9a`](https://github.com/eslint/eslint/commit/bc77c9af12539f350ef19e30611a153a5b869c6b) chore: Document and refactor ForkContext (#17566) (Nicholas C. Zakas) +* [`24e1f14`](https://github.com/eslint/eslint/commit/24e1f140ec68659e55c1ace0d7500addb135a2b4) chore: Refactor and document CodePath (#17558) (Nicholas C. Zakas) + v8.50.0 - September 22, 2023 * [`f8a8a2d`](https://github.com/eslint/eslint/commit/f8a8a2d6b45c82f94a574623759b6e3d2af193f3) chore: upgrade @eslint/js@8.50.0 (#17599) (Milos Djermanovic) diff --git a/Makefile.js b/Makefile.js index 7978369c0b0..5e5352895bc 100644 --- a/Makefile.js +++ b/Makefile.js @@ -16,7 +16,6 @@ const checker = require("npm-license"), glob = require("glob"), marked = require("marked"), matter = require("gray-matter"), - markdownlint = require("markdownlint"), os = require("os"), path = require("path"), semver = require("semver"), @@ -69,7 +68,7 @@ const NODE = "node ", // intentional extra space // Utilities - intentional extra space at the end of each string MOCHA = `${NODE_MODULES}mocha/bin/_mocha `, - ESLINT = `${NODE} bin/eslint.js --report-unused-disable-directives `, + ESLINT = `${NODE} bin/eslint.js `, // Files RULE_FILES = glob.sync("lib/rules/*.js").filter(filePath => path.basename(filePath) !== "index.js"), @@ -214,9 +213,11 @@ function generateRuleIndexPage() { }; if (rule.meta.deprecated) { - ruleTypesData.deprecated.rules.push({ + ruleTypesData.deprecated.push({ name: basename, - replacedBy: rule.meta.replacedBy || [] + replacedBy: rule.meta.replacedBy || [], + fixable: !!rule.meta.fixable, + hasSuggestions: !!rule.meta.hasSuggestions }); } else { const output = { @@ -226,22 +227,18 @@ function generateRuleIndexPage() { fixable: !!rule.meta.fixable, hasSuggestions: !!rule.meta.hasSuggestions }, - ruleType = ruleTypesData.types.find(c => c.name === rule.meta.type); + ruleType = ruleTypesData.types[rule.meta.type]; - if (!ruleType.rules) { - ruleType.rules = []; - } - - ruleType.rules.push(output); + ruleType.push(output); } }); - // `.rules` will be `undefined` if all rules in category are deprecated. - ruleTypesData.types = ruleTypesData.types.filter(ruleType => !!ruleType.rules); + ruleTypesData.types = Object.fromEntries( + Object.entries(ruleTypesData.types).filter(([, value]) => value && value.length > 0) + ); JSON.stringify(ruleTypesData, null, 4).to(docsSiteOutputFile); JSON.stringify(meta, null, 4).to(docsSiteMetaOutputFile); - } /** @@ -435,6 +432,7 @@ function getFirstVersionOfDeletion(filePath) { * @private */ function lintMarkdown(files) { + const markdownlint = require("markdownlint"); const config = yaml.load(fs.readFileSync(path.join(__dirname, "./.markdownlint.yml"), "utf8")), result = markdownlint.sync({ files, @@ -830,7 +828,7 @@ target.checkRuleFiles = function() { // check deprecated if (ruleDef.meta.deprecated && !hasDeprecatedInfo()) { - console.error(`Missing deprecated information in ${basename} rule code or README.md. Please write @deprecated tag in code or 「This rule was deprecated in ESLint ...」 in README.md.`); + console.error(`Missing deprecated information in ${basename} rule code or README.md. Please write @deprecated tag in code and「This rule was deprecated in ESLint ...」 in README.md.`); errors++; } @@ -869,6 +867,17 @@ target.checkRuleFiles = function() { }; +target.checkRuleExamples = function() { + const { execFileSync } = require("child_process"); + + // We don't need the stack trace of execFileSync if the command fails. + try { + execFileSync(process.execPath, ["tools/check-rule-examples.js", "docs/src/rules/*.md"], { stdio: "inherit" }); + } catch { + exit(1); + } +}; + target.checkLicenses = function() { /** diff --git a/README.md b/README.md index 0b829619324..227d40c7cc5 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ We are now at or near 100% compatibility with JSCS. If you try ESLint and believ ### Does Prettier replace ESLint? -No, ESLint does both traditional linting (looking for problematic patterns) and style checking (enforcement of conventions). You can use ESLint for everything, or you can combine both using Prettier to format your code and ESLint to catch possible errors. +No, ESLint and Prettier have diffent jobs: ESLint is a linter (looking for problematic patterns) and Prettier is a code formatter. Using both tools is common, refer to [Prettier's documentation](https://prettier.io/docs/en/install#eslint-and-other-linters) to learn how to configure them to work well with each other. ### Why can't ESLint find my plugins? @@ -209,12 +209,12 @@ The people who manage releases, review feature requests, and meet regularly to e
-
+Nicholas C. Zakas's Avatar
Nicholas C. Zakas
-
+Milos Djermanovic's Avatar
Milos Djermanovic
@@ -225,12 +225,12 @@ The people who review and implement new features.
-
+唯然's Avatar
唯然
-
+Nitin Kumar's Avatar
Nitin Kumar
@@ -241,19 +241,24 @@ The people who review and fix bugs and help triage issues.
-
+Bryan Mishkin's Avatar
Bryan Mishkin
-
+Francesco Trotta's Avatar
Francesco Trotta
-
+Yosuke Ota's Avatar
Yosuke Ota
+
+ +Tanuj Kanti's Avatar
+Tanuj Kanti +
### Website Team @@ -262,17 +267,17 @@ Team members who focus specifically on eslint.org
-
+Amaresh  S M's Avatar
Amaresh S M
-
+Strek's Avatar
Strek
-
+Percy Ma's Avatar
Percy Ma
@@ -288,8 +293,8 @@ The following companies, organizations, and individuals support ESLint's ongoing

Platinum Sponsors

Chrome Frameworks Fund Automattic

Gold Sponsors

Salesforce Airbnb

Silver Sponsors

-

Sentry Liftoff Siemens American Express

Bronze Sponsors

-

ThemeIsle Nx (by Nrwl) Anagram Solver Icons8 Discord Transloadit Ignition HeroCoders QuickBooks Tool hub

+

Liftoff American Express Workleap

Bronze Sponsors

+

ThemeIsle Anagram Solver Icons8 Discord Transloadit Ignition Nx HeroCoders

## Technology Sponsors diff --git a/bin/eslint.js b/bin/eslint.js index 7094ac77bc4..eeb4647e70b 100755 --- a/bin/eslint.js +++ b/bin/eslint.js @@ -92,6 +92,19 @@ function getErrorMessage(error) { return util.format("%o", error); } +/** + * Tracks error messages that are shown to the user so we only ever show the + * same message once. + * @type {Set} + */ +const displayedErrors = new Set(); + +/** + * Tracks whether an unexpected error was caught + * @type {boolean} + */ +let hadFatalError = false; + /** * Catch and report unexpected error. * @param {any} error The thrown error object. @@ -99,16 +112,20 @@ function getErrorMessage(error) { */ function onFatalError(error) { process.exitCode = 2; + hadFatalError = true; const { version } = require("../package.json"); - const message = getErrorMessage(error); - - console.error(` + const message = ` Oops! Something went wrong! :( ESLint: ${version} -${message}`); +${getErrorMessage(error)}`; + + if (!displayedErrors.has(message)) { + console.error(message); + displayedErrors.add(message); + } } //------------------------------------------------------------------------------ @@ -132,9 +149,25 @@ ${message}`); } // Otherwise, call the CLI. - process.exitCode = await require("../lib/cli").execute( + const exitCode = await require("../lib/cli").execute( process.argv, process.argv.includes("--stdin") ? await readStdin() : null, true ); + + /* + * If an uncaught exception or unhandled rejection was detected in the meantime, + * keep the fatal exit code 2 that is already assigned to `process.exitCode`. + * Without this condition, exit code 2 (unsuccessful execution) could be overwritten with + * 1 (successful execution, lint problems found) or even 0 (successful execution, no lint problems found). + * This ensures that unexpected errors that seemingly don't affect the success + * of the execution will still cause a non-zero exit code, as it's a common + * practice and the default behavior of Node.js to exit with non-zero + * in case of an uncaught exception or unhandled rejection. + * + * Otherwise, assign the exit code returned from CLI. + */ + if (!hadFatalError) { + process.exitCode = exitCode; + } }()).catch(onFatalError); diff --git a/conf/rule-type-list.json b/conf/rule-type-list.json index d5823acc898..6ca730f34f0 100644 --- a/conf/rule-type-list.json +++ b/conf/rule-type-list.json @@ -1,36 +1,28 @@ { - "types": [ - { "name": "problem", "displayName": "Possible Problems", "description": "These rules relate to possible logic errors in code:" }, - { "name": "suggestion", "displayName": "Suggestions", "description": "These rules suggest alternate ways of doing things:" }, - { "name": "layout", "displayName": "Layout & Formatting", "description": "These rules care about how the code looks rather than how it executes:" } - ], - "deprecated": { - "name": "Deprecated", - "description": "These rules have been deprecated in accordance with the deprecation policy, and replaced by newer rules:", - "rules": [] + "types": { + "problem": [], + "suggestion": [], + "layout": [] }, - "removed": { - "name": "Removed", - "description": "These rules from older versions of ESLint (before the deprecation policy existed) have been replaced by newer rules:", - "rules": [ - { "removed": "generator-star", "replacedBy": ["generator-star-spacing"] }, - { "removed": "global-strict", "replacedBy": ["strict"] }, - { "removed": "no-arrow-condition", "replacedBy": ["no-confusing-arrow", "no-constant-condition"] }, - { "removed": "no-comma-dangle", "replacedBy": ["comma-dangle"] }, - { "removed": "no-empty-class", "replacedBy": ["no-empty-character-class"] }, - { "removed": "no-empty-label", "replacedBy": ["no-labels"] }, - { "removed": "no-extra-strict", "replacedBy": ["strict"] }, - { "removed": "no-reserved-keys", "replacedBy": ["quote-props"] }, - { "removed": "no-space-before-semi", "replacedBy": ["semi-spacing"] }, - { "removed": "no-wrap-func", "replacedBy": ["no-extra-parens"] }, - { "removed": "space-after-function-name", "replacedBy": ["space-before-function-paren"] }, - { "removed": "space-after-keywords", "replacedBy": ["keyword-spacing"] }, - { "removed": "space-before-function-parentheses", "replacedBy": ["space-before-function-paren"] }, - { "removed": "space-before-keywords", "replacedBy": ["keyword-spacing"] }, - { "removed": "space-in-brackets", "replacedBy": ["object-curly-spacing", "array-bracket-spacing"] }, - { "removed": "space-return-throw-case", "replacedBy": ["keyword-spacing"] }, - { "removed": "space-unary-word-ops", "replacedBy": ["space-unary-ops"] }, - { "removed": "spaced-line-comment", "replacedBy": ["spaced-comment"] } - ] - } + "deprecated": [], + "removed": [ + { "removed": "generator-star", "replacedBy": ["generator-star-spacing"] }, + { "removed": "global-strict", "replacedBy": ["strict"] }, + { "removed": "no-arrow-condition", "replacedBy": ["no-confusing-arrow", "no-constant-condition"] }, + { "removed": "no-comma-dangle", "replacedBy": ["comma-dangle"] }, + { "removed": "no-empty-class", "replacedBy": ["no-empty-character-class"] }, + { "removed": "no-empty-label", "replacedBy": ["no-labels"] }, + { "removed": "no-extra-strict", "replacedBy": ["strict"] }, + { "removed": "no-reserved-keys", "replacedBy": ["quote-props"] }, + { "removed": "no-space-before-semi", "replacedBy": ["semi-spacing"] }, + { "removed": "no-wrap-func", "replacedBy": ["no-extra-parens"] }, + { "removed": "space-after-function-name", "replacedBy": ["space-before-function-paren"] }, + { "removed": "space-after-keywords", "replacedBy": ["keyword-spacing"] }, + { "removed": "space-before-function-parentheses", "replacedBy": ["space-before-function-paren"] }, + { "removed": "space-before-keywords", "replacedBy": ["keyword-spacing"] }, + { "removed": "space-in-brackets", "replacedBy": ["object-curly-spacing", "array-bracket-spacing"] }, + { "removed": "space-return-throw-case", "replacedBy": ["keyword-spacing"] }, + { "removed": "space-unary-word-ops", "replacedBy": ["space-unary-ops"] }, + { "removed": "spaced-line-comment", "replacedBy": ["spaced-comment"] } + ] } diff --git a/docs/.eleventy.js b/docs/.eleventy.js index 94a20211254..01b9c96aaba 100644 --- a/docs/.eleventy.js +++ b/docs/.eleventy.js @@ -14,6 +14,8 @@ const { highlighter, lineNumberPlugin } = require("./src/_plugins/md-syntax-high const { DateTime } = require("luxon"); +const markdownIt = require("markdown-it"); +const markdownItRuleExample = require("./tools/markdown-it-rule-example"); module.exports = function(eleventyConfig) { @@ -54,6 +56,7 @@ module.exports = function(eleventyConfig) { eleventyConfig.addGlobalData("GIT_BRANCH", process.env.BRANCH); eleventyConfig.addGlobalData("HEAD", process.env.BRANCH === "main"); eleventyConfig.addGlobalData("NOINDEX", process.env.BRANCH !== "latest"); + eleventyConfig.addGlobalData("PATH_PREFIX", pathPrefix); eleventyConfig.addDataExtension("yml", contents => yaml.load(contents)); //------------------------------------------------------------------------------ @@ -112,7 +115,7 @@ module.exports = function(eleventyConfig) { * Source: https://github.com/11ty/eleventy/issues/658 */ eleventyConfig.addFilter("markdown", value => { - const markdown = require("markdown-it")({ + const markdown = markdownIt({ html: true }); @@ -190,52 +193,44 @@ module.exports = function(eleventyConfig) { return btoa(unescape(encodeURIComponent(text))); } - /** - * Creates markdownItContainer settings for a playground-linked codeblock. - * @param {string} name Plugin name and class name to add to the code block. - * @returns {[string, object]} Plugin name and options for markdown-it. - */ - function withPlaygroundRender(name) { - return [ - name, - { - render(tokens, index) { - if (tokens[index].nesting !== 1) { - return ""; - } - - // See https://github.com/eslint/eslint.org/blob/ac38ab41f99b89a8798d374f74e2cce01171be8b/src/playground/App.js#L44 - const parserOptions = tokens[index].info?.split("correct ")[1]?.trim(); - const { content } = tokens[index + 1]; - const state = encodeToBase64( - JSON.stringify({ - ...(parserOptions && { options: { parserOptions: JSON.parse(parserOptions) } }), - text: content - }) - ); - const prefix = process.env.CONTEXT && process.env.CONTEXT !== "deploy-preview" - ? "" - : "https://eslint.org"; - - return ` -
+ // markdown-it plugin options for playground-linked code blocks in rule examples. + const ruleExampleOptions = markdownItRuleExample({ + open({ type, code, parserOptions, env }) { + const isRuleRemoved = !Object.prototype.hasOwnProperty.call(env.rules_meta, env.title); + + if (isRuleRemoved) { + return `
`; + } + + // See https://github.com/eslint/eslint.org/blob/ac38ab41f99b89a8798d374f74e2cce01171be8b/src/playground/App.js#L44 + const state = encodeToBase64( + JSON.stringify({ + options: { parserOptions }, + text: code + }) + ); + const prefix = process.env.CONTEXT && process.env.CONTEXT !== "deploy-preview" + ? "" + : "https://eslint.org"; + + return ` +
Open in Playground - `.trim(); - } - } - ]; - } + `.trim(); + }, + close() { + return "
"; + } + }); - const markdownIt = require("markdown-it"); const md = markdownIt({ html: true, linkify: true, typographer: true, highlight: (str, lang) => highlighter(md, str, lang) }) .use(markdownItAnchor, { slugify: s => slug(s) }) .use(markdownItContainer, "img-container", {}) - .use(markdownItContainer, ...withPlaygroundRender("correct")) - .use(markdownItContainer, ...withPlaygroundRender("incorrect")) + .use(markdownItContainer, "rule-example", ruleExampleOptions) .use(markdownItContainer, "warning", { render(tokens, idx) { return generateAlertMarkup("warning", tokens, idx); @@ -486,25 +481,6 @@ module.exports = function(eleventyConfig) { // Settings //------------------------------------------------------------------------------ - /* - * When we run `eleventy --serve`, Eleventy 1.x uses browser-sync to serve the content. - * By default, browser-sync (more precisely, underlying serve-static) will not serve - * `foo/bar.html` when we request `foo/bar`. Thus, we need to rewrite URLs to append `.html` - * so that pretty links without `.html` can work in a local development environment. - * - * There's no need to rewrite URLs that end with `/`, because that already works well - * (server will return the content of `index.html` in the directory). - * URLs with a file extension, like main.css, main.js, sitemap.xml, etc. should not be rewritten - */ - eleventyConfig.setBrowserSyncConfig({ - middleware(req, res, next) { - if (!/(?:\.[a-zA-Z][^/]*|\/)$/u.test(req.url)) { - req.url += ".html"; - } - return next(); - } - }); - /* * Generate the sitemap only in certain contexts to prevent unwanted discovery of sitemaps that * contain URLs we'd prefer not to appear in search results (URLs in sitemaps are considered important). @@ -524,14 +500,12 @@ module.exports = function(eleventyConfig) { eleventyConfig.ignores.add("src/static/sitemap.njk"); // ... then don't generate the sitemap. } - return { passthroughFileCopy: true, pathPrefix, markdownTemplateEngine: "njk", - dataTemplateEngine: "njk", htmlTemplateEngine: "njk", dir: { diff --git a/docs/package.json b/docs/package.json index 120aa91c60c..c79c756bf18 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,7 +1,7 @@ { "name": "docs-eslint", "private": true, - "version": "8.50.0", + "version": "8.55.0", "description": "", "main": "index.js", "keywords": [], @@ -23,11 +23,11 @@ "start": "npm-run-all build:sass build:postcss --parallel *:*:watch" }, "devDependencies": { - "@11ty/eleventy": "^1.0.1", - "@11ty/eleventy-img": "^1.0.0", - "@11ty/eleventy-navigation": "^0.3.2", + "@11ty/eleventy": "^2.0.1", + "@11ty/eleventy-img": "^3.1.1", + "@11ty/eleventy-navigation": "^0.3.5", "@11ty/eleventy-plugin-rss": "^1.1.1", - "@11ty/eleventy-plugin-syntaxhighlight": "^3.1.2", + "@11ty/eleventy-plugin-syntaxhighlight": "^5.0.0", "@munter/tap-render": "^0.2.0", "@types/markdown-it": "^12.2.3", "algoliasearch": "^4.12.1", diff --git a/docs/src/_data/further_reading_links.json b/docs/src/_data/further_reading_links.json index 045bd4faf62..a33ce6c0b3b 100644 --- a/docs/src/_data/further_reading_links.json +++ b/docs/src/_data/further_reading_links.json @@ -740,5 +740,12 @@ "logo": "https://v8.dev/favicon.ico", "title": "RegExp v flag with set notation and properties of strings · V8", "description": "The new RegExp `v` flag enables `unicodeSets` mode, unlocking support for extended character classes, including Unicode properties of strings, set notation, and improved case-insensitive matching." + }, + "https://codepoints.net/U+1680": { + "domain": "codepoints.net", + "url": "https://codepoints.net/U+1680", + "logo": "https://codepoints.net/favicon.ico", + "title": "U+1680 OGHAM SPACE MARK:   – Unicode – Codepoints", + "description": " , codepoint U+1680 OGHAM SPACE MARK in Unicode, is located in the block “Ogham”. It belongs to the Ogham script and is a Space Separator." } } \ No newline at end of file diff --git a/docs/src/_data/rules.json b/docs/src/_data/rules.json index 2ac3df8c535..3fe9ddcc3d5 100644 --- a/docs/src/_data/rules.json +++ b/docs/src/_data/rules.json @@ -1,2129 +1,2089 @@ { - "types": [ - { - "name": "problem", - "displayName": "Possible Problems", - "description": "These rules relate to possible logic errors in code:", - "rules": [ - { - "name": "array-callback-return", - "description": "Enforce `return` statements in callbacks of array methods", - "recommended": false, - "fixable": false, - "hasSuggestions": true - }, - { - "name": "constructor-super", - "description": "Require `super()` calls in constructors", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "for-direction", - "description": "Enforce \"for\" loop update clause moving the counter in the right direction", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "getter-return", - "description": "Enforce `return` statements in getters", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-async-promise-executor", - "description": "Disallow using an async function as a Promise executor", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-await-in-loop", - "description": "Disallow `await` inside of loops", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-class-assign", - "description": "Disallow reassigning class members", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-compare-neg-zero", - "description": "Disallow comparing against -0", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-cond-assign", - "description": "Disallow assignment operators in conditional expressions", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-const-assign", - "description": "Disallow reassigning `const` variables", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-constant-binary-expression", - "description": "Disallow expressions where the operation doesn't affect the value", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-constant-condition", - "description": "Disallow constant expressions in conditions", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-constructor-return", - "description": "Disallow returning value from constructor", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-control-regex", - "description": "Disallow control characters in regular expressions", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-debugger", - "description": "Disallow the use of `debugger`", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-dupe-args", - "description": "Disallow duplicate arguments in `function` definitions", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-dupe-class-members", - "description": "Disallow duplicate class members", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-dupe-else-if", - "description": "Disallow duplicate conditions in if-else-if chains", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-dupe-keys", - "description": "Disallow duplicate keys in object literals", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-duplicate-case", - "description": "Disallow duplicate case labels", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-duplicate-imports", - "description": "Disallow duplicate module imports", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-empty-character-class", - "description": "Disallow empty character classes in regular expressions", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-empty-pattern", - "description": "Disallow empty destructuring patterns", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-ex-assign", - "description": "Disallow reassigning exceptions in `catch` clauses", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-fallthrough", - "description": "Disallow fallthrough of `case` statements", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-func-assign", - "description": "Disallow reassigning `function` declarations", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-import-assign", - "description": "Disallow assigning to imported bindings", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-inner-declarations", - "description": "Disallow variable or `function` declarations in nested blocks", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-invalid-regexp", - "description": "Disallow invalid regular expression strings in `RegExp` constructors", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-irregular-whitespace", - "description": "Disallow irregular whitespace", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-loss-of-precision", - "description": "Disallow literal numbers that lose precision", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-misleading-character-class", - "description": "Disallow characters which are made with multiple code points in character class syntax", - "recommended": true, - "fixable": false, - "hasSuggestions": true - }, - { - "name": "no-new-native-nonconstructor", - "description": "Disallow `new` operators with global non-constructor functions", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-new-symbol", - "description": "Disallow `new` operators with the `Symbol` object", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-obj-calls", - "description": "Disallow calling global object properties as functions", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-promise-executor-return", - "description": "Disallow returning values from Promise executor functions", - "recommended": false, - "fixable": false, - "hasSuggestions": true - }, - { - "name": "no-prototype-builtins", - "description": "Disallow calling some `Object.prototype` methods directly on objects", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-self-assign", - "description": "Disallow assignments where both sides are exactly the same", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-self-compare", - "description": "Disallow comparisons where both sides are exactly the same", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-setter-return", - "description": "Disallow returning values from setters", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-sparse-arrays", - "description": "Disallow sparse arrays", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-template-curly-in-string", - "description": "Disallow template literal placeholder syntax in regular strings", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-this-before-super", - "description": "Disallow `this`/`super` before calling `super()` in constructors", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-undef", - "description": "Disallow the use of undeclared variables unless mentioned in `/*global */` comments", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-unexpected-multiline", - "description": "Disallow confusing multiline expressions", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-unmodified-loop-condition", - "description": "Disallow unmodified loop conditions", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-unreachable", - "description": "Disallow unreachable code after `return`, `throw`, `continue`, and `break` statements", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-unreachable-loop", - "description": "Disallow loops with a body that allows only one iteration", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-unsafe-finally", - "description": "Disallow control flow statements in `finally` blocks", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-unsafe-negation", - "description": "Disallow negating the left operand of relational operators", - "recommended": true, - "fixable": false, - "hasSuggestions": true - }, - { - "name": "no-unsafe-optional-chaining", - "description": "Disallow use of optional chaining in contexts where the `undefined` value is not allowed", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-unused-private-class-members", - "description": "Disallow unused private class members", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-unused-vars", - "description": "Disallow unused variables", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-use-before-define", - "description": "Disallow the use of variables before they are defined", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-useless-backreference", - "description": "Disallow useless backreferences in regular expressions", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "require-atomic-updates", - "description": "Disallow assignments that can lead to race conditions due to usage of `await` or `yield`", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "use-isnan", - "description": "Require calls to `isNaN()` when checking for `NaN`", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "valid-typeof", - "description": "Enforce comparing `typeof` expressions against valid strings", - "recommended": true, - "fixable": false, - "hasSuggestions": true - } - ] - }, - { - "name": "suggestion", - "displayName": "Suggestions", - "description": "These rules suggest alternate ways of doing things:", - "rules": [ - { - "name": "accessor-pairs", - "description": "Enforce getter and setter pairs in objects and classes", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "arrow-body-style", - "description": "Require braces around arrow function bodies", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "block-scoped-var", - "description": "Enforce the use of variables within the scope they are defined", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "camelcase", - "description": "Enforce camelcase naming convention", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "capitalized-comments", - "description": "Enforce or disallow capitalization of the first letter of a comment", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "class-methods-use-this", - "description": "Enforce that class methods utilize `this`", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "complexity", - "description": "Enforce a maximum cyclomatic complexity allowed in a program", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "consistent-return", - "description": "Require `return` statements to either always or never specify values", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "consistent-this", - "description": "Enforce consistent naming when capturing the current execution context", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "curly", - "description": "Enforce consistent brace style for all control statements", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "default-case", - "description": "Require `default` cases in `switch` statements", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "default-case-last", - "description": "Enforce default clauses in switch statements to be last", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "default-param-last", - "description": "Enforce default parameters to be last", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "dot-notation", - "description": "Enforce dot notation whenever possible", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "eqeqeq", - "description": "Require the use of `===` and `!==`", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "func-name-matching", - "description": "Require function names to match the name of the variable or property to which they are assigned", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "func-names", - "description": "Require or disallow named `function` expressions", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "func-style", - "description": "Enforce the consistent use of either `function` declarations or expressions", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "grouped-accessor-pairs", - "description": "Require grouped accessor pairs in object literals and classes", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "guard-for-in", - "description": "Require `for-in` loops to include an `if` statement", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "id-denylist", - "description": "Disallow specified identifiers", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "id-length", - "description": "Enforce minimum and maximum identifier lengths", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "id-match", - "description": "Require identifiers to match a specified regular expression", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "init-declarations", - "description": "Require or disallow initialization in variable declarations", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "logical-assignment-operators", - "description": "Require or disallow logical assignment operator shorthand", - "recommended": false, - "fixable": true, - "hasSuggestions": true - }, - { - "name": "max-classes-per-file", - "description": "Enforce a maximum number of classes per file", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "max-depth", - "description": "Enforce a maximum depth that blocks can be nested", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "max-lines", - "description": "Enforce a maximum number of lines per file", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "max-lines-per-function", - "description": "Enforce a maximum number of lines of code in a function", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "max-nested-callbacks", - "description": "Enforce a maximum depth that callbacks can be nested", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "max-params", - "description": "Enforce a maximum number of parameters in function definitions", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "max-statements", - "description": "Enforce a maximum number of statements allowed in function blocks", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "multiline-comment-style", - "description": "Enforce a particular style for multiline comments", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "new-cap", - "description": "Require constructor names to begin with a capital letter", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-alert", - "description": "Disallow the use of `alert`, `confirm`, and `prompt`", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-array-constructor", - "description": "Disallow `Array` constructors", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-bitwise", - "description": "Disallow bitwise operators", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-caller", - "description": "Disallow the use of `arguments.caller` or `arguments.callee`", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-case-declarations", - "description": "Disallow lexical declarations in case clauses", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-confusing-arrow", - "description": "Disallow arrow functions where they could be confused with comparisons", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-console", - "description": "Disallow the use of `console`", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-continue", - "description": "Disallow `continue` statements", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-delete-var", - "description": "Disallow deleting variables", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-div-regex", - "description": "Disallow equal signs explicitly at the beginning of regular expressions", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-else-return", - "description": "Disallow `else` blocks after `return` statements in `if` statements", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-empty", - "description": "Disallow empty block statements", - "recommended": true, - "fixable": false, - "hasSuggestions": true - }, - { - "name": "no-empty-function", - "description": "Disallow empty functions", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-empty-static-block", - "description": "Disallow empty static blocks", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-eq-null", - "description": "Disallow `null` comparisons without type-checking operators", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-eval", - "description": "Disallow the use of `eval()`", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-extend-native", - "description": "Disallow extending native types", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-extra-bind", - "description": "Disallow unnecessary calls to `.bind()`", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-extra-boolean-cast", - "description": "Disallow unnecessary boolean casts", - "recommended": true, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-extra-label", - "description": "Disallow unnecessary labels", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-extra-semi", - "description": "Disallow unnecessary semicolons", - "recommended": true, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-floating-decimal", - "description": "Disallow leading or trailing decimal points in numeric literals", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-global-assign", - "description": "Disallow assignments to native objects or read-only global variables", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-implicit-coercion", - "description": "Disallow shorthand type conversions", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-implicit-globals", - "description": "Disallow declarations in the global scope", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-implied-eval", - "description": "Disallow the use of `eval()`-like methods", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-inline-comments", - "description": "Disallow inline comments after code", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-invalid-this", - "description": "Disallow use of `this` in contexts where the value of `this` is `undefined`", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-iterator", - "description": "Disallow the use of the `__iterator__` property", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-label-var", - "description": "Disallow labels that share a name with a variable", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-labels", - "description": "Disallow labeled statements", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-lone-blocks", - "description": "Disallow unnecessary nested blocks", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-lonely-if", - "description": "Disallow `if` statements as the only statement in `else` blocks", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-loop-func", - "description": "Disallow function declarations that contain unsafe references inside loop statements", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-magic-numbers", - "description": "Disallow magic numbers", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-mixed-operators", - "description": "Disallow mixed binary operators", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-multi-assign", - "description": "Disallow use of chained assignment expressions", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-multi-str", - "description": "Disallow multiline strings", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-negated-condition", - "description": "Disallow negated conditions", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-nested-ternary", - "description": "Disallow nested ternary expressions", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-new", - "description": "Disallow `new` operators outside of assignments or comparisons", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-new-func", - "description": "Disallow `new` operators with the `Function` object", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-new-wrappers", - "description": "Disallow `new` operators with the `String`, `Number`, and `Boolean` objects", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-nonoctal-decimal-escape", - "description": "Disallow `\\8` and `\\9` escape sequences in string literals", - "recommended": true, - "fixable": false, - "hasSuggestions": true - }, - { - "name": "no-object-constructor", - "description": "Disallow calls to the `Object` constructor without an argument", - "recommended": false, - "fixable": false, - "hasSuggestions": true - }, - { - "name": "no-octal", - "description": "Disallow octal literals", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-octal-escape", - "description": "Disallow octal escape sequences in string literals", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-param-reassign", - "description": "Disallow reassigning `function` parameters", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-plusplus", - "description": "Disallow the unary operators `++` and `--`", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-proto", - "description": "Disallow the use of the `__proto__` property", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-redeclare", - "description": "Disallow variable redeclaration", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-regex-spaces", - "description": "Disallow multiple spaces in regular expressions", - "recommended": true, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-restricted-exports", - "description": "Disallow specified names in exports", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-restricted-globals", - "description": "Disallow specified global variables", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-restricted-imports", - "description": "Disallow specified modules when loaded by `import`", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-restricted-properties", - "description": "Disallow certain properties on certain objects", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-restricted-syntax", - "description": "Disallow specified syntax", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-return-assign", - "description": "Disallow assignment operators in `return` statements", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-script-url", - "description": "Disallow `javascript:` urls", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-sequences", - "description": "Disallow comma operators", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-shadow", - "description": "Disallow variable declarations from shadowing variables declared in the outer scope", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-shadow-restricted-names", - "description": "Disallow identifiers from shadowing restricted names", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-ternary", - "description": "Disallow ternary operators", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-throw-literal", - "description": "Disallow throwing literals as exceptions", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-undef-init", - "description": "Disallow initializing variables to `undefined`", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-undefined", - "description": "Disallow the use of `undefined` as an identifier", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-underscore-dangle", - "description": "Disallow dangling underscores in identifiers", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-unneeded-ternary", - "description": "Disallow ternary operators when simpler alternatives exist", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-unused-expressions", - "description": "Disallow unused expressions", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-unused-labels", - "description": "Disallow unused labels", - "recommended": true, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-useless-call", - "description": "Disallow unnecessary calls to `.call()` and `.apply()`", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-useless-catch", - "description": "Disallow unnecessary `catch` clauses", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-useless-computed-key", - "description": "Disallow unnecessary computed property keys in objects and classes", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-useless-concat", - "description": "Disallow unnecessary concatenation of literals or template literals", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-useless-constructor", - "description": "Disallow unnecessary constructors", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-useless-escape", - "description": "Disallow unnecessary escape characters", - "recommended": true, - "fixable": false, - "hasSuggestions": true - }, - { - "name": "no-useless-rename", - "description": "Disallow renaming import, export, and destructured assignments to the same name", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-useless-return", - "description": "Disallow redundant return statements", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-var", - "description": "Require `let` or `const` instead of `var`", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-void", - "description": "Disallow `void` operators", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-warning-comments", - "description": "Disallow specified warning terms in comments", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-with", - "description": "Disallow `with` statements", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "object-shorthand", - "description": "Require or disallow method and property shorthand syntax for object literals", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "one-var", - "description": "Enforce variables to be declared either together or separately in functions", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "one-var-declaration-per-line", - "description": "Require or disallow newlines around variable declarations", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "operator-assignment", - "description": "Require or disallow assignment operator shorthand where possible", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "prefer-arrow-callback", - "description": "Require using arrow functions for callbacks", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "prefer-const", - "description": "Require `const` declarations for variables that are never reassigned after declared", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "prefer-destructuring", - "description": "Require destructuring from arrays and/or objects", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "prefer-exponentiation-operator", - "description": "Disallow the use of `Math.pow` in favor of the `**` operator", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "prefer-named-capture-group", - "description": "Enforce using named capture group in regular expression", - "recommended": false, - "fixable": false, - "hasSuggestions": true - }, - { - "name": "prefer-numeric-literals", - "description": "Disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "prefer-object-has-own", - "description": "Disallow use of `Object.prototype.hasOwnProperty.call()` and prefer use of `Object.hasOwn()`", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "prefer-object-spread", - "description": "Disallow using Object.assign with an object literal as the first argument and prefer the use of object spread instead", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "prefer-promise-reject-errors", - "description": "Require using Error objects as Promise rejection reasons", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "prefer-regex-literals", - "description": "Disallow use of the `RegExp` constructor in favor of regular expression literals", - "recommended": false, - "fixable": false, - "hasSuggestions": true - }, - { - "name": "prefer-rest-params", - "description": "Require rest parameters instead of `arguments`", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "prefer-spread", - "description": "Require spread operators instead of `.apply()`", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "prefer-template", - "description": "Require template literals instead of string concatenation", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "quote-props", - "description": "Require quotes around object literal property names", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "radix", - "description": "Enforce the consistent use of the radix argument when using `parseInt()`", - "recommended": false, - "fixable": false, - "hasSuggestions": true - }, - { - "name": "require-await", - "description": "Disallow async functions which have no `await` expression", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "require-unicode-regexp", - "description": "Enforce the use of `u` or `v` flag on RegExp", - "recommended": false, - "fixable": false, - "hasSuggestions": true - }, - { - "name": "require-yield", - "description": "Require generator functions to contain `yield`", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "sort-imports", - "description": "Enforce sorted import declarations within modules", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "sort-keys", - "description": "Require object keys to be sorted", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "sort-vars", - "description": "Require variables within the same declaration block to be sorted", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "spaced-comment", - "description": "Enforce consistent spacing after the `//` or `/*` in a comment", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "strict", - "description": "Require or disallow strict mode directives", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "symbol-description", - "description": "Require symbol descriptions", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "vars-on-top", - "description": "Require `var` declarations be placed at the top of their containing scope", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "yoda", - "description": "Require or disallow \"Yoda\" conditions", - "recommended": false, - "fixable": true, - "hasSuggestions": false - } - ] - }, - { - "name": "layout", - "displayName": "Layout & Formatting", - "description": "These rules care about how the code looks rather than how it executes:", - "rules": [ - { - "name": "array-bracket-newline", - "description": "Enforce linebreaks after opening and before closing array brackets", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "array-bracket-spacing", - "description": "Enforce consistent spacing inside array brackets", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "array-element-newline", - "description": "Enforce line breaks after each array element", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "arrow-parens", - "description": "Require parentheses around arrow function arguments", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "arrow-spacing", - "description": "Enforce consistent spacing before and after the arrow in arrow functions", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "block-spacing", - "description": "Disallow or enforce spaces inside of blocks after opening block and before closing block", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "brace-style", - "description": "Enforce consistent brace style for blocks", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "comma-dangle", - "description": "Require or disallow trailing commas", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "comma-spacing", - "description": "Enforce consistent spacing before and after commas", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "comma-style", - "description": "Enforce consistent comma style", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "computed-property-spacing", - "description": "Enforce consistent spacing inside computed property brackets", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "dot-location", - "description": "Enforce consistent newlines before and after dots", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "eol-last", - "description": "Require or disallow newline at the end of files", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "func-call-spacing", - "description": "Require or disallow spacing between function identifiers and their invocations", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "function-call-argument-newline", - "description": "Enforce line breaks between arguments of a function call", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "function-paren-newline", - "description": "Enforce consistent line breaks inside function parentheses", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "generator-star-spacing", - "description": "Enforce consistent spacing around `*` operators in generator functions", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "implicit-arrow-linebreak", - "description": "Enforce the location of arrow function bodies", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "indent", - "description": "Enforce consistent indentation", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "jsx-quotes", - "description": "Enforce the consistent use of either double or single quotes in JSX attributes", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "key-spacing", - "description": "Enforce consistent spacing between keys and values in object literal properties", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "keyword-spacing", - "description": "Enforce consistent spacing before and after keywords", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "line-comment-position", - "description": "Enforce position of line comments", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "linebreak-style", - "description": "Enforce consistent linebreak style", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "lines-around-comment", - "description": "Require empty lines around comments", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "lines-between-class-members", - "description": "Require or disallow an empty line between class members", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "max-len", - "description": "Enforce a maximum line length", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "max-statements-per-line", - "description": "Enforce a maximum number of statements allowed per line", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "multiline-ternary", - "description": "Enforce newlines between operands of ternary expressions", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "new-parens", - "description": "Enforce or disallow parentheses when invoking a constructor with no arguments", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "newline-per-chained-call", - "description": "Require a newline after each call in a method chain", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-extra-parens", - "description": "Disallow unnecessary parentheses", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-mixed-spaces-and-tabs", - "description": "Disallow mixed spaces and tabs for indentation", - "recommended": true, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-multi-spaces", - "description": "Disallow multiple spaces", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-multiple-empty-lines", - "description": "Disallow multiple empty lines", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-tabs", - "description": "Disallow all tabs", - "recommended": false, - "fixable": false, - "hasSuggestions": false - }, - { - "name": "no-trailing-spaces", - "description": "Disallow trailing whitespace at the end of lines", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "no-whitespace-before-property", - "description": "Disallow whitespace before properties", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "nonblock-statement-body-position", - "description": "Enforce the location of single-line statements", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "object-curly-newline", - "description": "Enforce consistent line breaks after opening and before closing braces", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "object-curly-spacing", - "description": "Enforce consistent spacing inside braces", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "object-property-newline", - "description": "Enforce placing object properties on separate lines", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "operator-linebreak", - "description": "Enforce consistent linebreak style for operators", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "padded-blocks", - "description": "Require or disallow padding within blocks", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "padding-line-between-statements", - "description": "Require or disallow padding lines between statements", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "quotes", - "description": "Enforce the consistent use of either backticks, double, or single quotes", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "rest-spread-spacing", - "description": "Enforce spacing between rest and spread operators and their expressions", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "semi", - "description": "Require or disallow semicolons instead of ASI", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "semi-spacing", - "description": "Enforce consistent spacing before and after semicolons", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "semi-style", - "description": "Enforce location of semicolons", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "space-before-blocks", - "description": "Enforce consistent spacing before blocks", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "space-before-function-paren", - "description": "Enforce consistent spacing before `function` definition opening parenthesis", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "space-in-parens", - "description": "Enforce consistent spacing inside parentheses", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "space-infix-ops", - "description": "Require spacing around infix operators", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "space-unary-ops", - "description": "Enforce consistent spacing before or after unary operators", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "switch-colon-spacing", - "description": "Enforce spacing around colons of switch statements", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "template-curly-spacing", - "description": "Require or disallow spacing around embedded expressions of template strings", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "template-tag-spacing", - "description": "Require or disallow spacing between template tags and their literals", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "unicode-bom", - "description": "Require or disallow Unicode byte order mark (BOM)", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "wrap-iife", - "description": "Require parentheses around immediate `function` invocations", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "wrap-regex", - "description": "Require parenthesis around regex literals", - "recommended": false, - "fixable": true, - "hasSuggestions": false - }, - { - "name": "yield-star-spacing", - "description": "Require or disallow spacing around the `*` in `yield*` expressions", - "recommended": false, - "fixable": true, - "hasSuggestions": false - } - ] - } - ], - "deprecated": { - "name": "Deprecated", - "description": "These rules have been deprecated in accordance with the deprecation policy, and replaced by newer rules:", - "rules": [ + "types": { + "problem": [ + { + "name": "array-callback-return", + "description": "Enforce `return` statements in callbacks of array methods", + "recommended": false, + "fixable": false, + "hasSuggestions": true + }, + { + "name": "constructor-super", + "description": "Require `super()` calls in constructors", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "for-direction", + "description": "Enforce \"for\" loop update clause moving the counter in the right direction", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "getter-return", + "description": "Enforce `return` statements in getters", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-async-promise-executor", + "description": "Disallow using an async function as a Promise executor", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-await-in-loop", + "description": "Disallow `await` inside of loops", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-class-assign", + "description": "Disallow reassigning class members", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-compare-neg-zero", + "description": "Disallow comparing against -0", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-cond-assign", + "description": "Disallow assignment operators in conditional expressions", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-const-assign", + "description": "Disallow reassigning `const` variables", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-constant-binary-expression", + "description": "Disallow expressions where the operation doesn't affect the value", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-constant-condition", + "description": "Disallow constant expressions in conditions", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-constructor-return", + "description": "Disallow returning value from constructor", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-control-regex", + "description": "Disallow control characters in regular expressions", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-debugger", + "description": "Disallow the use of `debugger`", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-dupe-args", + "description": "Disallow duplicate arguments in `function` definitions", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-dupe-class-members", + "description": "Disallow duplicate class members", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-dupe-else-if", + "description": "Disallow duplicate conditions in if-else-if chains", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-dupe-keys", + "description": "Disallow duplicate keys in object literals", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-duplicate-case", + "description": "Disallow duplicate case labels", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-duplicate-imports", + "description": "Disallow duplicate module imports", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-empty-character-class", + "description": "Disallow empty character classes in regular expressions", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-empty-pattern", + "description": "Disallow empty destructuring patterns", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-ex-assign", + "description": "Disallow reassigning exceptions in `catch` clauses", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-fallthrough", + "description": "Disallow fallthrough of `case` statements", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-func-assign", + "description": "Disallow reassigning `function` declarations", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-import-assign", + "description": "Disallow assigning to imported bindings", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-inner-declarations", + "description": "Disallow variable or `function` declarations in nested blocks", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-invalid-regexp", + "description": "Disallow invalid regular expression strings in `RegExp` constructors", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-irregular-whitespace", + "description": "Disallow irregular whitespace", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-loss-of-precision", + "description": "Disallow literal numbers that lose precision", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-misleading-character-class", + "description": "Disallow characters which are made with multiple code points in character class syntax", + "recommended": true, + "fixable": false, + "hasSuggestions": true + }, + { + "name": "no-new-native-nonconstructor", + "description": "Disallow `new` operators with global non-constructor functions", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-new-symbol", + "description": "Disallow `new` operators with the `Symbol` object", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-obj-calls", + "description": "Disallow calling global object properties as functions", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-promise-executor-return", + "description": "Disallow returning values from Promise executor functions", + "recommended": false, + "fixable": false, + "hasSuggestions": true + }, + { + "name": "no-prototype-builtins", + "description": "Disallow calling some `Object.prototype` methods directly on objects", + "recommended": true, + "fixable": false, + "hasSuggestions": true + }, + { + "name": "no-self-assign", + "description": "Disallow assignments where both sides are exactly the same", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-self-compare", + "description": "Disallow comparisons where both sides are exactly the same", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-setter-return", + "description": "Disallow returning values from setters", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-sparse-arrays", + "description": "Disallow sparse arrays", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-template-curly-in-string", + "description": "Disallow template literal placeholder syntax in regular strings", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-this-before-super", + "description": "Disallow `this`/`super` before calling `super()` in constructors", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-undef", + "description": "Disallow the use of undeclared variables unless mentioned in `/*global */` comments", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-unexpected-multiline", + "description": "Disallow confusing multiline expressions", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-unmodified-loop-condition", + "description": "Disallow unmodified loop conditions", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-unreachable", + "description": "Disallow unreachable code after `return`, `throw`, `continue`, and `break` statements", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-unreachable-loop", + "description": "Disallow loops with a body that allows only one iteration", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-unsafe-finally", + "description": "Disallow control flow statements in `finally` blocks", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-unsafe-negation", + "description": "Disallow negating the left operand of relational operators", + "recommended": true, + "fixable": false, + "hasSuggestions": true + }, + { + "name": "no-unsafe-optional-chaining", + "description": "Disallow use of optional chaining in contexts where the `undefined` value is not allowed", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-unused-private-class-members", + "description": "Disallow unused private class members", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-unused-vars", + "description": "Disallow unused variables", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-use-before-define", + "description": "Disallow the use of variables before they are defined", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-useless-backreference", + "description": "Disallow useless backreferences in regular expressions", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "require-atomic-updates", + "description": "Disallow assignments that can lead to race conditions due to usage of `await` or `yield`", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "use-isnan", + "description": "Require calls to `isNaN()` when checking for `NaN`", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "valid-typeof", + "description": "Enforce comparing `typeof` expressions against valid strings", + "recommended": true, + "fixable": false, + "hasSuggestions": true + } + ], + "suggestion": [ + { + "name": "accessor-pairs", + "description": "Enforce getter and setter pairs in objects and classes", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "arrow-body-style", + "description": "Require braces around arrow function bodies", + "recommended": false, + "fixable": true, + "hasSuggestions": false + }, + { + "name": "block-scoped-var", + "description": "Enforce the use of variables within the scope they are defined", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "camelcase", + "description": "Enforce camelcase naming convention", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "capitalized-comments", + "description": "Enforce or disallow capitalization of the first letter of a comment", + "recommended": false, + "fixable": true, + "hasSuggestions": false + }, + { + "name": "class-methods-use-this", + "description": "Enforce that class methods utilize `this`", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "complexity", + "description": "Enforce a maximum cyclomatic complexity allowed in a program", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "consistent-return", + "description": "Require `return` statements to either always or never specify values", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "consistent-this", + "description": "Enforce consistent naming when capturing the current execution context", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "curly", + "description": "Enforce consistent brace style for all control statements", + "recommended": false, + "fixable": true, + "hasSuggestions": false + }, + { + "name": "default-case", + "description": "Require `default` cases in `switch` statements", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "default-case-last", + "description": "Enforce default clauses in switch statements to be last", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "default-param-last", + "description": "Enforce default parameters to be last", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "dot-notation", + "description": "Enforce dot notation whenever possible", + "recommended": false, + "fixable": true, + "hasSuggestions": false + }, + { + "name": "eqeqeq", + "description": "Require the use of `===` and `!==`", + "recommended": false, + "fixable": true, + "hasSuggestions": false + }, + { + "name": "func-name-matching", + "description": "Require function names to match the name of the variable or property to which they are assigned", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "func-names", + "description": "Require or disallow named `function` expressions", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "func-style", + "description": "Enforce the consistent use of either `function` declarations or expressions", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "grouped-accessor-pairs", + "description": "Require grouped accessor pairs in object literals and classes", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "guard-for-in", + "description": "Require `for-in` loops to include an `if` statement", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "id-denylist", + "description": "Disallow specified identifiers", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "id-length", + "description": "Enforce minimum and maximum identifier lengths", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "id-match", + "description": "Require identifiers to match a specified regular expression", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "init-declarations", + "description": "Require or disallow initialization in variable declarations", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "logical-assignment-operators", + "description": "Require or disallow logical assignment operator shorthand", + "recommended": false, + "fixable": true, + "hasSuggestions": true + }, + { + "name": "max-classes-per-file", + "description": "Enforce a maximum number of classes per file", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "max-depth", + "description": "Enforce a maximum depth that blocks can be nested", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "max-lines", + "description": "Enforce a maximum number of lines per file", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "max-lines-per-function", + "description": "Enforce a maximum number of lines of code in a function", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "max-nested-callbacks", + "description": "Enforce a maximum depth that callbacks can be nested", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "max-params", + "description": "Enforce a maximum number of parameters in function definitions", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "max-statements", + "description": "Enforce a maximum number of statements allowed in function blocks", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "multiline-comment-style", + "description": "Enforce a particular style for multiline comments", + "recommended": false, + "fixable": true, + "hasSuggestions": false + }, + { + "name": "new-cap", + "description": "Require constructor names to begin with a capital letter", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-alert", + "description": "Disallow the use of `alert`, `confirm`, and `prompt`", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-array-constructor", + "description": "Disallow `Array` constructors", + "recommended": false, + "fixable": false, + "hasSuggestions": true + }, + { + "name": "no-bitwise", + "description": "Disallow bitwise operators", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-caller", + "description": "Disallow the use of `arguments.caller` or `arguments.callee`", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-case-declarations", + "description": "Disallow lexical declarations in case clauses", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-console", + "description": "Disallow the use of `console`", + "recommended": false, + "fixable": false, + "hasSuggestions": true + }, + { + "name": "no-continue", + "description": "Disallow `continue` statements", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-delete-var", + "description": "Disallow deleting variables", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-div-regex", + "description": "Disallow equal signs explicitly at the beginning of regular expressions", + "recommended": false, + "fixable": true, + "hasSuggestions": false + }, + { + "name": "no-else-return", + "description": "Disallow `else` blocks after `return` statements in `if` statements", + "recommended": false, + "fixable": true, + "hasSuggestions": false + }, + { + "name": "no-empty", + "description": "Disallow empty block statements", + "recommended": true, + "fixable": false, + "hasSuggestions": true + }, + { + "name": "no-empty-function", + "description": "Disallow empty functions", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-empty-static-block", + "description": "Disallow empty static blocks", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-eq-null", + "description": "Disallow `null` comparisons without type-checking operators", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-eval", + "description": "Disallow the use of `eval()`", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-extend-native", + "description": "Disallow extending native types", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-extra-bind", + "description": "Disallow unnecessary calls to `.bind()`", + "recommended": false, + "fixable": true, + "hasSuggestions": false + }, + { + "name": "no-extra-boolean-cast", + "description": "Disallow unnecessary boolean casts", + "recommended": true, + "fixable": true, + "hasSuggestions": false + }, + { + "name": "no-extra-label", + "description": "Disallow unnecessary labels", + "recommended": false, + "fixable": true, + "hasSuggestions": false + }, + { + "name": "no-global-assign", + "description": "Disallow assignments to native objects or read-only global variables", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-implicit-coercion", + "description": "Disallow shorthand type conversions", + "recommended": false, + "fixable": true, + "hasSuggestions": false + }, + { + "name": "no-implicit-globals", + "description": "Disallow declarations in the global scope", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-implied-eval", + "description": "Disallow the use of `eval()`-like methods", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-inline-comments", + "description": "Disallow inline comments after code", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-invalid-this", + "description": "Disallow use of `this` in contexts where the value of `this` is `undefined`", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-iterator", + "description": "Disallow the use of the `__iterator__` property", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-label-var", + "description": "Disallow labels that share a name with a variable", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-labels", + "description": "Disallow labeled statements", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-lone-blocks", + "description": "Disallow unnecessary nested blocks", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-lonely-if", + "description": "Disallow `if` statements as the only statement in `else` blocks", + "recommended": false, + "fixable": true, + "hasSuggestions": false + }, + { + "name": "no-loop-func", + "description": "Disallow function declarations that contain unsafe references inside loop statements", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-magic-numbers", + "description": "Disallow magic numbers", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-multi-assign", + "description": "Disallow use of chained assignment expressions", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-multi-str", + "description": "Disallow multiline strings", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-negated-condition", + "description": "Disallow negated conditions", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-nested-ternary", + "description": "Disallow nested ternary expressions", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-new", + "description": "Disallow `new` operators outside of assignments or comparisons", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-new-func", + "description": "Disallow `new` operators with the `Function` object", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-new-wrappers", + "description": "Disallow `new` operators with the `String`, `Number`, and `Boolean` objects", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-nonoctal-decimal-escape", + "description": "Disallow `\\8` and `\\9` escape sequences in string literals", + "recommended": true, + "fixable": false, + "hasSuggestions": true + }, + { + "name": "no-object-constructor", + "description": "Disallow calls to the `Object` constructor without an argument", + "recommended": false, + "fixable": false, + "hasSuggestions": true + }, + { + "name": "no-octal", + "description": "Disallow octal literals", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-octal-escape", + "description": "Disallow octal escape sequences in string literals", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-param-reassign", + "description": "Disallow reassigning `function` parameters", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-plusplus", + "description": "Disallow the unary operators `++` and `--`", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-proto", + "description": "Disallow the use of the `__proto__` property", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-redeclare", + "description": "Disallow variable redeclaration", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-regex-spaces", + "description": "Disallow multiple spaces in regular expressions", + "recommended": true, + "fixable": true, + "hasSuggestions": false + }, + { + "name": "no-restricted-exports", + "description": "Disallow specified names in exports", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-restricted-globals", + "description": "Disallow specified global variables", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-restricted-imports", + "description": "Disallow specified modules when loaded by `import`", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-restricted-properties", + "description": "Disallow certain properties on certain objects", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-restricted-syntax", + "description": "Disallow specified syntax", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-return-assign", + "description": "Disallow assignment operators in `return` statements", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-script-url", + "description": "Disallow `javascript:` urls", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-sequences", + "description": "Disallow comma operators", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-shadow", + "description": "Disallow variable declarations from shadowing variables declared in the outer scope", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-shadow-restricted-names", + "description": "Disallow identifiers from shadowing restricted names", + "recommended": true, + "fixable": false, + "hasSuggestions": false + }, { - "name": "callback-return", - "replacedBy": [] + "name": "no-ternary", + "description": "Disallow ternary operators", + "recommended": false, + "fixable": false, + "hasSuggestions": false }, { - "name": "global-require", - "replacedBy": [] + "name": "no-throw-literal", + "description": "Disallow throwing literals as exceptions", + "recommended": false, + "fixable": false, + "hasSuggestions": false }, { - "name": "handle-callback-err", - "replacedBy": [] + "name": "no-undef-init", + "description": "Disallow initializing variables to `undefined`", + "recommended": false, + "fixable": true, + "hasSuggestions": false }, { - "name": "id-blacklist", - "replacedBy": [ - "id-denylist" - ] + "name": "no-undefined", + "description": "Disallow the use of `undefined` as an identifier", + "recommended": false, + "fixable": false, + "hasSuggestions": false }, { - "name": "indent-legacy", - "replacedBy": [ - "indent" - ] + "name": "no-underscore-dangle", + "description": "Disallow dangling underscores in identifiers", + "recommended": false, + "fixable": false, + "hasSuggestions": false }, { - "name": "lines-around-directive", - "replacedBy": [ - "padding-line-between-statements" - ] + "name": "no-unneeded-ternary", + "description": "Disallow ternary operators when simpler alternatives exist", + "recommended": false, + "fixable": true, + "hasSuggestions": false }, { - "name": "newline-after-var", - "replacedBy": [ - "padding-line-between-statements" - ] + "name": "no-unused-expressions", + "description": "Disallow unused expressions", + "recommended": false, + "fixable": false, + "hasSuggestions": false }, { - "name": "newline-before-return", - "replacedBy": [ - "padding-line-between-statements" - ] + "name": "no-unused-labels", + "description": "Disallow unused labels", + "recommended": true, + "fixable": true, + "hasSuggestions": false }, { - "name": "no-buffer-constructor", - "replacedBy": [] + "name": "no-useless-call", + "description": "Disallow unnecessary calls to `.call()` and `.apply()`", + "recommended": false, + "fixable": false, + "hasSuggestions": false }, { - "name": "no-catch-shadow", - "replacedBy": [ - "no-shadow" - ] + "name": "no-useless-catch", + "description": "Disallow unnecessary `catch` clauses", + "recommended": true, + "fixable": false, + "hasSuggestions": false }, { - "name": "no-mixed-requires", - "replacedBy": [] + "name": "no-useless-computed-key", + "description": "Disallow unnecessary computed property keys in objects and classes", + "recommended": false, + "fixable": true, + "hasSuggestions": false }, { - "name": "no-native-reassign", - "replacedBy": [ - "no-global-assign" - ] + "name": "no-useless-concat", + "description": "Disallow unnecessary concatenation of literals or template literals", + "recommended": false, + "fixable": false, + "hasSuggestions": false }, { - "name": "no-negated-in-lhs", - "replacedBy": [ - "no-unsafe-negation" - ] + "name": "no-useless-constructor", + "description": "Disallow unnecessary constructors", + "recommended": false, + "fixable": false, + "hasSuggestions": false }, { - "name": "no-new-object", - "replacedBy": [ - "no-object-constructor" - ] + "name": "no-useless-escape", + "description": "Disallow unnecessary escape characters", + "recommended": true, + "fixable": false, + "hasSuggestions": true }, { - "name": "no-new-require", - "replacedBy": [] + "name": "no-useless-rename", + "description": "Disallow renaming import, export, and destructured assignments to the same name", + "recommended": false, + "fixable": true, + "hasSuggestions": false }, { - "name": "no-path-concat", - "replacedBy": [] + "name": "no-useless-return", + "description": "Disallow redundant return statements", + "recommended": false, + "fixable": true, + "hasSuggestions": false }, { - "name": "no-process-env", - "replacedBy": [] + "name": "no-var", + "description": "Require `let` or `const` instead of `var`", + "recommended": false, + "fixable": true, + "hasSuggestions": false }, { - "name": "no-process-exit", - "replacedBy": [] + "name": "no-void", + "description": "Disallow `void` operators", + "recommended": false, + "fixable": false, + "hasSuggestions": false }, { - "name": "no-restricted-modules", - "replacedBy": [] + "name": "no-warning-comments", + "description": "Disallow specified warning terms in comments", + "recommended": false, + "fixable": false, + "hasSuggestions": false }, { - "name": "no-return-await", - "replacedBy": [] + "name": "no-with", + "description": "Disallow `with` statements", + "recommended": true, + "fixable": false, + "hasSuggestions": false }, { - "name": "no-spaced-func", - "replacedBy": [ - "func-call-spacing" - ] + "name": "object-shorthand", + "description": "Require or disallow method and property shorthand syntax for object literals", + "recommended": false, + "fixable": true, + "hasSuggestions": false }, { - "name": "no-sync", - "replacedBy": [] + "name": "one-var", + "description": "Enforce variables to be declared either together or separately in functions", + "recommended": false, + "fixable": true, + "hasSuggestions": false }, { - "name": "prefer-reflect", - "replacedBy": [] + "name": "operator-assignment", + "description": "Require or disallow assignment operator shorthand where possible", + "recommended": false, + "fixable": true, + "hasSuggestions": false }, { - "name": "require-jsdoc", - "replacedBy": [] + "name": "prefer-arrow-callback", + "description": "Require using arrow functions for callbacks", + "recommended": false, + "fixable": true, + "hasSuggestions": false }, { - "name": "valid-jsdoc", - "replacedBy": [] - } - ] - }, - "removed": { - "name": "Removed", - "description": "These rules from older versions of ESLint (before the deprecation policy existed) have been replaced by newer rules:", - "rules": [ + "name": "prefer-const", + "description": "Require `const` declarations for variables that are never reassigned after declared", + "recommended": false, + "fixable": true, + "hasSuggestions": false + }, + { + "name": "prefer-destructuring", + "description": "Require destructuring from arrays and/or objects", + "recommended": false, + "fixable": true, + "hasSuggestions": false + }, + { + "name": "prefer-exponentiation-operator", + "description": "Disallow the use of `Math.pow` in favor of the `**` operator", + "recommended": false, + "fixable": true, + "hasSuggestions": false + }, { - "removed": "generator-star", - "replacedBy": [ - "generator-star-spacing" - ] + "name": "prefer-named-capture-group", + "description": "Enforce using named capture group in regular expression", + "recommended": false, + "fixable": false, + "hasSuggestions": true }, { - "removed": "global-strict", - "replacedBy": [ - "strict" - ] + "name": "prefer-numeric-literals", + "description": "Disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals", + "recommended": false, + "fixable": true, + "hasSuggestions": false }, { - "removed": "no-arrow-condition", - "replacedBy": [ - "no-confusing-arrow", - "no-constant-condition" - ] + "name": "prefer-object-has-own", + "description": "Disallow use of `Object.prototype.hasOwnProperty.call()` and prefer use of `Object.hasOwn()`", + "recommended": false, + "fixable": true, + "hasSuggestions": false }, { - "removed": "no-comma-dangle", - "replacedBy": [ - "comma-dangle" - ] + "name": "prefer-object-spread", + "description": "Disallow using Object.assign with an object literal as the first argument and prefer the use of object spread instead", + "recommended": false, + "fixable": true, + "hasSuggestions": false }, { - "removed": "no-empty-class", - "replacedBy": [ - "no-empty-character-class" - ] + "name": "prefer-promise-reject-errors", + "description": "Require using Error objects as Promise rejection reasons", + "recommended": false, + "fixable": false, + "hasSuggestions": false }, { - "removed": "no-empty-label", - "replacedBy": [ - "no-labels" - ] + "name": "prefer-regex-literals", + "description": "Disallow use of the `RegExp` constructor in favor of regular expression literals", + "recommended": false, + "fixable": false, + "hasSuggestions": true }, { - "removed": "no-extra-strict", - "replacedBy": [ - "strict" - ] + "name": "prefer-rest-params", + "description": "Require rest parameters instead of `arguments`", + "recommended": false, + "fixable": false, + "hasSuggestions": false }, { - "removed": "no-reserved-keys", - "replacedBy": [ - "quote-props" - ] + "name": "prefer-spread", + "description": "Require spread operators instead of `.apply()`", + "recommended": false, + "fixable": false, + "hasSuggestions": false }, { - "removed": "no-space-before-semi", - "replacedBy": [ - "semi-spacing" - ] + "name": "prefer-template", + "description": "Require template literals instead of string concatenation", + "recommended": false, + "fixable": true, + "hasSuggestions": false }, { - "removed": "no-wrap-func", - "replacedBy": [ - "no-extra-parens" - ] + "name": "radix", + "description": "Enforce the consistent use of the radix argument when using `parseInt()`", + "recommended": false, + "fixable": false, + "hasSuggestions": true }, { - "removed": "space-after-function-name", - "replacedBy": [ - "space-before-function-paren" - ] + "name": "require-await", + "description": "Disallow async functions which have no `await` expression", + "recommended": false, + "fixable": false, + "hasSuggestions": false }, { - "removed": "space-after-keywords", - "replacedBy": [ - "keyword-spacing" - ] + "name": "require-unicode-regexp", + "description": "Enforce the use of `u` or `v` flag on RegExp", + "recommended": false, + "fixable": false, + "hasSuggestions": true }, { - "removed": "space-before-function-parentheses", - "replacedBy": [ - "space-before-function-paren" - ] + "name": "require-yield", + "description": "Require generator functions to contain `yield`", + "recommended": true, + "fixable": false, + "hasSuggestions": false }, { - "removed": "space-before-keywords", - "replacedBy": [ - "keyword-spacing" - ] + "name": "sort-imports", + "description": "Enforce sorted import declarations within modules", + "recommended": false, + "fixable": true, + "hasSuggestions": false }, { - "removed": "space-in-brackets", - "replacedBy": [ - "object-curly-spacing", - "array-bracket-spacing" - ] + "name": "sort-keys", + "description": "Require object keys to be sorted", + "recommended": false, + "fixable": false, + "hasSuggestions": false }, { - "removed": "space-return-throw-case", - "replacedBy": [ - "keyword-spacing" - ] + "name": "sort-vars", + "description": "Require variables within the same declaration block to be sorted", + "recommended": false, + "fixable": true, + "hasSuggestions": false }, { - "removed": "space-unary-word-ops", - "replacedBy": [ - "space-unary-ops" - ] + "name": "strict", + "description": "Require or disallow strict mode directives", + "recommended": false, + "fixable": true, + "hasSuggestions": false + }, + { + "name": "symbol-description", + "description": "Require symbol descriptions", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "vars-on-top", + "description": "Require `var` declarations be placed at the top of their containing scope", + "recommended": false, + "fixable": false, + "hasSuggestions": false + }, + { + "name": "yoda", + "description": "Require or disallow \"Yoda\" conditions", + "recommended": false, + "fixable": true, + "hasSuggestions": false + } + ], + "layout": [ + { + "name": "line-comment-position", + "description": "Enforce position of line comments", + "recommended": false, + "fixable": false, + "hasSuggestions": false }, { - "removed": "spaced-line-comment", - "replacedBy": [ - "spaced-comment" - ] + "name": "unicode-bom", + "description": "Require or disallow Unicode byte order mark (BOM)", + "recommended": false, + "fixable": true, + "hasSuggestions": false } ] - } + }, + "deprecated": [ + { + "name": "array-bracket-newline", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "array-bracket-spacing", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "array-element-newline", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "arrow-parens", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "arrow-spacing", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "block-spacing", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "brace-style", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "callback-return", + "replacedBy": [], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "comma-dangle", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "comma-spacing", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "comma-style", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "computed-property-spacing", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "dot-location", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "eol-last", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "func-call-spacing", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "function-call-argument-newline", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "function-paren-newline", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "generator-star-spacing", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "global-require", + "replacedBy": [], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "handle-callback-err", + "replacedBy": [], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "id-blacklist", + "replacedBy": [ + "id-denylist" + ], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "implicit-arrow-linebreak", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "indent", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "indent-legacy", + "replacedBy": [ + "indent" + ], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "jsx-quotes", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "key-spacing", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "keyword-spacing", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "linebreak-style", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "lines-around-comment", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "lines-around-directive", + "replacedBy": [ + "padding-line-between-statements" + ], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "lines-between-class-members", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "max-len", + "replacedBy": [], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "max-statements-per-line", + "replacedBy": [], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "multiline-ternary", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "new-parens", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "newline-after-var", + "replacedBy": [ + "padding-line-between-statements" + ], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "newline-before-return", + "replacedBy": [ + "padding-line-between-statements" + ], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "newline-per-chained-call", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "no-buffer-constructor", + "replacedBy": [], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-catch-shadow", + "replacedBy": [ + "no-shadow" + ], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-confusing-arrow", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "no-extra-parens", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "no-extra-semi", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "no-floating-decimal", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "no-mixed-operators", + "replacedBy": [], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-mixed-requires", + "replacedBy": [], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-mixed-spaces-and-tabs", + "replacedBy": [], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-multi-spaces", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "no-multiple-empty-lines", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "no-native-reassign", + "replacedBy": [ + "no-global-assign" + ], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-negated-in-lhs", + "replacedBy": [ + "no-unsafe-negation" + ], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-new-object", + "replacedBy": [ + "no-object-constructor" + ], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-new-require", + "replacedBy": [], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-path-concat", + "replacedBy": [], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-process-env", + "replacedBy": [], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-process-exit", + "replacedBy": [], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-restricted-modules", + "replacedBy": [], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-return-await", + "replacedBy": [], + "fixable": false, + "hasSuggestions": true + }, + { + "name": "no-spaced-func", + "replacedBy": [ + "func-call-spacing" + ], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "no-sync", + "replacedBy": [], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-tabs", + "replacedBy": [], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "no-trailing-spaces", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "no-whitespace-before-property", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "nonblock-statement-body-position", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "object-curly-newline", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "object-curly-spacing", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "object-property-newline", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "one-var-declaration-per-line", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "operator-linebreak", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "padded-blocks", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "padding-line-between-statements", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "prefer-reflect", + "replacedBy": [], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "quote-props", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "quotes", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "require-jsdoc", + "replacedBy": [], + "fixable": false, + "hasSuggestions": false + }, + { + "name": "rest-spread-spacing", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "semi", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "semi-spacing", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "semi-style", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "space-before-blocks", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "space-before-function-paren", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "space-in-parens", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "space-infix-ops", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "space-unary-ops", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "spaced-comment", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "switch-colon-spacing", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "template-curly-spacing", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "template-tag-spacing", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "valid-jsdoc", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "wrap-iife", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "wrap-regex", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + }, + { + "name": "yield-star-spacing", + "replacedBy": [], + "fixable": true, + "hasSuggestions": false + } + ], + "removed": [ + { + "removed": "generator-star", + "replacedBy": [ + "generator-star-spacing" + ] + }, + { + "removed": "global-strict", + "replacedBy": [ + "strict" + ] + }, + { + "removed": "no-arrow-condition", + "replacedBy": [ + "no-confusing-arrow", + "no-constant-condition" + ] + }, + { + "removed": "no-comma-dangle", + "replacedBy": [ + "comma-dangle" + ] + }, + { + "removed": "no-empty-class", + "replacedBy": [ + "no-empty-character-class" + ] + }, + { + "removed": "no-empty-label", + "replacedBy": [ + "no-labels" + ] + }, + { + "removed": "no-extra-strict", + "replacedBy": [ + "strict" + ] + }, + { + "removed": "no-reserved-keys", + "replacedBy": [ + "quote-props" + ] + }, + { + "removed": "no-space-before-semi", + "replacedBy": [ + "semi-spacing" + ] + }, + { + "removed": "no-wrap-func", + "replacedBy": [ + "no-extra-parens" + ] + }, + { + "removed": "space-after-function-name", + "replacedBy": [ + "space-before-function-paren" + ] + }, + { + "removed": "space-after-keywords", + "replacedBy": [ + "keyword-spacing" + ] + }, + { + "removed": "space-before-function-parentheses", + "replacedBy": [ + "space-before-function-paren" + ] + }, + { + "removed": "space-before-keywords", + "replacedBy": [ + "keyword-spacing" + ] + }, + { + "removed": "space-in-brackets", + "replacedBy": [ + "object-curly-spacing", + "array-bracket-spacing" + ] + }, + { + "removed": "space-return-throw-case", + "replacedBy": [ + "keyword-spacing" + ] + }, + { + "removed": "space-unary-word-ops", + "replacedBy": [ + "space-unary-ops" + ] + }, + { + "removed": "spaced-line-comment", + "replacedBy": [ + "spaced-comment" + ] + } + ] } \ No newline at end of file diff --git a/docs/src/_data/rules_categories.js b/docs/src/_data/rules_categories.js new file mode 100644 index 00000000000..46856958f22 --- /dev/null +++ b/docs/src/_data/rules_categories.js @@ -0,0 +1,26 @@ +module.exports = eleventy => { + const PATH_PREFIX = eleventy.PATH_PREFIX; + + return { + problem: { + displayName: "Possible Problems", + description: "These rules relate to possible logic errors in code:" + }, + suggestion: { + displayName: "Suggestions", + description: "These rules suggest alternate ways of doing things:" + }, + layout: { + displayName: "Layout & Formatting", + description: "These rules care about how the code looks rather than how it executes:" + }, + deprecated: { + displayName: "Deprecated", + description: `These rules have been deprecated in accordance with the deprecation policy, and replaced by newer rules:` + }, + removed: { + displayName: "Removed", + description: `These rules from older versions of ESLint (before the deprecation policy existed) have been replaced by newer rules:` + } + } +}; diff --git a/docs/src/_data/rules_meta.json b/docs/src/_data/rules_meta.json index f4004424124..06db19be509 100644 --- a/docs/src/_data/rules_meta.json +++ b/docs/src/_data/rules_meta.json @@ -8,6 +8,8 @@ } }, "array-bracket-newline": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce linebreaks after opening and before closing array brackets", @@ -17,6 +19,8 @@ "fixable": "whitespace" }, "array-bracket-spacing": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent spacing inside array brackets", @@ -35,6 +39,8 @@ "hasSuggestions": true }, "array-element-newline": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce line breaks after each array element", @@ -53,6 +59,8 @@ "fixable": "code" }, "arrow-parens": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Require parentheses around arrow function arguments", @@ -62,6 +70,8 @@ "fixable": "code" }, "arrow-spacing": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent spacing before and after the arrow in arrow functions", @@ -79,6 +89,8 @@ } }, "block-spacing": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Disallow or enforce spaces inside of blocks after opening block and before closing block", @@ -88,6 +100,8 @@ "fixable": "whitespace" }, "brace-style": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent brace style for blocks", @@ -132,6 +146,8 @@ } }, "comma-dangle": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Require or disallow trailing commas", @@ -141,6 +157,8 @@ "fixable": "code" }, "comma-spacing": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent spacing before and after commas", @@ -150,6 +168,8 @@ "fixable": "whitespace" }, "comma-style": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent comma style", @@ -167,6 +187,8 @@ } }, "computed-property-spacing": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent spacing inside computed property brackets", @@ -233,6 +255,8 @@ } }, "dot-location": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent newlines before and after dots", @@ -251,6 +275,8 @@ "fixable": "code" }, "eol-last": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Require or disallow newline at the end of files", @@ -278,6 +304,8 @@ "fixable": null }, "func-call-spacing": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Require or disallow spacing between function identifiers and their invocations", @@ -311,6 +339,8 @@ } }, "function-call-argument-newline": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce line breaks between arguments of a function call", @@ -320,6 +350,8 @@ "fixable": "whitespace" }, "function-paren-newline": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent line breaks inside function parentheses", @@ -329,6 +361,8 @@ "fixable": "whitespace" }, "generator-star-spacing": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent spacing around `*` operators in generator functions", @@ -419,6 +453,8 @@ } }, "implicit-arrow-linebreak": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce the location of arrow function bodies", @@ -428,6 +464,8 @@ "fixable": "whitespace" }, "indent": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent indentation", @@ -458,6 +496,8 @@ } }, "jsx-quotes": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce the consistent use of either double or single quotes in JSX attributes", @@ -467,6 +507,8 @@ "fixable": "whitespace" }, "key-spacing": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent spacing between keys and values in object literal properties", @@ -476,6 +518,8 @@ "fixable": "whitespace" }, "keyword-spacing": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent spacing before and after keywords", @@ -493,6 +537,8 @@ } }, "linebreak-style": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent linebreak style", @@ -502,6 +548,8 @@ "fixable": "whitespace" }, "lines-around-comment": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Require empty lines around comments", @@ -524,6 +572,8 @@ ] }, "lines-between-class-members": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Require or disallow an empty line between class members", @@ -559,6 +609,8 @@ } }, "max-len": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce a maximum line length", @@ -607,6 +659,8 @@ } }, "max-statements-per-line": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce a maximum number of statements allowed per line", @@ -624,6 +678,8 @@ "fixable": "whitespace" }, "multiline-ternary": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce newlines between operands of ternary expressions", @@ -641,6 +697,8 @@ } }, "new-parens": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce or disallow parentheses when invoking a constructor with no arguments", @@ -676,6 +734,8 @@ ] }, "newline-per-chained-call": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Require a newline after each call in a method chain", @@ -698,7 +758,8 @@ "description": "Disallow `Array` constructors", "recommended": false, "url": "https://eslint.org/docs/latest/rules/no-array-constructor" - } + }, + "hasSuggestions": true }, "no-async-promise-executor": { "type": "problem", @@ -789,6 +850,8 @@ } }, "no-confusing-arrow": { + "deprecated": true, + "replacedBy": [], "type": "suggestion", "docs": { "description": "Disallow arrow functions where they could be confused with comparisons", @@ -803,7 +866,8 @@ "description": "Disallow the use of `console`", "recommended": false, "url": "https://eslint.org/docs/latest/rules/no-console" - } + }, + "hasSuggestions": true }, "no-const-assign": { "type": "problem", @@ -1038,6 +1102,8 @@ "fixable": "code" }, "no-extra-parens": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Disallow unnecessary parentheses", @@ -1047,6 +1113,8 @@ "fixable": "code" }, "no-extra-semi": { + "deprecated": true, + "replacedBy": [], "type": "suggestion", "docs": { "description": "Disallow unnecessary semicolons", @@ -1064,6 +1132,8 @@ } }, "no-floating-decimal": { + "deprecated": true, + "replacedBy": [], "type": "suggestion", "docs": { "description": "Disallow leading or trailing decimal points in numeric literals", @@ -1236,6 +1306,8 @@ "hasSuggestions": true }, "no-mixed-operators": { + "deprecated": true, + "replacedBy": [], "type": "suggestion", "docs": { "description": "Disallow mixed binary operators", @@ -1254,6 +1326,8 @@ } }, "no-mixed-spaces-and-tabs": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Disallow mixed spaces and tabs for indentation", @@ -1270,6 +1344,8 @@ } }, "no-multi-spaces": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Disallow multiple spaces", @@ -1287,6 +1363,8 @@ } }, "no-multiple-empty-lines": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Disallow multiple empty lines", @@ -1508,7 +1586,8 @@ "description": "Disallow calling some `Object.prototype` methods directly on objects", "recommended": true, "url": "https://eslint.org/docs/latest/rules/no-prototype-builtins" - } + }, + "hasSuggestions": true }, "no-redeclare": { "type": "suggestion", @@ -1685,6 +1764,8 @@ } }, "no-tabs": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Disallow all tabs", @@ -1725,6 +1806,8 @@ } }, "no-trailing-spaces": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Disallow trailing whitespace at the end of lines", @@ -1977,6 +2060,8 @@ } }, "no-whitespace-before-property": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Disallow whitespace before properties", @@ -1994,6 +2079,8 @@ } }, "nonblock-statement-body-position": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce the location of single-line statements", @@ -2003,6 +2090,8 @@ "fixable": "whitespace" }, "object-curly-newline": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent line breaks after opening and before closing braces", @@ -2012,6 +2101,8 @@ "fixable": "whitespace" }, "object-curly-spacing": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent spacing inside braces", @@ -2021,6 +2112,8 @@ "fixable": "whitespace" }, "object-property-newline": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce placing object properties on separate lines", @@ -2048,6 +2141,8 @@ "fixable": "code" }, "one-var-declaration-per-line": { + "deprecated": true, + "replacedBy": [], "type": "suggestion", "docs": { "description": "Require or disallow newlines around variable declarations", @@ -2066,6 +2161,8 @@ "fixable": "code" }, "operator-linebreak": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent linebreak style for operators", @@ -2075,6 +2172,8 @@ "fixable": "code" }, "padded-blocks": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Require or disallow padding within blocks", @@ -2084,6 +2183,8 @@ "fixable": "whitespace" }, "padding-line-between-statements": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Require or disallow padding lines between statements", @@ -2219,6 +2320,8 @@ "fixable": "code" }, "quote-props": { + "deprecated": true, + "replacedBy": [], "type": "suggestion", "docs": { "description": "Require quotes around object literal property names", @@ -2228,6 +2331,8 @@ "fixable": "code" }, "quotes": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce the consistent use of either backticks, double, or single quotes", @@ -2290,6 +2395,8 @@ } }, "rest-spread-spacing": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce spacing between rest and spread operators and their expressions", @@ -2299,6 +2406,8 @@ "fixable": "whitespace" }, "semi": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Require or disallow semicolons instead of ASI", @@ -2308,6 +2417,8 @@ "fixable": "code" }, "semi-spacing": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent spacing before and after semicolons", @@ -2317,6 +2428,8 @@ "fixable": "whitespace" }, "semi-style": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce location of semicolons", @@ -2352,6 +2465,8 @@ "fixable": "code" }, "space-before-blocks": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent spacing before blocks", @@ -2361,6 +2476,8 @@ "fixable": "whitespace" }, "space-before-function-paren": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent spacing before `function` definition opening parenthesis", @@ -2370,6 +2487,8 @@ "fixable": "whitespace" }, "space-in-parens": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent spacing inside parentheses", @@ -2379,6 +2498,8 @@ "fixable": "whitespace" }, "space-infix-ops": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Require spacing around infix operators", @@ -2388,6 +2509,8 @@ "fixable": "whitespace" }, "space-unary-ops": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce consistent spacing before or after unary operators", @@ -2397,6 +2520,8 @@ "fixable": "whitespace" }, "spaced-comment": { + "deprecated": true, + "replacedBy": [], "type": "suggestion", "docs": { "description": "Enforce consistent spacing after the `//` or `/*` in a comment", @@ -2415,6 +2540,8 @@ "fixable": "code" }, "switch-colon-spacing": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Enforce spacing around colons of switch statements", @@ -2433,6 +2560,8 @@ "fixable": null }, "template-curly-spacing": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Require or disallow spacing around embedded expressions of template strings", @@ -2442,6 +2571,8 @@ "fixable": "whitespace" }, "template-tag-spacing": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Require or disallow spacing between template tags and their literals", @@ -2496,6 +2627,8 @@ } }, "wrap-iife": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Require parentheses around immediate `function` invocations", @@ -2505,6 +2638,8 @@ "fixable": "code" }, "wrap-regex": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Require parenthesis around regex literals", @@ -2514,6 +2649,8 @@ "fixable": "code" }, "yield-star-spacing": { + "deprecated": true, + "replacedBy": [], "type": "layout", "docs": { "description": "Require or disallow spacing around the `*` in `yield*` expressions", diff --git a/docs/src/_includes/components/rule-categories.macro.html b/docs/src/_includes/components/rule-categories.macro.html index 193f6def64e..f38d371049e 100644 --- a/docs/src/_includes/components/rule-categories.macro.html +++ b/docs/src/_includes/components/rule-categories.macro.html @@ -21,7 +21,7 @@
💡 hasSuggestions

- Some problems reported by this rule are manually fixable by editor suggestions + Some problems reported by this rule are manually fixable by editor suggestions

{%- endif -%} diff --git a/docs/src/_includes/components/rule.macro.html b/docs/src/_includes/components/rule.macro.html index e4cf876d0d9..cd5d61b0386 100644 --- a/docs/src/_includes/components/rule.macro.html +++ b/docs/src/_includes/components/rule.macro.html @@ -26,22 +26,24 @@

{{ params.description }}

{%- endif -%}
+ {%- if params.removed == undefined -%}
Categories: - {%- if (params.deprecated) or (params.removed) -%} + {%- if params.deprecated -%}

{%- else -%} -

+

{%- endif -%} -

+

-

+

+ {%- endif -%} {%- endmacro -%} diff --git a/docs/src/_includes/components/social-icons.html b/docs/src/_includes/components/social-icons.html index bb025af5b13..6f2b887e949 100644 --- a/docs/src/_includes/components/social-icons.html +++ b/docs/src/_includes/components/social-icons.html @@ -3,11 +3,8 @@