Skip to content

Commit

Permalink
Update: Wildcard support for the new-cap rule (fixes eslint#5187)
Browse files Browse the repository at this point in the history
  • Loading branch information
diki committed Mar 7, 2016
1 parent ef341be commit 33d2c2b
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 2 deletions.
20 changes: 20 additions & 0 deletions docs/rules/new-cap.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ var colleague = foo.bar.Person();
var colleague = foo.Person();
```

```js
/*eslint new-cap: [2, {"capIsNewWildcardExceptions": ["foo"]}]*/
var colleague = foo.Person();
var colleague = foo.Animal();
```

```js
/*eslint new-cap: [2, {"newIsCapWildcardExceptions": ["foo"]}]*/
var colleague = foo.person();
var colleague = foo.animal();
```

## Options

By default both `newIsCap` and `capIsNew` options are set to `true`.
Expand Down Expand Up @@ -76,6 +88,14 @@ Array of uppercase-starting function names that are permitted to be used without

If provided, it must be an `Array`. The default values will continue to be excluded when `capIsNewExceptions` is provided.

### `newIsCapWildcardExceptions`

Array of lowercase library names where all child methods belong to are permitted to be used with `new` operator

### `capIsNewWildcardExceptions`

Array of uppercase-starting library names where all child methods belong to are permitted to be used without the `new` operator

### `properties`

By default, this rule will check properties such as `object.Property` using the other options (default value is `true`). When set to `false`, this rule will not check properties so `new object.property()` is valid even when `newIsCap` is `true`.
Expand Down
44 changes: 42 additions & 2 deletions lib/rules/new-cap.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ module.exports = function(context) {

var capIsNewExceptions = calculateCapIsNewExceptions(config);

var newIsCapWildcardExceptions = checkArray(config, "newIsCapWildcardExceptions", []).reduce(invert, {});

var capIsNewWildcardExceptions = checkArray(config, "capIsNewWildcardExceptions", []).reduce(invert, {});

var listeners = {};

//--------------------------------------------------------------------------
Expand Down Expand Up @@ -159,6 +163,28 @@ module.exports = function(context) {
return skipProperties && node.callee.type === "MemberExpression";
}

/**
* Check if capitalization allowed for a CallExpression member methods e.g Immutable.Map
* @param {Object} allowedMap Object mapping calleName to a Boolean
* @param {ASTNode} node CallExpression node
* @returns {Boolean} Returns true if the callee.calleName may be capitalized
*/
function isCapAllowedByWildcard(allowedMap, node) {
// https://github.com/eslint/eslint/issues/5187
var calleeName = context.getSource(node.callee);
if (calleeName.indexOf(".") > 0) {
var namespaceName = calleeName.split(".")[0];
if (allowedMap[namespaceName]) {
return true;
}
} else {
if (allowedMap[calleeName]) {
return true;
}
}
return skipProperties && node.callee.type === "MemberExpression";
}

/**
* Reports the given message for the given node. The location will be the start of the property or the callee.
* @param {ASTNode} node CallExpression or NewExpression node.
Expand Down Expand Up @@ -186,7 +212,8 @@ module.exports = function(context) {
if (constructorName) {
var capitalization = getCap(constructorName);
var isAllowed = capitalization !== "lower" || isCapAllowed(newIsCapExceptions, node, constructorName);
if (!isAllowed) {
var isAllowedByWildcard = capitalization !== "lower" || isCapAllowedByWildcard(newIsCapWildcardExceptions, node);
if (!isAllowed && !isAllowedByWildcard) {
report(node, "A constructor name should not start with a lowercase letter.");
}
}
Expand All @@ -200,7 +227,8 @@ module.exports = function(context) {
if (calleeName) {
var capitalization = getCap(calleeName);
var isAllowed = capitalization !== "upper" || isCapAllowed(capIsNewExceptions, node, calleeName);
if (!isAllowed) {
var isAllowedByWildcard = capitalization !== "upper" || isCapAllowedByWildcard(capIsNewWildcardExceptions, node);
if (!isAllowed && !isAllowedByWildcard) {
report(node, "A function with a name starting with an uppercase letter should only be used as a constructor.");
}
}
Expand Down Expand Up @@ -232,6 +260,18 @@ module.exports.schema = [
"type": "string"
}
},
"newIsCapWildcardExceptions": {
"type": "array",
"items": {
"type": "string"
}
},
"capIsNewWildcardExceptions": {
"type": "array",
"items": {
"type": "string"
}
},
"properties": {
"type": "boolean"
}
Expand Down
3 changes: 3 additions & 0 deletions tests/lib/rules/new-cap.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ ruleTester.run("new-cap", rule, {
"var o = { 1: function() {} }; o[1]();",
"var o = { 1: function() {} }; new o[1]();",
{ code: "var x = Foo(42);", options: [{ capIsNew: true, capIsNewExceptions: ["Foo"] }] },
{ code: "var x = Foo.Bar(42);", options: [{ capIsNew: true, capIsNewWildcardExceptions: ["Foo"] }] },
{ code: "var x = new foo(42);", options: [{ newIsCap: true, newIsCapExceptions: ["foo"] }] },
{ code: "var x = new foo(42);", options: [{ newIsCap: true, newIsCapWildcardExceptions: ["foo"] }] },
{ code: "var x = new foo.bar(42);", options: [{ newIsCap: true, newIsCapWildcardExceptions: ["foo"] }] },
{ code: "var x = Object(42);", options: [{ capIsNewExceptions: ["Foo"] }] },

{ code: "var x = Foo.Bar(42);", options: [{ capIsNewExceptions: ["Bar"] }] },
Expand Down

0 comments on commit 33d2c2b

Please sign in to comment.