Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New: no-restricted-properties rule (fixes #3218) #7017

Merged
merged 7 commits into from Sep 2, 2016
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions conf/eslint.json
Expand Up @@ -88,6 +88,7 @@
"no-restricted-globals": "off",
"no-restricted-imports": "off",
"no-restricted-modules": "off",
"no-restricted-properties": "off",
"no-restricted-syntax": "off",
"no-return-assign": "off",
"no-script-url": "off",
Expand Down
74 changes: 74 additions & 0 deletions docs/rules/no-restricted-properties.md
@@ -0,0 +1,74 @@
# disallow certain object properties (no-restricted-properties)

Certain properties on objects may be disallowed in a codebase. This is useful for deprecating an API or restricting usage of a module's methods. For example, you may want to disallow using `describe.only` when using Mocha or telling people to use `Object.assign` instead of `_.extend`.


## Rule Details

This rule looks for accessing a given property key on a given object name, either when reading the property's value or invoking it as a function. You may specify an optional message to indicate an alternative API or a reason for the restriction.

### Options

This rule takes a list of objects, where the object name and property names are specified:

```json
{
"rules": {
"no-restricted-properties": [2, [{
"object": "disallowedObjectName",
"property": "disallowedPropertyName"
}]]
}
}
```

Multiple object/property values can be disallowed, and you can specify an optional message:

```json
{
"rules": {
"no-restricted-properties": [2, [{
"object": "disallowedObjectName",
"property": "disallowedPropertyName"
}, {
"object": "disallowedObjectName",
"property": "anotherDisallowedPropertyName",
"message": "Please use allowedObjectName.allowedPropertyName."
}]]
}
}
```

Examples of **incorrect** code for this rule:

```js
/* eslint no-restricted-properties: [2, {
"object": "disallowedObjectName",
"property": "disallowedPropertyName"
}] */

var example = disallowedObjectName.disallowedPropertyName; /*error Disallowed object property: disallowedObjectName.disallowedPropertyName.*/

disallowedObjectName.disallowedPropertyName(); /*error Disallowed object property: disallowedObjectName.disallowedPropertyName.*/
```

Examples of **correct** code for this rule:

```js
/* eslint no-restricted-properties: [2, {
"object": "disallowedObjectName",
"property": "disallowedPropertyName"
}] */

var example = disallowedObjectName.somePropertyName;

allowedObjectName.disallowedPropertyName();
```

## When Not To Use It

If you don't have any object/property combinations to restrict, you should not use this rule.

## Related Rules

* [no-restricted-syntax](no-restricted-syntax.md)
86 changes: 86 additions & 0 deletions lib/rules/no-restricted-properties.js
@@ -0,0 +1,86 @@
/**
* @fileoverview Rule to disallow certain object properties
* @author Will Klein & Eli White
*/

"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
meta: {
docs: {
description: "disallow certain properties on certain objects",
category: "Node.js and CommonJS",
recommended: false
},

schema: {
type: "array",
items: {
type: "object",
properties: {
object: {
type: "string"
},
property: {
type: "string"
},
message: {
type: "string"
}
},
additionalProperties: false,
required: [
"object",
"property"
]
},
uniqueItems: true
}
},

create(context) {
const restrictedCalls = context.options;

if (restrictedCalls.length === 0) {
return {};
}

const restrictedProperties = restrictedCalls.reduce(function(restrictions, option) {
const objectName = option.object;
const propertyName = option.property;

if (!restrictions.has(objectName)) {
restrictions.set(objectName, new Map());
}

restrictions.get(objectName).set(propertyName, {
message: option.message
});

return restrictions;
}, new Map());

return {
MemberExpression(node) {
const objectName = node.object && node.object.name;
const propertyName = node.property && node.property.name;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only going to catch foo.bar notation. Should we also catch foo['bar'] as well?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @mysticatea had recently added a "get simple property name" method
to ast-utils, might want to hunt that down.

On Sep 2, 2016 8:56 AM, "Ilya Volodin" notifications@github.com wrote:

In lib/rules/no-restricted-properties.js
#7017 (comment):

  •        if (!restrictions.has(objectName)) {
    
  •            restrictions.set(objectName, new Map());
    
  •        }
    
  •        restrictions.get(objectName).set(propertyName, {
    
  •            message: option.message
    
  •        });
    
  •        return restrictions;
    
  •    }, new Map());
    
  •    return {
    
  •        MemberExpression(node) {
    
  •            const objectName = node.object && node.object.name;
    
  •            const propertyName = node.property && node.property.name;
    

This is only going to catch foo.bar notation. Should we also catch
foo['bar'] as well?


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/eslint/eslint/pull/7017/files/576e4cc27d1d2f01961be20a41fa94df19a75b19#r77349937,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AARWekx4AnK_LRONX9DtdWUi0el5Plroks5qmCsngaJpZM4JwEv3
.

const matchedObject = restrictedProperties.get(objectName);
const matchedObjectProperty = matchedObject && matchedObject.get(propertyName);

if (matchedObjectProperty) {
const message = matchedObjectProperty.message ? " " + matchedObjectProperty.message : "";

context.report(node, "'{{objectName}}.{{propertyName}}' is restricted from being used.{{message}}", {
objectName,
propertyName,
message
});
}
}
};
}
};
115 changes: 115 additions & 0 deletions tests/lib/rules/no-restricted-properties.js
@@ -0,0 +1,115 @@
/**
* @fileoverview Tests for no-restricted-properties rule.
* @author Will Klein & Eli White
*/

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const rule = require("../../../lib/rules/no-restricted-properties"),
RuleTester = require("../../../lib/testers/rule-tester");

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

const ruleTester = new RuleTester();

ruleTester.run("no-restricted-properties", rule, {
valid: [
{
code: "someObject.someProperty",
options: [{
object: "someObject",
property: "disallowedProperty"
}]
}, {
code: "anotherObject.disallowedProperty",
options: [{
object: "someObject",
property: "disallowedProperty"
}]
}, {
code: "someObject.someProperty()",
options: [{
object: "someObject",
property: "disallowedProperty"
}]
}, {
code: "anotherObject.disallowedProperty()",
options: [{
object: "someObject",
property: "disallowedProperty"
}]
}, {
code: "anotherObject.disallowedProperty()",
options: [{
object: "someObject",
property: "disallowedProperty",
message: "Please use someObject.allowedProperty instead."
}]
}, {
code: "obj.toString",
options: [{
object: "obj",
property: "__proto__"
}]
}, {
code: "toString.toString",
options: [{
object: "obj",
property: "foo"
}]
}, {
code: "obj.toString",
options: [{
object: "obj",
property: "foo"
}]
}
],

invalid: [
{
code: "someObject.disallowedProperty",
options: [{
object: "someObject",
property: "disallowedProperty"
}],
errors: [{
message: "'someObject.disallowedProperty' is restricted from being used.",
type: "MemberExpression"
}]
}, {
code: "someObject.disallowedProperty",
options: [{
object: "someObject",
property: "disallowedProperty",
message: "Please use someObject.allowedProperty instead."
}],
errors: [{
message: "'someObject.disallowedProperty' is restricted from being used. Please use someObject.allowedProperty instead.",
type: "MemberExpression"
}]
}, {
code: "someObject.disallowedProperty; anotherObject.anotherDisallowedProperty()",
options: [{
object: "someObject",
property: "disallowedProperty"
}, {
object: "anotherObject",
property: "anotherDisallowedProperty"
}],
errors: [{
message: "'someObject.disallowedProperty' is restricted from being used.",
type: "MemberExpression"
}, {
message: "'anotherObject.anotherDisallowedProperty' is restricted from being used.",
type: "MemberExpression"
}]
}
]
});