|                                                                                                                                                                                                                                      |  | /** * @fileoverview Disallow reassignment of function parameters. * @author Nat Burns */"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
const stopNodePattern = /(?:Statement|Declaration|Function(?:Expression)?|Program)$/u;
/** @type {import('../shared/types').Rule} */module.exports = {    meta: {        type: "suggestion",
        docs: {            description: "Disallow reassigning `function` parameters",            recommended: false,            url: "https://eslint.org/docs/latest/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                            },                            ignorePropertyModificationsForRegex: {                                type: "array",                                items: {                                    type: "string"                                },                                uniqueItems: true                            }                        },                        additionalProperties: false                    }                ]            }        ],
        messages: {            assignmentToFunctionParam: "Assignment to function parameter '{{name}}'.",            assignmentToFunctionParamProp: "Assignment to property of function parameter '{{name}}'."        }    },
    create(context) {        const props = context.options[0] && context.options[0].props;        const ignoredPropertyAssignmentsFor = context.options[0] && context.options[0].ignorePropertyModificationsFor || [];        const ignoredPropertyAssignmentsForRegex = context.options[0] && context.options[0].ignorePropertyModificationsForRegex || [];        const sourceCode = context.sourceCode;
        /**         * 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) ||                    parent.type === "ForInStatement" || parent.type === "ForOfStatement")) {                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;
                    // e.g. for (foo.a in b) {}
                    case "ForInStatement":                    case "ForOfStatement":                        if (parent.left === node) {                            return true;                        }
                        // this is a stop node for parent.right and parent.body
                        return false;
                    // 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;
                    // EXCLUDES: e.g. (foo ? a : b).c = bar;
                    case "ConditionalExpression":                        if (parent.test === node) {                            return false;                        }
                        break;
                    // no default
                }
                node = parent;                parent = node.parent;            }
            return false;        }
        /**         * Tests that an identifier name matches any of the ignored property assignments.         * First we test strings in ignoredPropertyAssignmentsFor.         * Then we instantiate and test RegExp objects from ignoredPropertyAssignmentsForRegex strings.         * @param {string} identifierName A string that describes the name of an identifier to         * ignore property assignments for.         * @returns {boolean} Whether the string matches an ignored property assignment regular expression or not.         */        function isIgnoredPropertyAssignment(identifierName) {            return ignoredPropertyAssignmentsFor.includes(identifierName) ||                ignoredPropertyAssignmentsForRegex.some(ignored => new RegExp(ignored, "u").test(identifierName));        }
        /**         * 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,                        messageId: "assignmentToFunctionParam",                        data: { name: identifier.name }                    });                } else if (props && isModifyingProp(reference) && !isIgnoredPropertyAssignment(identifier.name)) {                    context.report({                        node: identifier,                        messageId: "assignmentToFunctionParamProp",                        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) {            sourceCode.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        };
    }};
 |