Skip to content

Commit

Permalink
Update: Allow regex in no-param-reassign ignore option array
Browse files Browse the repository at this point in the history
  • Loading branch information
lbennett-stacki committed Jan 16, 2019
1 parent da9174e commit 359c3bd
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 3 deletions.
6 changes: 5 additions & 1 deletion docs/rules/no-param-reassign.md
Expand Up @@ -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";
Expand All @@ -90,6 +90,10 @@ function foo(bar) {
function foo(bar) {
bar.aaa++;
}

function foo(fooBar) {
fooBar.aaa++;
}
```


Expand Down
34 changes: 32 additions & 2 deletions lib/rules/no-param-reassign.js
Expand Up @@ -56,7 +56,25 @@ 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 into a regular expression object.
* If the string already represents a regular expression, it will copy the expression.
* If the string does not represent a regular expression, it will create an
* expression for an exact match of the string.
*
* @param {Array} ignoredIdentifier - A string to parse into a regular expression that describes
* the name of an identifier to ignore property assignments for.
* @returns {regexp} The resulting regular expression object.
*/
function parseIgnoredPropertyAssignment(ignoredIdentifier) {
const matches = /^\/(.*)\/(.*)$/.exec(ignoredIdentifier);

return matches ? new RegExp(matches[1], matches[2]) : new RegExp(`^${ignoredIdentifier}$`);
}

ignoredPropertyAssignments = ignoredPropertyAssignments.map(parseIgnoredPropertyAssignment);

/**
* Checks whether or not the reference modifies properties of its variable.
Expand Down Expand Up @@ -125,6 +143,18 @@ module.exports = {
return false;
}

/**
* Tests that a string matches any of the ignored property assignment regular expressions.
*
* @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 ignoredPropertyAssignments.some(ignoredAssignment => ignoredAssignment.test(identifierName));
}

/**
* Reports a reference if is non initializer and writable.
* @param {Reference} reference - A reference to check.
Expand All @@ -146,7 +176,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 } });
}
}
Expand Down
17 changes: 17 additions & 0 deletions tests/lib/rules/no-param-reassign.js
Expand Up @@ -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 }],
Expand Down Expand Up @@ -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 }],
Expand Down

0 comments on commit 359c3bd

Please sign in to comment.