| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 | /** * @fileoverview Rule to disallow `\8` and `\9` escape sequences in string literals. * @author Milos Djermanovic */"use strict";//------------------------------------------------------------------------------// Helpers//------------------------------------------------------------------------------const QUICK_TEST_REGEX = /\\[89]/u;/** * Returns unicode escape sequence that represents the given character. * @param {string} character A single code unit. * @returns {string} "\uXXXX" sequence. */function getUnicodeEscape(character) {    return `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`;}//------------------------------------------------------------------------------// Rule Definition//------------------------------------------------------------------------------/** @type {import('../shared/types').Rule} */module.exports = {    meta: {        type: "suggestion",        docs: {            description: "Disallow `\\8` and `\\9` escape sequences in string literals",            recommended: true,            url: "https://eslint.org/docs/rules/no-nonoctal-decimal-escape"        },        hasSuggestions: true,        schema: [],        messages: {            decimalEscape: "Don't use '{{decimalEscape}}' escape sequence.",            // suggestions            refactor: "Replace '{{original}}' with '{{replacement}}'. This maintains the current functionality.",            escapeBackslash: "Replace '{{original}}' with '{{replacement}}' to include the actual backslash character."        }    },    create(context) {        const sourceCode = context.getSourceCode();        /**         * Creates a new Suggestion object.         * @param {string} messageId "refactor" or "escapeBackslash".         * @param {int[]} range The range to replace.         * @param {string} replacement New text for the range.         * @returns {Object} Suggestion         */        function createSuggestion(messageId, range, replacement) {            return {                messageId,                data: {                    original: sourceCode.getText().slice(...range),                    replacement                },                fix(fixer) {                    return fixer.replaceTextRange(range, replacement);                }            };        }        return {            Literal(node) {                if (typeof node.value !== "string") {                    return;                }                if (!QUICK_TEST_REGEX.test(node.raw)) {                    return;                }                const regex = /(?:[^\\]|(?<previousEscape>\\.))*?(?<decimalEscape>\\[89])/suy;                let match;                while ((match = regex.exec(node.raw))) {                    const { previousEscape, decimalEscape } = match.groups;                    const decimalEscapeRangeEnd = node.range[0] + match.index + match[0].length;                    const decimalEscapeRangeStart = decimalEscapeRangeEnd - decimalEscape.length;                    const decimalEscapeRange = [decimalEscapeRangeStart, decimalEscapeRangeEnd];                    const suggest = [];                    // When `regex` is matched, `previousEscape` can only capture characters adjacent to `decimalEscape`                    if (previousEscape === "\\0") {                        /*                         * Now we have a NULL escape "\0" immediately followed by a decimal escape, e.g.: "\0\8".                         * Fixing this to "\08" would turn "\0" into a legacy octal escape. To avoid producing                         * an octal escape while fixing a decimal escape, we provide different suggestions.                         */                        suggest.push(                            createSuggestion( // "\0\8" -> "\u00008"                                "refactor",                                [decimalEscapeRangeStart - previousEscape.length, decimalEscapeRangeEnd],                                `${getUnicodeEscape("\0")}${decimalEscape[1]}`                            ),                            createSuggestion( // "\8" -> "\u0038"                                "refactor",                                decimalEscapeRange,                                getUnicodeEscape(decimalEscape[1])                            )                        );                    } else {                        suggest.push(                            createSuggestion( // "\8" -> "8"                                "refactor",                                decimalEscapeRange,                                decimalEscape[1]                            )                        );                    }                    suggest.push(                        createSuggestion( // "\8" -> "\\8"                            "escapeBackslash",                            decimalEscapeRange,                            `\\${decimalEscape}`                        )                    );                    context.report({                        node,                        loc: {                            start: sourceCode.getLocFromIndex(decimalEscapeRangeStart),                            end: sourceCode.getLocFromIndex(decimalEscapeRangeEnd)                        },                        messageId: "decimalEscape",                        data: {                            decimalEscape                        },                        suggest                    });                }            }        };    }};
 |