Skip to content

Commit 9acd838

Browse files
author
Alexej Yaroshevich
committed
new rules: enforce, trailingUndersoreAccess, strict
some tests refactoring, new tests, readme update closes #4, closes #5
1 parent ecf4c6f commit 9acd838

12 files changed

Lines changed: 505 additions & 22 deletions

File tree

README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ Values:
5151
- "requireReturnTypes" ensures returns in jsdoc contains type
5252
- "checkRedundantReturns" reports redundant returns in jsdoc
5353
- "checkTypes" reports invalid types in jsdoc
54+
- "enforce" reports empty and jsdoc
55+
- "strict" reports for invalid jsdoc definitions
56+
- "leadingUnderscoreAccess" reports not set @access for `_underscored` function names
57+
- "trailingUnderscoreAccess" reports not set @access for `underscored`_ function names
5458

5559
#### Example
5660

@@ -62,7 +66,10 @@ Values:
6266
"checkReturnTypes": true,
6367
"checkRedundantReturns": true,
6468
"requireReturnTypes": true,
65-
"checkTypes": true
69+
"checkTypes": true,
70+
"strict": true,
71+
"enforce": true,
72+
"leadingUnderscoreAccess": 'private'
6673
}
6774
```
6875

@@ -72,12 +79,13 @@ Values:
7279
/**
7380
* Adds style error to the list
7481
*
82+
* @private
7583
* @param {String} message
7684
* @param {Number|Object} line
7785
* @param {Number} [column]
7886
* @returns {String[]}
7987
*/
80-
add: function(message, line, column) {
88+
_add: function(message, line, column) {
8189
return ['foo', 'bar'];
8290
}
8391
```
@@ -88,12 +96,13 @@ add: function(message, line, column) {
8896
/**
8997
* Adds style error to the list
9098
*
99+
* @protected
91100
* @param {String} message
92101
* @param {Number,Object} line
93102
* @param {Number} [column]
94103
* @returns {String}
95104
*/
96-
add: function() {
105+
_add: function() {
97106
}
98107
```
99108

lib/jsdoc-helpers.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,30 @@ module.exports = {
1010
};
1111

1212
function jsDocParseComments (comments) {
13-
1413
/**
1514
* metaobject for parsing jsdoc comments string
1615
*/
1716
return {
18-
node: getJsDocForNode,
17+
forNode: getJsDocForNode,
1918
parse: parseJsDoc
2019
};
2120

2221
function getJsDocForNode (node) {
2322
var jsdoc = getJsDocForLine(node.loc.start.line);
2423
if (jsdoc) {
25-
jsdoc.data = parseJsDoc(jsdoc.value);
26-
jsdoc.forEachTag = function (fn) {
27-
if (!this.data.tags || !this.data.tags.length) return;
28-
this.data.tags.forEach(fn);
29-
};
24+
jsdoc.data = parseJsDoc(jsdoc.value) || {};
25+
jsdoc.invalid = !jsdoc.data.line;
26+
jsdoc.data.tags = jsdoc.data.tags || [];
27+
jsdoc.forEachTag = jsDocForEachTag;
3028
}
3129
return jsdoc;
3230
}
3331

32+
function jsDocForEachTag (fn) {
33+
if (!this.data.tags || !this.data.tags.length) return;
34+
this.data.tags.forEach(fn);
35+
}
36+
3437
function getJsDocForLine (line) {
3538
line--;
3639
for (var i = 0, l = comments.length; i < l; i++) {

lib/rules/validate-jsdoc.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@ module.exports = function() {};
77

88
module.exports.prototype = {
99

10-
configure: function(options) {
10+
configure: function (options) {
1111
assert(typeof options === 'object', 'jsDoc option requires object value');
1212
this._options = options;
1313
this._optionsList = Object.keys(options);
1414
},
1515

16-
getOptionName: function() {
16+
getOptionName: function () {
1717
return 'jsDoc';
1818
},
1919

20-
check: function(file, errors) {
20+
check: function (file, errors) {
2121
var validators = this.loadValidators();
2222

2323
// skip if there is nothing to check
@@ -32,14 +32,13 @@ module.exports.prototype = {
3232
'FunctionDeclaration',
3333
'FunctionExpression'
3434

35-
], function(node) {
36-
node.jsDoc = jsDocs.node(node);
37-
35+
], function (node) {
36+
node.jsDoc = jsDocs.forNode(node);
3837
for (var j = 0, k = validators.length; j < k; j += 1) {
3938
validators[j].call(that, node, addError);
4039
}
4140

42-
function addError (text, loc) {
41+
function addError(text, loc) {
4342
loc = loc || {};
4443
loc.line = loc.hasOwnProperty('line') ? loc.line : (node.jsDoc.loc.start.line);
4544
loc.column = loc.hasOwnProperty('column') ? loc.column : 0; //node.jsDoc[i].indexOf('@');
@@ -49,7 +48,7 @@ module.exports.prototype = {
4948

5049
},
5150

52-
loadValidators: function() {
51+
loadValidators: function () {
5352
var passedOptions = this._optionsList;
5453
var validators = [];
5554
if (!passedOptions) {
@@ -58,12 +57,16 @@ module.exports.prototype = {
5857

5958
Object.keys(validatorsByName).forEach(function (name) {
6059
var v = validatorsByName[name];
60+
61+
// skip unused
6162
if (!v.coveredOptions) {
6263
return;
6364
}
65+
66+
// store used
6467
for (var i = 0, l = v.coveredOptions.length; i < l; i += 1) {
6568
if (passedOptions.indexOf(v.coveredOptions[i]) !== -1) {
66-
validators.push(v.bind(this));
69+
validators.push(v);
6770
return;
6871
}
6972
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
//var jsDocHelpers = require('../../jsdoc-helpers');
3+
4+
module.exports = validateEnforce;
5+
module.exports.coveredOptions = [
6+
'strict',
7+
'enforce',
8+
'leadingUnderscoreAccess',
9+
'trailingUnderscoreAccess'
10+
];
11+
12+
/**
13+
* validator for jsdoc data existance
14+
* @param {(FunctionDeclaration|FunctionExpression)} node
15+
* @param {Function} err
16+
*/
17+
function validateEnforce(node, err) {
18+
var options = this._options;
19+
20+
if (!node.jsDoc && options) {
21+
err('jsdoc definition required', node.loc.start);
22+
}
23+
}

lib/rules/validate-jsdoc/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
module.exports = {
2+
enforce: require('./enforce'),
23
param: require('./param'),
3-
returns: require('./returns')
4+
returns: require('./returns'),
5+
trailingUnderscores: require('./trailing-underscores')
46
};
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
2+
module.exports = validateTrailingUnderscores;
3+
module.exports.coveredOptions = [
4+
'strict',
5+
'leadingUnderscoreAccess',
6+
'trailingUnderscoreAccess'
7+
];
8+
9+
/**
10+
* validator for jsdoc data existance
11+
* @param {(FunctionDeclaration|FunctionExpression)} node
12+
* @param {Function} err
13+
*/
14+
function validateTrailingUnderscores(node, err) {
15+
var options = this._options;
16+
var __strict = options.strict;
17+
if (!node.jsDoc) {
18+
return;
19+
}
20+
21+
// underscores
22+
var leading = options.leadingUnderscoreAccess;
23+
var trailing = options.trailingUnderscoreAccess;
24+
25+
var name;
26+
switch (node.parentNode.type) {
27+
case 'VariableDeclarator':
28+
name = node.parentNode.id.name;
29+
break;
30+
case 'Property':
31+
name = node.parentNode.key.name;
32+
break;
33+
default: // try to use func name itself (if not anonymous)
34+
name = (node.id||{}).name;
35+
break;
36+
}
37+
38+
// skip if anonymous or something
39+
if (!name) {
40+
return;
41+
}
42+
43+
// skip names without underscores at begin/end
44+
var nameHasLeading = name[0] === '_';
45+
var nameHasTrailing = name.substr(-1) === '_';
46+
if (!nameHasLeading && !nameHasTrailing) {
47+
return;
48+
}
49+
50+
if (leading || trailing || __strict) {
51+
var access;
52+
node.jsDoc.data.tags.forEach(function (tag) {
53+
if (['private', 'protected', 'public', 'access'].indexOf(tag.tag) !== -1) {
54+
if (access && __strict) {
55+
err('Multiple access definition');
56+
}
57+
if (tag.tag === 'access' && !tag.name && __strict) {
58+
err('Invalid access definition');
59+
}
60+
access = tag.tag === 'access' ? tag.name : tag.tag || 'public';
61+
}
62+
});
63+
64+
if ((leading && nameHasLeading && (!access || [true, access].indexOf(leading) === -1)) ||
65+
(trailing && nameHasTrailing && (!access || [true, access].indexOf(trailing) === -1))) {
66+
err('Method access doesn\'t match');
67+
}
68+
}
69+
}

test/init.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
var Checker = require('jscs/lib/checker');
12
var parse = require('comment-parser');
3+
var expect = require('chai').expect;
24

35
global.parse = parse;
46
global.fnBody = fnBody;
7+
global.checker = rulesChecker;
58

69
function fnBody(func) {
710
var str = func.toString();
@@ -28,3 +31,46 @@ function fnBody(func) {
2831

2932
return out;
3033
}
34+
35+
function rulesChecker(opts) {
36+
var checker;
37+
38+
beforeEach(function () {
39+
checker = new Checker();
40+
checker.registerDefaultRules();
41+
if (opts) {
42+
checker.configure(opts);
43+
}
44+
});
45+
46+
return {
47+
cases: function (items) {
48+
items = items || [];
49+
items.forEach(function (test) {
50+
51+
(test.skip? it.skip : it)(test.it, function () {
52+
if (test.rules) {
53+
checker.configure({ jsDoc: test.rules });
54+
}
55+
56+
var body = test.code.call ? fnBody(test.code) : test.code;
57+
var checked = checker.checkString(body);
58+
var errors = checked.getErrorList();
59+
if (errors.length && errors[0].rule === 'parseError') {
60+
console.error(errors[0]);
61+
throw new Error(errors[0].message);
62+
}
63+
64+
if (!test.hasOwnProperty('errors') || (typeof test.errors === 'number')) {
65+
expect(checked.getErrorCount())
66+
.to.eq(test.errors || 0);
67+
} else {
68+
expect(errors)
69+
.to.deep.equal(test.errors);
70+
}
71+
});
72+
73+
});
74+
}
75+
};
76+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
describe('rules/validate-jsdoc enforce', function () {
2+
var cases = global.checker({
3+
additionalRules: ['lib/rules/validate-jsdoc.js']
4+
}).cases;
5+
6+
describe('enforce-existance', function () {
7+
8+
cases([
9+
/* jshint ignore:start */
10+
{
11+
it: 'should report jsdoc absence',
12+
rules: { enforce: true },
13+
errors: 1,
14+
code: function () {
15+
var x = 1;
16+
function funcName(p) {}
17+
}
18+
}, {
19+
it: 'still should report jsdoc absence',
20+
rules: { enforce: {} },
21+
errors: 1,
22+
code: function () {
23+
function _funcName(p) {}
24+
}
25+
}, {
26+
it: 'should not report jsdoc absence',
27+
rules: { enforce: true },
28+
code: function () {
29+
/**
30+
* yolo
31+
*/
32+
function funcName(p) {
33+
}
34+
35+
/**
36+
* @param {Object} p
37+
*/
38+
function funcName(p) {
39+
}
40+
}
41+
}
42+
/* jshint ignore:end */
43+
]);
44+
45+
});
46+
});

0 commit comments

Comments
 (0)