| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 | /** * @fileoverview Disallow reassignment of function parameters. * @author Nat Burns */"use strict";//------------------------------------------------------------------------------// Rule Definition//------------------------------------------------------------------------------const stopNodePattern = /(?:Statement|Declaration|Function(?:Expression)?|Program)$/;module.exports = {    meta: {        docs: {            description: "disallow reassigning `function` parameters",            category: "Best Practices",            recommended: false,            url: "https://eslint.org/docs/rules/no-param-reassign"        },        schema: [            {                oneOf: [                    {                        type: "object",                        properties: {                            props: {                                enum: [false]                            }                        },                        additionalProperties: false                    },                    {                        type: "object",                        properties: {                            props: {                                enum: [true]                            },                            ignorePropertyModificationsFor: {                                type: "array",                                items: {                                    type: "string"                                },                                uniqueItems: true                            }                        },                        additionalProperties: false                    }                ]            }        ]    },    create(context) {        const props = context.options[0] && Boolean(context.options[0].props);        const ignoredPropertyAssignmentsFor = context.options[0] && context.options[0].ignorePropertyModificationsFor || [];        /**         * Checks whether or not the reference modifies properties of its variable.         * @param {Reference} reference - A reference to check.         * @returns {boolean} Whether or not the reference modifies properties of its variable.         */        function isModifyingProp(reference) {            let node = reference.identifier;            let parent = node.parent;            while (parent && !stopNodePattern.test(parent.type)) {                switch (parent.type) {                    // e.g. foo.a = 0;                    case "AssignmentExpression":                        return parent.left === node;                    // e.g. ++foo.a;                    case "UpdateExpression":                        return true;                    // e.g. delete foo.a;                    case "UnaryExpression":                        if (parent.operator === "delete") {                            return true;                        }                        break;                    // EXCLUDES: e.g. cache.get(foo.a).b = 0;                    case "CallExpression":                        if (parent.callee !== node) {                            return false;                        }                        break;                    // EXCLUDES: e.g. cache[foo.a] = 0;                    case "MemberExpression":                        if (parent.property === node) {                            return false;                        }                        break;                    // EXCLUDES: e.g. ({ [foo]: a }) = bar;                    case "Property":                        if (parent.key === node) {                            return false;                        }                        break;                    // no default                }                node = parent;                parent = node.parent;            }            return false;        }        /**         * Reports a reference if is non initializer and writable.         * @param {Reference} reference - A reference to check.         * @param {int} index - The index of the reference in the references.         * @param {Reference[]} references - The array that the reference belongs to.         * @returns {void}         */        function checkReference(reference, index, references) {            const identifier = reference.identifier;            if (identifier &&                !reference.init &&                /*                 * Destructuring assignments can have multiple default value,                 * so possibly there are multiple writeable references for the same identifier.                 */                (index === 0 || references[index - 1].identifier !== identifier)            ) {                if (reference.isWrite()) {                    context.report({ node: identifier, message: "Assignment to function parameter '{{name}}'.", data: { name: identifier.name } });                } else if (props && isModifyingProp(reference) && ignoredPropertyAssignmentsFor.indexOf(identifier.name) === -1) {                    context.report({ node: identifier, message: "Assignment to property of function parameter '{{name}}'.", data: { name: identifier.name } });                }            }        }        /**         * Finds and reports references that are non initializer and writable.         * @param {Variable} variable - A variable to check.         * @returns {void}         */        function checkVariable(variable) {            if (variable.defs[0].type === "Parameter") {                variable.references.forEach(checkReference);            }        }        /**         * Checks parameters of a given function node.         * @param {ASTNode} node - A function node to check.         * @returns {void}         */        function checkForFunction(node) {            context.getDeclaredVariables(node).forEach(checkVariable);        }        return {            // `:exit` is needed for the `node.parent` property of identifier nodes.            "FunctionDeclaration:exit": checkForFunction,            "FunctionExpression:exit": checkForFunction,            "ArrowFunctionExpression:exit": checkForFunction        };    }};
 |