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
- });
- }
- }
- };
- }
- };
|