Skip to content
This repository has been archived by the owner on Mar 23, 2024. It is now read-only.

Commit

Permalink
disallowDanglingUnderscores: Support an array of additional exceptions
Browse files Browse the repository at this point in the history
Closes gh-725
Fixes #723
  • Loading branch information
hzoo authored and Joel Kemp committed Oct 29, 2014
1 parent d2206fd commit 7773546
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 42 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1711,33 +1711,39 @@ var x = {'a': 1};

### disallowDanglingUnderscores

Disallows identifiers that start or end in `_`, except for some popular exceptions:
Disallows identifiers that start or end in `_`. Some popular identifiers that are automatically excepted:

- `__proto__` (javascript)
- `_` (underscore.js)
- `__filename` (node.js global)
- `__dirname` (node.js global)
- `super_` (node.js, used by [`util.inherits`](http://nodejs.org/docs/latest/api/util.html#util_util_inherits_constructor_superconstructor))

Type: `Boolean`
Type: `Boolean` or `Object`

Values: `true`
Values:
- `true`
- `Object`:
- `allExcept`: array of quoted identifiers

JSHint: [`nomen`](http://www.jshint.com/docs/options/#nomen)

#### Example

```js
"disallowDanglingUnderscores": true
"disallowDanglingUnderscores": { allExcept: ["_exception"] }
```

##### Valid

```js
var x = 1;
var o = obj.__proto__;
var y = _.extend;
var z = __dirname;
var w = __filename;
var x_y = 1;
var v = _exception;
```

##### Invalid
Expand Down
43 changes: 31 additions & 12 deletions lib/rules/disallow-dangling-underscores.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,49 @@ module.exports = function() {};

module.exports.prototype = {

configure: function(disallowDanglingUnderscores) {
configure: function(identifiers) {
assert(
typeof disallowDanglingUnderscores === 'boolean',
'disallowDanglingUnderscores option requires boolean value'
identifiers === true ||
typeof identifiers === 'object',
this.getOptionName() + ' option requires the value `true` ' +
'or an object with String[] `allExcept` property'
);

// verify first item in `allExcept` property in object (if it's an object)
assert(
disallowDanglingUnderscores === true,
'disallowDanglingUnderscores option requires true value or should be removed'
typeof identifiers !== 'object' ||
Array.isArray(identifiers.allExcept) &&
typeof identifiers.allExcept[0] === 'string',
'Property `allExcept` in requireSpaceAfterLineComment should be an array of strings'
);

this._allowedIdentifiers = {
_: true,
__dirname: true,
__filename: true,
super_: true
};
var isTrue = identifiers === true;
var defaultIdentifiers = [
'__proto__',
'_',
'__dirname',
'__filename',
'super_'
];

if (isTrue) {
identifiers = defaultIdentifiers;
} else {
identifiers = (identifiers.allExcept).concat(defaultIdentifiers);
}

this._identifierIndex = {};
for (var i = 0, l = identifiers.length; i < l; i++) {
this._identifierIndex[identifiers[i]] = true;
}
},

getOptionName: function() {
return 'disallowDanglingUnderscores';
},

check: function(file, errors) {
var allowedIdentifiers = this._allowedIdentifiers;
var allowedIdentifiers = this._identifierIndex;

file.iterateTokensByType('Identifier', function(token) {
var value = token.value;
Expand Down
97 changes: 71 additions & 26 deletions test/rules/disallow-dangling-underscores.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,88 @@ describe('rules/disallow-dangling-underscores', function() {
beforeEach(function() {
checker = new Checker();
checker.registerDefaultRules();
checker.configure({ disallowDanglingUnderscores: true });
});

it('should report leading underscores', function() {
assert(checker.checkString('var _x = "x";').getErrorCount() === 1);
});
describe('option value true', function() {
beforeEach(function() {
checker.configure({ disallowDanglingUnderscores: true });
});

it('should report trailing underscores', function() {
assert(checker.checkString('var x_ = "x";').getErrorCount() === 1);
});
it('should report leading underscores', function() {
assert(checker.checkString('var _x = "x";').getErrorCount() === 1);
});

it('should report trailing underscores in member expressions', function() {
assert(checker.checkString('var x = this._privateField;').getErrorCount() === 1);
assert(checker.checkString('var x = instance._protectedField;').getErrorCount() === 1);
});
it('should report trailing underscores', function() {
assert(checker.checkString('var x_ = "x";').getErrorCount() === 1);
});

it('should report trailing underscores', function() {
assert(checker.checkString('var x_ = "x";').getErrorCount() === 1);
});
it('should report trailing underscores in member expressions', function() {
assert(checker.checkString('var x = this._privateField;').getErrorCount() === 1);
assert(checker.checkString('var x = instance._protectedField;').getErrorCount() === 1);
});

it('should not report underscore.js', function() {
assert(checker.checkString('var extend = _.extend;').isEmpty());
});
it('should report trailing underscores', function() {
assert(checker.checkString('var x_ = "x";').getErrorCount() === 1);
});

it('should not report node globals', function() {
assert(checker.checkString('var a = __dirname + __filename;').isEmpty());
});
it('should not report inner underscores', function() {
assert(checker.checkString('var x_y = "x";').isEmpty());
});

it('should not report the super constructor reference created by node\'s util.inherits', function() {
assert(checker.checkString('Inheritor.super_.call(this);').isEmpty());
it('should not report no underscores', function() {
assert(checker.checkString('var xy = "x";').isEmpty());
});
});

it('should not report inner underscores', function() {
assert(checker.checkString('var x_y = "x";').isEmpty());
describe('option value true: default exceptions', function() {
beforeEach(function() {
checker.configure({ disallowDanglingUnderscores: true });
});

it('should not report the prototype property', function() {
assert(checker.checkString('var proto = obj.__proto__;').isEmpty());
});

it('should not report underscore.js', function() {
assert(checker.checkString('var extend = _.extend;').isEmpty());
});

it('should not report node globals', function() {
assert(checker.checkString('var a = __dirname + __filename;').isEmpty());
});

it('should not report the super constructor reference created by node\'s util.inherits', function() {
assert(checker.checkString('Inheritor.super_.call(this);').isEmpty());
});
});

it('should not report no underscores', function() {
assert(checker.checkString('var xy = "x";').isEmpty());
describe('exceptions', function() {
beforeEach(function() {
checker.configure({ disallowDanglingUnderscores: { allExcept: ['_test', 'test_', '_test_', '__test'] } });
});

it('should not report default exceptions: underscore.js', function() {
assert(checker.checkString('var extend = _.extend;').isEmpty());
});

it('should not report _test', function() {
assert(checker.checkString('var a = _test;').isEmpty());
});

it('should not report test_', function() {
assert(checker.checkString('var a = test_;').isEmpty());
});

it('should not report _test_', function() {
assert(checker.checkString('var a = _test_;').isEmpty());
});

it('should not report test__', function() {
assert(checker.checkString('var a = __test;').isEmpty());
});

it('should report dangling underscore identifier that is not included in the array', function() {
assert(checker.checkString('var a = _notIncluded;').getErrorCount() === 1);
});
});
});

0 comments on commit 7773546

Please sign in to comment.