From 42962cdd9c469e814ce11b1ada4cdf0ab67c2ed3 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Wed, 16 Jan 2019 02:08:50 +0000 Subject: [PATCH] Update: Allow regex in no-param-reassign ignore option array --- docs/rules/no-param-reassign.md | 6 +++++- lib/rules/no-param-reassign.js | 31 ++++++++++++++++++++++++++-- tests/lib/rules/no-param-reassign.js | 17 +++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/docs/rules/no-param-reassign.md b/docs/rules/no-param-reassign.md index 51c81a8c0935..64fa919ac79d 100644 --- a/docs/rules/no-param-reassign.md +++ b/docs/rules/no-param-reassign.md @@ -77,7 +77,7 @@ function foo(bar) { Examples of **correct** code for the `{ "props": true }` option with `"ignorePropertyModificationsFor"` set: ```js -/*eslint no-param-reassign: ["error", { "props": true, "ignorePropertyModificationsFor": ["bar"] }]*/ +/*eslint no-param-reassign: ["error", { "props": true, "ignorePropertyModificationsFor": ["bar", "/^foo/"] }]*/ function foo(bar) { bar.prop = "value"; @@ -90,6 +90,10 @@ function foo(bar) { function foo(bar) { bar.aaa++; } + +function foo(fooBar) { + fooBar.aaa++; +} ``` diff --git a/lib/rules/no-param-reassign.js b/lib/rules/no-param-reassign.js index 349345c148a7..6976449e894c 100644 --- a/lib/rules/no-param-reassign.js +++ b/lib/rules/no-param-reassign.js @@ -56,7 +56,21 @@ module.exports = { create(context) { const props = context.options[0] && Boolean(context.options[0].props); - const ignoredPropertyAssignmentsFor = context.options[0] && context.options[0].ignorePropertyModificationsFor || []; + let ignoredPropertyAssignments = context.options[0] && context.options[0].ignorePropertyModificationsFor || []; + + /** + * Parses a string that describes a property to ignore into a regular expression object. + * + * @param {Array} ignoredProperty - A string to parse into a regular expression. + * @returns {regexp} The resulting regular expression object representing the ignoredProperty string. + */ + function parseIgnoredPropertyAssignment(ignoredProperty) { + const matches = /^\/(.*)\/(.*)$/.exec(ignoredProperty); + + return matches ? new RegExp(matches[1], matches[2]) : new RegExp(`^${ignoredProperty}$`); + } + + ignoredPropertyAssignments = ignoredPropertyAssignments.map(parseIgnoredPropertyAssignment); /** * Checks whether or not the reference modifies properties of its variable. @@ -125,6 +139,19 @@ module.exports = { return false; } + /** + * Tests if a string that describes an assigned property name matches + * any of the ignored property regular expressions. + * + * @param {string} propertyName - A string that described an assigned property name. + * @returns {boolean} Whether the string matches an ignore property regular + * expression or not. + */ + function isIgnoredPropertyAssignment(propertyName) { + + return ignoredPropertyAssignments.some(ignoredProperty => ignoredProperty.test(propertyName)); + } + /** * Reports a reference if is non initializer and writable. * @param {Reference} reference - A reference to check. @@ -146,7 +173,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 40156f481ec8..e758e3b843a7 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, ignorePropertyModificationsFor: ["/^a.*$/"] }] }, + { code: "function foo(aFoo) { ++aFoo.b; }", options: [{ props: true, ignorePropertyModificationsFor: ["/^a.*$/"] }] }, + { code: "function foo(aFoo) { delete aFoo.b; }", options: [{ props: true, ignorePropertyModificationsFor: ["/^a.*$/"] }] }, + { code: "function foo(a, z) { aFoo.b = 0; x.y = 0; }", options: [{ props: true, ignorePropertyModificationsFor: ["/^a.*$/", "/^x.*$/"] }] }, + { code: "function foo(aFoo) { aFoo.b.c = 0;}", options: [{ props: true, ignorePropertyModificationsFor: ["/^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, ignorePropertyModificationsFor: ["/^a.*$/"] }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Assignment to property of function parameter 'bar'." }] + }, + { + code: "function foo(bar) { [bar.a] = []; }", + options: [{ props: true, ignorePropertyModificationsFor: ["/^B.*$/"] }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Assignment to property of function parameter 'bar'." }] + }, { code: "function foo(bar) { ({foo: bar.a} = {}); }", options: [{ props: true }],