From e798ea96eb1464e115128e281da623143cfdc1b2 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Wed, 16 Jan 2019 02:08:50 +0000 Subject: [PATCH] Update: Add ignored prop regex no-param-reassign Uses new `ignoredPropertyAssignmentsRegex` option. --- docs/rules/no-param-reassign.md | 20 +++++++++++++++++++- lib/rules/no-param-reassign.js | 23 ++++++++++++++++++++++- tests/lib/rules/no-param-reassign.js | 17 +++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/docs/rules/no-param-reassign.md b/docs/rules/no-param-reassign.md index 51c81a8c093..658215b437a 100644 --- a/docs/rules/no-param-reassign.md +++ b/docs/rules/no-param-reassign.md @@ -34,7 +34,7 @@ function foo(bar) { ## Options -This rule takes one option, an object, with a boolean property `"props"` and an array `"ignorePropertyModificationsFor"`. `"props"` is `false` by default. If `"props"` is set to `true`, this rule warns against the modification of parameter properties unless they're included in `"ignorePropertyModificationsFor"`, which is an empty array by default. +This rule takes one option, an object, with a boolean property `"props"`, and arrays `"ignorePropertyModificationsFor"` and `"ignorePropertyModificationsForRegex"`. `"props"` is `false` by default. If `"props"` is set to `true`, this rule warns against the modification of parameter properties unless they're included in `"ignorePropertyModificationsFor"` or `"ignorePropertyModificationsForRegex"`, which is an empty array by default. ### props @@ -92,6 +92,24 @@ function foo(bar) { } ``` +Examples of **correct** code for the `{ "props": true }` option with `"ignorePropertyModificationsForRegex"` set: + +```js +/*eslint no-param-reassign: ["error", { "props": true, "ignorePropertyModificationsForRegex": ["^bar"] }]*/ + +function foo(bar) { + barVar.prop = "value"; +} + +function foo(bar) { + delete barrito.aaa; +} + +function foo(bar) { + bar_.aaa++; +} +``` + ## When Not To Use It diff --git a/lib/rules/no-param-reassign.js b/lib/rules/no-param-reassign.js index 880ff93cb53..15150c03b24 100644 --- a/lib/rules/no-param-reassign.js +++ b/lib/rules/no-param-reassign.js @@ -45,6 +45,13 @@ module.exports = { type: "string" }, uniqueItems: true + }, + ignorePropertyModificationsForRegex: { + type: "array", + items: { + type: "string" + }, + uniqueItems: true } }, additionalProperties: false @@ -57,6 +64,7 @@ module.exports = { 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 || []; /** * Checks whether or not the reference modifies properties of its variable. @@ -125,6 +133,19 @@ module.exports = { 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. @@ -146,7 +167,7 @@ module.exports = { ) { 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) { + } else if (props && isModifyingProp(reference) && !isIgnoredPropertyAssignment(identifier.name)) { context.report({ node: identifier, message: "Assignment to property of function parameter '{{name}}'.", data: { name: identifier.name } }); } } diff --git a/tests/lib/rules/no-param-reassign.js b/tests/lib/rules/no-param-reassign.js index 40156f481ec..a7540228cc9 100644 --- a/tests/lib/rules/no-param-reassign.js +++ b/tests/lib/rules/no-param-reassign.js @@ -39,6 +39,11 @@ ruleTester.run("no-param-reassign", rule, { { code: "function foo(a) { delete a.b; }", options: [{ props: true, ignorePropertyModificationsFor: ["a"] }] }, { code: "function foo(a, z) { a.b = 0; x.y = 0; }", options: [{ props: true, ignorePropertyModificationsFor: ["a", "x"] }] }, { code: "function foo(a) { a.b.c = 0;}", options: [{ props: true, ignorePropertyModificationsFor: ["a"] }] }, + { code: "function foo(aFoo) { aFoo.b = 0; }", options: [{ props: true, ignorePropertyModificationsForRegex: ["^a.*$"] }] }, + { code: "function foo(aFoo) { ++aFoo.b; }", options: [{ props: true, ignorePropertyModificationsForRegex: ["^a.*$"] }] }, + { code: "function foo(aFoo) { delete aFoo.b; }", options: [{ props: true, ignorePropertyModificationsForRegex: ["^a.*$"] }] }, + { code: "function foo(a, z) { aFoo.b = 0; x.y = 0; }", options: [{ props: true, ignorePropertyModificationsForRegex: ["^a.*$", "^x.*$"] }] }, + { code: "function foo(aFoo) { aFoo.b.c = 0;}", options: [{ props: true, ignorePropertyModificationsForRegex: ["^a.*$"] }] }, { code: "function foo(a) { ({ [a]: variable } = value) }", options: [{ props: true }], @@ -106,6 +111,18 @@ ruleTester.run("no-param-reassign", rule, { parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Assignment to property of function parameter 'bar'." }] }, + { + code: "function foo(bar) { [bar.a] = []; }", + options: [{ props: true, ignorePropertyModificationsForRegex: ["^a.*$"] }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Assignment to property of function parameter 'bar'." }] + }, + { + code: "function foo(bar) { [bar.a] = []; }", + options: [{ props: true, ignorePropertyModificationsForRegex: ["^B.*$"] }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Assignment to property of function parameter 'bar'." }] + }, { code: "function foo(bar) { ({foo: bar.a} = {}); }", options: [{ props: true }],