Skip to content

Commit 6b499e3

Browse files
author
Alexej Yaroshevich
committed
rework jsdoc rules validators, implement comment-parser
1 parent 4b2dfad commit 6b499e3

11 files changed

Lines changed: 124 additions & 61 deletions

File tree

.jshintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
"noarg" : true,
99
"unused" : true,
1010
"maxlen" : 120,
11-
"predef" : ["describe", "it", "beforeEach", "afterEach"]
11+
"mocha" : true
1212
}

lib/jsdoc-helpers.js

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
var doctrineParser = require('doctrine');
2+
var parse = require('comment-parser');
23

34
module.exports = {
45
parseComments : jsDocParseComments,
6+
tagValidator : jsDocTagValidator,
57

68
parse : jsDocParseType,
79
match : jsDocMatchType
@@ -14,23 +16,60 @@ function jsDocParseComments (comments) {
1416
*/
1517
return {
1618
node: getJsDocForNode,
17-
line: getJsDocForLine
19+
parse: parseJsDoc
1820
};
1921

2022
function getJsDocForNode (node) {
21-
return getJsDocForLine(node.loc.start.line);
23+
var jsdoc = getJsDocForLine(node.loc.start.line);
24+
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+
};
30+
}
31+
return jsdoc;
2232
}
2333

2434
function getJsDocForLine (line) {
2535
line--;
2636
for (var i = 0, l = comments.length; i < l; i++) {
27-
var comment = comments[i];
28-
if (comment.loc.end.line === line && comment.type === 'Block' && comment.value.charAt(0) === '*') {
29-
return comment;
37+
var commentNode = comments[i];
38+
if (commentNode.loc.end.line === line && commentNode.type === 'Block' &&
39+
commentNode.value.charAt(0) === '*') {
40+
return commentNode;
3041
}
3142
}
3243
return null;
3344
}
45+
46+
function parseJsDoc (comment) {
47+
return parse('/*' + comment + '*/', {
48+
lineNumbers: true,
49+
rawValue: true
50+
})[0] || [];
51+
}
52+
}
53+
54+
function jsDocTagValidator (validator) {
55+
return function (node, err) {
56+
var that = this;
57+
if (!node.jsDoc) {
58+
return;
59+
}
60+
node.jsDoc.forEachTag(function (tag, i) {
61+
// call line validator
62+
validator.call(that, node, tag, fixErrLocation(err, node, i));
63+
});
64+
};
65+
66+
function fixErrLocation (err, node, shift) {
67+
return function (text, loc) {
68+
loc = loc || {};
69+
loc.line = loc.hasOwnProperty('line') ? loc.line : (node.loc.start.line + shift);
70+
err(text, loc);
71+
};
72+
}
3473
}
3574

3675
/**

lib/rules/validate-jsdoc.js

Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
var assert = require('assert');
22

33
var jsDocHelpers = require('../jsdoc-helpers');
4+
var validatorsByName = require('./validate-jsdoc/index');
45

56
module.exports = function() {};
67

@@ -17,66 +18,46 @@ module.exports.prototype = {
1718
},
1819

1920
check: function(file, errors) {
20-
var lineValidators = this.loadLineValidators();
21+
var validators = this.loadValidators();
2122

2223
// skip if there is nothing to check
23-
if (!lineValidators.length) {
24+
if (!validators.length) {
2425
return;
2526
}
2627

2728
var jsDocs = jsDocHelpers.parseComments(file.getComments());
29+
var that = this;
2830

2931
file.iterateNodesByType([
3032
'FunctionDeclaration',
3133
'FunctionExpression'
34+
3235
], function(node) {
33-
var jsDoc = jsDocs.node(node);
34-
if (!jsDoc) {
35-
return;
36-
}
36+
node.jsDoc = jsDocs.node(node);
3737

38-
node.jsDoc = jsDoc.value.split('\n');
39-
node.jsDoc = node.jsDoc || {};
40-
node.jsDoc.paramIndex = 0;
41-
42-
function addError (text, locStart) {
43-
locStart = locStart || {};
44-
errors.add(
45-
text,
46-
locStart.line || (jsDoc.loc.start.line + i),
47-
locStart.column || (node.jsDoc[i].indexOf('@'))
48-
);
38+
for (var j = 0, k = validators.length; j < k; j += 1) {
39+
validators[j].call(that, node, addError);
4940
}
5041

51-
for (var i = 0, l = node.jsDoc.length; i < l; i++) {
52-
var line = node.jsDoc[i].trim();
53-
if (line.charAt(0) !== '*') {
54-
continue;
55-
}
56-
57-
line = line.substr(1).trim();
58-
59-
for (var j = 0, k = lineValidators.length; j < k; j++) {
60-
lineValidators[j](node, line, addError);
61-
}
42+
function addError (text, loc) {
43+
loc = loc || {};
44+
loc.line = loc.hasOwnProperty('line') ? loc.line : (node.jsDoc.loc.start.line);
45+
loc.column = loc.hasOwnProperty('column') ? loc.column : 0; //node.jsDoc[i].indexOf('@');
46+
errors.add(text, loc.line, loc.column);
6247
}
6348
});
6449

6550
},
6651

67-
loadLineValidators: function() {
52+
loadValidators: function() {
6853
var passedOptions = this._optionsList;
6954
var validators = [];
7055
if (!passedOptions) {
7156
return validators;
7257
}
7358

74-
var availableValidators = [
75-
'param',
76-
'returns'
77-
];
78-
availableValidators.forEach(function (name) {
79-
var v = require('./validate-jsdoc/' + name);
59+
Object.keys(validatorsByName).forEach(function (name) {
60+
var v = validatorsByName[name];
8061
if (!v.coveredOptions) {
8162
return;
8263
}

lib/rules/validate-jsdoc/index.js

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

lib/rules/validate-jsdoc/param.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
var jsDocHelpers = require('../../jsdoc-helpers');
33

4-
module.exports = validateParamLine;
4+
module.exports = jsDocHelpers.tagValidator(validateParamLine);
55
module.exports.coveredOptions = [
66
'checkParamNames',
77
'requireParamTypes',
@@ -12,10 +12,13 @@ module.exports.coveredOptions = [
1212
/**
1313
* validator for @param
1414
* @param {{type: 'FunctionDeclaration'}|{type: 'FunctionExpression'}} node
15-
* @param {Number} line
15+
* @param {JSDocTag} tag
1616
* @param {Function} err
1717
*/
18-
function validateParamLine(node, line, err) {
18+
function validateParamLine(node, tag, err) {
19+
node.jsDoc.paramIndex = node.jsDoc.paramIndex || 0;
20+
21+
var line = tag.value;
1922
var options = this._options;
2023
if (line.indexOf('@param') !== 0) {
2124
return;

lib/rules/validate-jsdoc/returns.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
var jsDocHelpers = require('../../jsdoc-helpers');
33
var esprimaHelpers = require('../../esprima-helpers');
44

5-
module.exports = validateReturnsLine;
5+
module.exports = jsDocHelpers.tagValidator(validateReturnsTag);
66
module.exports.coveredOptions = [
77
'checkReturnTypes',
88
'requireReturnTypes',
@@ -13,11 +13,12 @@ module.exports.coveredOptions = [
1313
/**
1414
* validator for @return/@returns
1515
* @param {(FunctionDeclaration|FunctionExpression)} node
16-
* @param {Number} line
16+
* @param {JSDocTag} tag
1717
* @param {Function} err
1818
*/
19-
function validateReturnsLine(node, line, err) {
19+
function validateReturnsTag(node, tag, err) {
2020
var options = this._options;
21+
var line = tag.value;
2122
if (line.indexOf('@return') !== 0) {
2223
return;
2324
}

package.json

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "jscs-jsdoc",
33
"author": "Alexej Yaroshevich <[email protected]>",
44
"description": "JSCS jsdoc plugin",
5-
"version": "0.0.6",
5+
"version": "0.0.7",
66
"main": "lib/index",
77
"homepage": "https://github.com/zxqfox/jscs-jsdoc",
88
"license": "MIT",
@@ -20,20 +20,23 @@
2020
"node": ">= 0.8.0"
2121
},
2222
"dependencies": {
23-
"doctrine": "~0.5.0"
23+
"comment-parser": "zxqfox/comment-parser#jscs-jsdoc",
24+
"doctrine": "^0.5.2"
2425
},
2526
"devDependencies": {
26-
"jscs": ">=1.3.0 <2.0",
27-
"jshint": "~2.4.4",
2827
"browserify": "~3.30.2",
28+
"chai": "^1.9.1",
29+
"esprima": "^1.2.2",
30+
"jscs": ">=1.3.0 <2.0",
31+
"jshint": "~2.5.4",
2932
"mocha": "~1.17.1"
3033
},
3134
"peerDependencies": {
3235
"jscs": ">=1.3.0 <2.0"
3336
},
3437
"scripts": {
3538
"lint": "jshint . && jscs lib test",
36-
"test": "npm run lint && mocha -u bdd -R spec",
39+
"test": "npm run lint && mocha",
3740
"browserify": "browserify --standalone JscsPluginJsdoc lib/index.js -o jscs-jsdoc-browser.js"
3841
},
3942
"files": [

test/init.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
var parse = require('comment-parser');
2+
3+
global.parse = parse;
4+
global.fnBody = fnBody;
5+
6+
function fnBody(func) {
7+
var str = func.toString();
8+
var out = str.slice(
9+
str.indexOf('{') + 1,
10+
str.lastIndexOf('}')
11+
);
12+
13+
// strip trailing spaces and tabs
14+
out = out.replace(/^\n*|[ \t]*$/g, '');
15+
16+
// strip preceding indentation
17+
var blockIndent = 0;
18+
out.match(/^([ \t]*)/gm).map(function (v) {
19+
if (!blockIndent || (v.length > 0 && v.length < blockIndent)) {
20+
blockIndent = v.length;
21+
}
22+
});
23+
24+
// rebuild block without inner indent
25+
out = !blockIndent ? out : out.split('\n').map(function (v) {
26+
return v.substr(blockIndent);
27+
}).join('\n');
28+
29+
return out;
30+
}

test/mocha.opts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
-u bdd -R spec

test/test.validate-jsdoc-basic.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
var Checker = require('jscs/lib/checker');
22
var assert = require('assert');
33

4-
describe('rules/validate-jsdoc', function () {
4+
describe('rules/validate-jsdoc @param', function () {
55

66
var checker;
77
beforeEach(function () {
@@ -18,7 +18,7 @@ describe('rules/validate-jsdoc', function () {
1818
checker.checkString(
1919
'var x = 1;\n' +
2020
'/**\n' +
21-
' * @param' +
21+
' * @param\n' +
2222
' */\n' +
2323
'function funcName(xxx) {\n' +
2424
'\n' +

0 commit comments

Comments
 (0)