| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 | /** * @fileoverview Rule to disallow unnecessary computed property keys in object literals * @author Burak Yigit Kaya */"use strict";//------------------------------------------------------------------------------// Requirements//------------------------------------------------------------------------------const astUtils = require("./utils/ast-utils");//------------------------------------------------------------------------------// Helpers//------------------------------------------------------------------------------/** * Determines whether the computed key syntax is unnecessarily used for the given node. * In particular, it determines whether removing the square brackets and using the content between them * directly as the key (e.g. ['foo'] -> 'foo') would produce valid syntax and preserve the same behavior. * Valid non-computed keys are only: identifiers, number literals and string literals. * Only literals can preserve the same behavior, with a few exceptions for specific node types: * Property *   - { ["__proto__"]: foo } defines a property named "__proto__" *     { "__proto__": foo } defines object's prototype * PropertyDefinition *   - class C { ["constructor"]; } defines an instance field named "constructor" *     class C { "constructor"; } produces a parsing error *   - class C { static ["constructor"]; } defines a static field named "constructor" *     class C { static "constructor"; } produces a parsing error *   - class C { static ["prototype"]; } produces a runtime error (doesn't break the whole script) *     class C { static "prototype"; } produces a parsing error (breaks the whole script) * MethodDefinition *   - class C { ["constructor"]() {} } defines a prototype method named "constructor" *     class C { "constructor"() {} } defines the constructor *   - class C { static ["prototype"]() {} } produces a runtime error (doesn't break the whole script) *     class C { static "prototype"() {} } produces a parsing error (breaks the whole script) * @param {ASTNode} node The node to check. It can be `Property`, `PropertyDefinition` or `MethodDefinition`. * @throws {Error} (Unreachable.) * @returns {void} `true` if the node has useless computed key. */function hasUselessComputedKey(node) {    if (!node.computed) {        return false;    }    const { key } = node;    if (key.type !== "Literal") {        return false;    }    const { value } = key;    if (typeof value !== "number" && typeof value !== "string") {        return false;    }    switch (node.type) {        case "Property":            return value !== "__proto__";        case "PropertyDefinition":            if (node.static) {                return value !== "constructor" && value !== "prototype";            }            return value !== "constructor";        case "MethodDefinition":            if (node.static) {                return value !== "prototype";            }            return value !== "constructor";        /* c8 ignore next */        default:            throw new Error(`Unexpected node type: ${node.type}`);    }}//------------------------------------------------------------------------------// Rule Definition//------------------------------------------------------------------------------/** @type {import('../shared/types').Rule} */module.exports = {    meta: {        type: "suggestion",        docs: {            description: "Disallow unnecessary computed property keys in objects and classes",            recommended: false,            url: "https://eslint.org/docs/rules/no-useless-computed-key"        },        schema: [{            type: "object",            properties: {                enforceForClassMembers: {                    type: "boolean",                    default: false                }            },            additionalProperties: false        }],        fixable: "code",        messages: {            unnecessarilyComputedProperty: "Unnecessarily computed property [{{property}}] found."        }    },    create(context) {        const sourceCode = context.getSourceCode();        const enforceForClassMembers = context.options[0] && context.options[0].enforceForClassMembers;        /**         * Reports a given node if it violated this rule.         * @param {ASTNode} node The node to check.         * @returns {void}         */        function check(node) {            if (hasUselessComputedKey(node)) {                const { key } = node;                context.report({                    node,                    messageId: "unnecessarilyComputedProperty",                    data: { property: sourceCode.getText(key) },                    fix(fixer) {                        const leftSquareBracket = sourceCode.getTokenBefore(key, astUtils.isOpeningBracketToken);                        const rightSquareBracket = sourceCode.getTokenAfter(key, astUtils.isClosingBracketToken);                        // If there are comments between the brackets and the property name, don't do a fix.                        if (sourceCode.commentsExistBetween(leftSquareBracket, rightSquareBracket)) {                            return null;                        }                        const tokenBeforeLeftBracket = sourceCode.getTokenBefore(leftSquareBracket);                        // Insert a space before the key to avoid changing identifiers, e.g. ({ get[2]() {} }) to ({ get2() {} })                        const needsSpaceBeforeKey = tokenBeforeLeftBracket.range[1] === leftSquareBracket.range[0] &&                            !astUtils.canTokensBeAdjacent(tokenBeforeLeftBracket, sourceCode.getFirstToken(key));                        const replacementKey = (needsSpaceBeforeKey ? " " : "") + key.raw;                        return fixer.replaceTextRange([leftSquareBracket.range[0], rightSquareBracket.range[1]], replacementKey);                    }                });            }        }        /**         * A no-op function to act as placeholder for checking a node when the `enforceForClassMembers` option is `false`.         * @returns {void}         * @private         */        function noop() {}        return {            Property: check,            MethodDefinition: enforceForClassMembers ? check : noop,            PropertyDefinition: enforceForClassMembers ? check : noop        };    }};
 |