| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 | /** * @fileoverview Rule to warn about using dot notation instead of square bracket notation when possible. * @author Josh Perez */"use strict";//------------------------------------------------------------------------------// Requirements//------------------------------------------------------------------------------const astUtils = require("./utils/ast-utils");const keywords = require("./utils/keywords");//------------------------------------------------------------------------------// Rule Definition//------------------------------------------------------------------------------const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/u;// `null` literal must be handled separately.const literalTypesToCheck = new Set(["string", "boolean"]);/** @type {import('../shared/types').Rule} */module.exports = {    meta: {        type: "suggestion",        docs: {            description: "Enforce dot notation whenever possible",            recommended: false,            url: "https://eslint.org/docs/rules/dot-notation"        },        schema: [            {                type: "object",                properties: {                    allowKeywords: {                        type: "boolean",                        default: true                    },                    allowPattern: {                        type: "string",                        default: ""                    }                },                additionalProperties: false            }        ],        fixable: "code",        messages: {            useDot: "[{{key}}] is better written in dot notation.",            useBrackets: ".{{key}} is a syntax error."        }    },    create(context) {        const options = context.options[0] || {};        const allowKeywords = options.allowKeywords === void 0 || options.allowKeywords;        const sourceCode = context.getSourceCode();        let allowPattern;        if (options.allowPattern) {            allowPattern = new RegExp(options.allowPattern, "u");        }        /**         * Check if the property is valid dot notation         * @param {ASTNode} node The dot notation node         * @param {string} value Value which is to be checked         * @returns {void}         */        function checkComputedProperty(node, value) {            if (                validIdentifier.test(value) &&                (allowKeywords || !keywords.includes(String(value))) &&                !(allowPattern && allowPattern.test(value))            ) {                const formattedValue = node.property.type === "Literal" ? JSON.stringify(value) : `\`${value}\``;                context.report({                    node: node.property,                    messageId: "useDot",                    data: {                        key: formattedValue                    },                    *fix(fixer) {                        const leftBracket = sourceCode.getTokenAfter(node.object, astUtils.isOpeningBracketToken);                        const rightBracket = sourceCode.getLastToken(node);                        const nextToken = sourceCode.getTokenAfter(node);                        // Don't perform any fixes if there are comments inside the brackets.                        if (sourceCode.commentsExistBetween(leftBracket, rightBracket)) {                            return;                        }                        // Replace the brackets by an identifier.                        if (!node.optional) {                            yield fixer.insertTextBefore(                                leftBracket,                                astUtils.isDecimalInteger(node.object) ? " ." : "."                            );                        }                        yield fixer.replaceTextRange(                            [leftBracket.range[0], rightBracket.range[1]],                            value                        );                        // Insert a space after the property if it will be connected to the next token.                        if (                            nextToken &&                            rightBracket.range[1] === nextToken.range[0] &&                            !astUtils.canTokensBeAdjacent(String(value), nextToken)                        ) {                            yield fixer.insertTextAfter(node, " ");                        }                    }                });            }        }        return {            MemberExpression(node) {                if (                    node.computed &&                    node.property.type === "Literal" &&                    (literalTypesToCheck.has(typeof node.property.value) || astUtils.isNullLiteral(node.property))                ) {                    checkComputedProperty(node, node.property.value);                }                if (                    node.computed &&                    node.property.type === "TemplateLiteral" &&                    node.property.expressions.length === 0                ) {                    checkComputedProperty(node, node.property.quasis[0].value.cooked);                }                if (                    !allowKeywords &&                    !node.computed &&                    node.property.type === "Identifier" &&                    keywords.includes(String(node.property.name))                ) {                    context.report({                        node: node.property,                        messageId: "useBrackets",                        data: {                            key: node.property.name                        },                        *fix(fixer) {                            const dotToken = sourceCode.getTokenBefore(node.property);                            // A statement that starts with `let[` is parsed as a destructuring variable declaration, not a MemberExpression.                            if (node.object.type === "Identifier" && node.object.name === "let" && !node.optional) {                                return;                            }                            // Don't perform any fixes if there are comments between the dot and the property name.                            if (sourceCode.commentsExistBetween(dotToken, node.property)) {                                return;                            }                            // Replace the identifier to brackets.                            if (!node.optional) {                                yield fixer.remove(dotToken);                            }                            yield fixer.replaceText(node.property, `["${node.property.name}"]`);                        }                    });                }            }        };    }};
 |