Skip to content

Commit 9f8c0a4

Browse files
committed
Merge pull request #49 from zxqfox/feature/tag-checking-presets
Feature/tag checking presets
2 parents 588a0b4 + 1bcdd0e commit 9f8c0a4

File tree

12 files changed

+526
-3
lines changed

12 files changed

+526
-3
lines changed

README.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,78 @@ To use plugin you should add these lines to configuration file `.jscsrc`:
6262

6363
## Rules
6464

65+
### checkAnnotations
66+
67+
Ensures tag names are valid
68+
69+
There are 3 presets for `Closure Compiler`, `JSDoc3` and `JSDuck5`.
70+
71+
By default it allows any tag of mixed set. You can pass `Object` to select preset with `preset` field and add custom tags with `extra` field.
72+
73+
Type: `Boolean` or `String` or `{"preset": String, "extra": Object}` (`extra` field should contains tags in keys and boolean in values. false means no value possible)
74+
75+
Values: `true`, `"closurecompiler"`, `"jsdoc3"`, `"jsduck5"`, `Object`
76+
77+
Context: `file`
78+
79+
Tags: `*`
80+
81+
#### Example
82+
83+
```js
84+
"checkAnnotations": true
85+
```
86+
87+
##### Valid
88+
89+
```js
90+
/**
91+
* @chainable
92+
* @param {string} message
93+
* @return {string}
94+
*/
95+
function _f() {}
96+
```
97+
98+
##### Invalid
99+
100+
```js
101+
/**
102+
* @pororo
103+
* @lalala
104+
*/
105+
function _f() {}
106+
```
107+
108+
#### Example 2
109+
110+
```js
111+
"checkAnnotations": {
112+
"preset": "jsdoc3",
113+
"extra": {
114+
"boomer": false
115+
}
116+
}
117+
```
118+
119+
##### Valid
120+
121+
```js
122+
/**
123+
* @boomer
124+
* @argument {String}
125+
*/
126+
function _f() {}
127+
```
128+
129+
##### Invalid
130+
131+
```js
132+
/** @still-invalid */
133+
```
134+
135+
####
136+
65137
### checkParamNames
66138

67139
Ensures param names in jsdoc and in function declaration are equal

lib/jsdoc.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ module.exports = {
3535
* @constructor
3636
*/
3737
function DocComment(value, loc) {
38+
assert.equal(typeof value, 'string', '');
39+
assert(typeof loc === 'object' && typeof loc.start === 'object' &&
40+
typeof loc.start.line === 'number' && typeof loc.start.column === 'number' &&
41+
loc.start.line > 0 && loc.start.column >= 0,
42+
'Location object should contains start field with valid line and column numbers');
43+
3844
// parse comments
3945
var _parsed = _parseComment(value) || {};
4046

lib/rules/validate-jsdoc.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ module.exports.prototype = {
3030
if (v.options) {
3131
validators.checkOptions(v, options);
3232
}
33+
// configure
34+
if (v.configure) {
35+
v.configure(options);
36+
}
3337
// index rules by tags and scopes
3438
(v.scopes || ['']).forEach(function(scope) {
3539
if (!v.tags) {
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
var assert = require('assert');
2+
3+
var availablePresets = require('../../tags');
4+
var jsdoc = require('../../jsdoc');
5+
6+
module.exports = validateAnnotations;
7+
module.exports.scopes = ['file'];
8+
module.exports.options = {
9+
checkAnnotations: true
10+
};
11+
12+
var tags;
13+
14+
validateAnnotations.configure = function(options) {
15+
var o = options.checkAnnotations;
16+
17+
assert(o === true || typeof o === 'string' || typeof o === 'object',
18+
'jsDoc.checkAnnotation rule was not configured properly');
19+
20+
if (typeof o === 'string') {
21+
o = {preset: o};
22+
} else if (typeof o === 'object') {
23+
var oKeys = Object.keys(o);
24+
oKeys.forEach(function(key) {
25+
if (key === 'preset') {
26+
assert(typeof o.preset === 'string', 'jsDoc.checkAnnotation.preset should be preset name');
27+
} else if (key === 'extra') {
28+
assert(typeof o[key] === 'object', 'jsDoc.checkAnnotation.preset should be `tag: fulfill` map');
29+
} else {
30+
throw new Error('jsDoc.checkAnnotation.' + key + ' is unsupported field');
31+
}
32+
});
33+
}
34+
35+
tags = Object.create ? Object.create(null) : {};
36+
37+
if (o === true) {
38+
Object.keys(availablePresets).forEach(function(preset) {
39+
var presetTags = availablePresets[preset];
40+
Object.keys(presetTags).forEach(function(tag) {
41+
tags[tag] = tags[tag] || presetTags[tag];
42+
});
43+
});
44+
45+
} else if (typeof o === 'object') {
46+
if (o.preset) {
47+
assert(availablePresets[o.preset], 'Unknown tag preset ' + o.preset);
48+
Object.keys(availablePresets[o.preset]).forEach(function(tag) {
49+
tags[tag] = tags[tag] || availablePresets[o.preset][tag];
50+
});
51+
}
52+
if (o.extra) {
53+
Object.keys(o.extra).forEach(function(tag) {
54+
if (o.extra[tag] === null) {
55+
delete tags[tag];
56+
} else {
57+
tags[tag] = o.extra[tag];
58+
}
59+
});
60+
}
61+
}
62+
};
63+
64+
/**
65+
* validator for annotations
66+
* @param {JSCS.JSFile} file
67+
* @param {JSCS.Errors} errors
68+
*/
69+
function validateAnnotations(file, errors) {
70+
var checkFulfill = true; //this._options.checkAnnotations.checkFulfill;
71+
var comments = file.getComments();
72+
comments.forEach(function(commentNode) {
73+
if (commentNode.type !== 'Block' || commentNode.value[0] !== '*') {
74+
return;
75+
}
76+
77+
// trying to create DocComment object
78+
var node = jsdoc.createDocCommentByCommentNode(commentNode);
79+
if (!node.valid) {
80+
return;
81+
}
82+
83+
node.iterate(function(tag) {
84+
if (!(tag.id in tags)) {
85+
errors.add('unavailable tag ' + tag.id, tag.loc);
86+
}
87+
if (!checkFulfill) {
88+
return;
89+
}
90+
91+
// checking tag fullfill
92+
var isFilled = tag.name || tag.type || tag.description;
93+
if (tags[tag.id] === false && isFilled) {
94+
errors.add('unexpected data in tag ' + tag.id, tag.loc);
95+
} else if (tags[tag.id] === true && !isFilled) {
96+
errors.add('incomplete data in tag ' + tag.id, tag.loc);
97+
}
98+
});
99+
});
100+
}

lib/rules/validate-jsdoc/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ var validatorsByName = module.exports = {
1212
requireReturnTypes: require('./require-return-types'),
1313
checkRedundantReturns: require('./check-redundant-returns'),
1414

15-
//returns: require('./returns'),
15+
checkAnnotations: require('./check-annotations'),
16+
1617
checkRedundantAccess: require('./check-redundant-access'),
1718
enforceExistence: require('./enforce-existence'),
1819
leadingUnderscoreAccess: require('./leading-underscore-access')

lib/tags/closurecompiler.json

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{
2+
"augments": true,
3+
"author": true,
4+
"argument": true,
5+
"borrows": true,
6+
"class": true,
7+
"const": false,
8+
"constant": false,
9+
"constructor": false,
10+
"constructs": false,
11+
"default": true,
12+
"define": true,
13+
"deprecated": true,
14+
"description": true,
15+
"dict": false,
16+
"enum": true,
17+
"event": true,
18+
"example": true,
19+
"export": false,
20+
"extends": true,
21+
"field": false,
22+
"fileOverview": true,
23+
"final": false,
24+
"function": false,
25+
"ignore": false,
26+
"implements": true,
27+
"inheritDoc": false,
28+
"inner": false,
29+
"interface": false,
30+
"lends": true,
31+
"license": true,
32+
"link": true,
33+
"memberOf": true,
34+
"name": true,
35+
"namespace": true,
36+
"nosideeffects": false,
37+
"override": false,
38+
"package": false,
39+
"param": true,
40+
"preserve": true,
41+
"private": false,
42+
"property": true,
43+
"public": false,
44+
"requires": true,
45+
"returns": true,
46+
"see": true,
47+
"since": true,
48+
"static": false,
49+
"struct": false,
50+
"template": true,
51+
"this": true,
52+
"throws": true,
53+
"type": true,
54+
"typedef": true,
55+
"version": true
56+
}

lib/tags/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
jsdoc3: require('./jsdoc3'),
3+
jsduck5: require('./jsduck5'),
4+
closurecompiler: require('./closurecompiler')
5+
};

lib/tags/jsdoc3.json

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
{
2+
"abstract": false,
3+
"access": true,
4+
"alias": true,
5+
"arg": true,
6+
"argument": true,
7+
"augments": true,
8+
"author": true,
9+
"borrows": true,
10+
"callback": true,
11+
"class": true,
12+
"classdesc": true,
13+
"const": true,
14+
"constant": true,
15+
"constructor": true,
16+
"constructs": true,
17+
"copyright": true,
18+
"default": true,
19+
"defaultvalue": true,
20+
"deprecated": true,
21+
"desc": true,
22+
"description": true,
23+
"emits": true,
24+
"enum": true,
25+
"event": true,
26+
"example": true,
27+
"exception": true,
28+
"exports": true,
29+
"extends": true,
30+
"external": true,
31+
"fileoverview": true,
32+
"file": true,
33+
"fires": true,
34+
"func": true,
35+
"function": true,
36+
"global": false,
37+
"host": true,
38+
"ignore": false,
39+
"inner": false,
40+
"instance": false,
41+
"kind": true,
42+
"lends": true,
43+
"license": true,
44+
"member": true,
45+
"memberof": true,
46+
"method": true,
47+
"mixes": true,
48+
"mixin": true,
49+
"module": true,
50+
"name": true,
51+
"namespace": true,
52+
"overview": true,
53+
"param": true,
54+
"private": false,
55+
"prop": true,
56+
"property": true,
57+
"protected": false,
58+
"public": false,
59+
"readonly": false,
60+
"requires": true,
61+
"return": true,
62+
"returns": true,
63+
"see": true,
64+
"since": true,
65+
"static": false,
66+
"summary": true,
67+
"this": true,
68+
"throws": true,
69+
"todo": true,
70+
"tutorial": true,
71+
"type": true,
72+
"typedef": true,
73+
"var": true,
74+
"variation": true,
75+
"version": true,
76+
"virtual": true
77+
}

0 commit comments

Comments
 (0)