Skip to content

Commit

Permalink
little refactoring of jsdoc validators
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexej Yaroshevich committed Aug 20, 2014
1 parent c0924b6 commit 4b2dfad
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 115 deletions.
142 changes: 27 additions & 115 deletions lib/rules/validate-jsdoc.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
var assert = require('assert'),
var assert = require('assert');

jsDocHelpers = require('../jsdoc-helpers'),
esprimaHelpers = require('../esprima-helpers');
var jsDocHelpers = require('../jsdoc-helpers');

module.exports = function() {};

Expand All @@ -10,24 +9,15 @@ module.exports.prototype = {
configure: function(options) {
assert(typeof options === 'object', 'jsDoc option requires object value');
this._options = options;
this._optionsList = Object.keys(options);
},

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

check: function(file, errors) {
var options = this._options;
var lineValidators = [];

// create validators list
if (options.checkParamNames || options.checkRedundantParams || options.requireParamTypes) {
lineValidators.push(validateParamLine);
}
if (options.checkReturnTypes || options.checkRedundantReturns || options.checkTypes ||
options.requireReturnTypes) {
lineValidators.push(validateReturnsLine);
}
var lineValidators = this.loadLineValidators();

// skip if there is nothing to check
if (!lineValidators.length) {
Expand All @@ -36,7 +26,10 @@ module.exports.prototype = {

var jsDocs = jsDocHelpers.parseComments(file.getComments());

file.iterateNodesByType(['FunctionDeclaration', 'FunctionExpression'], function(node) {
file.iterateNodesByType([
'FunctionDeclaration',
'FunctionExpression'
], function(node) {
var jsDoc = jsDocs.node(node);
if (!jsDoc) {
return;
Expand Down Expand Up @@ -69,113 +62,32 @@ module.exports.prototype = {
}
});

/**
* validator for @param
* @param {{type: 'FunctionDeclaration'}|{type: 'FunctionExpression'}} node
* @param {Number} line
* @param {Function} err
*/
function validateParamLine(node, line, err) {
if (line.indexOf('@param') !== 0) {
return;
}

// checking validity
var match = line.match(/^@param\s+(?:{(.+?)})?\s*(\[)?([a-zA-Z0-9_\.\$]+)/);
if (!match) {
return err('Invalid JsDoc @param');
}

var jsDocType = match[1];
var jsDocName = match[3];
var jsDocOptional = match[2] === '[';

// checking existance
if (options.requireParamTypes && !jsDocType) {
return err('Missing JsDoc @param type');
}

var jsDocParsedType = jsDocHelpers.parse(jsDocType);
if (options.checkTypes && jsDocParsedType.invalid) {
return err('Invalid JsDoc type definition');
}

// skip if there is dot in param name (object's inner param)
if (jsDocName.indexOf('.') !== -1) {
return;
}

// checking redudant
var param = node.params[node.jsDoc.paramIndex];
if (options.checkRedundantParams && !jsDocOptional && !param) {
return err('Redundant JsDoc @param');
}

// checking name
if (options.checkParamNames && jsDocName !== param.name) {
return err('Invalid JsDoc @param argument name');
}
},

node.jsDoc.paramIndex++;
loadLineValidators: function() {
var passedOptions = this._optionsList;
var validators = [];
if (!passedOptions) {
return validators;
}

/**
* validator for @return/@returns
* @param {(FunctionDeclaration|FunctionExpression)} node
* @param {Number} line
* @param {Function} err
*/
function validateReturnsLine(node, line, err) {
if (line.indexOf('@return') !== 0) {
return;
}

// checking validity
var match = line.match(/^@returns?\s+(?:{(.+?)})?/);
if (!match) {
return err('Invalid JsDoc @returns');
}

var jsDocType = match[1];

// checking existance
if (options.requireReturnTypes && !jsDocType) {
err('Missing JsDoc @returns type');
}

var jsDocParsedType = jsDocHelpers.parse(jsDocType);
if (options.checkTypes && jsDocParsedType.invalid) {
return err('Invalid JsDoc type definition');
}

if (!options.checkRedundantReturns && !options.checkReturnTypes) {
var availableValidators = [
'param',
'returns'
];
availableValidators.forEach(function (name) {
var v = require('./validate-jsdoc/' + name);
if (!v.coveredOptions) {
return;
}

var returnsArgumentStatements = [];
esprimaHelpers.treeIterator.iterate(node, function(n/*, parentNode, parentCollection*/) {
if (n && n.type === 'ReturnStatement' && n.argument) {
if (node === esprimaHelpers.closestScopeNode(n)) {
returnsArgumentStatements.push(n.argument);
}
for (var i = 0, l = v.coveredOptions.length; i < l; i += 1) {
if (passedOptions.indexOf(v.coveredOptions[i]) !== -1) {
validators.push(v.bind(this));
return;
}
});

// checking redundant
if (options.checkRedundantReturns && !returnsArgumentStatements.length) {
err('Redundant JsDoc @returns');
}
}.bind(this));

// try to check returns types
if (options.checkReturnTypes && jsDocParsedType) {
returnsArgumentStatements.forEach(function (argument) {
if (!jsDocHelpers.match(jsDocParsedType, argument)) {
err('Wrong returns value', argument.loc.start);
}
});
}
}

return validators;
}

};
61 changes: 61 additions & 0 deletions lib/rules/validate-jsdoc/param.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@

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

module.exports = validateParamLine;
module.exports.coveredOptions = [
'checkParamNames',
'requireParamTypes',
'checkRedundantParams',
'checkTypes',
];

/**
* validator for @param
* @param {{type: 'FunctionDeclaration'}|{type: 'FunctionExpression'}} node
* @param {Number} line
* @param {Function} err
*/
function validateParamLine(node, line, err) {
var options = this._options;
if (line.indexOf('@param') !== 0) {
return;
}

// checking validity
var match = line.match(/^@param\s+(?:{(.+?)})?\s*(\[)?([a-zA-Z0-9_\.\$]+)/);
if (!match) {
return err('Invalid JsDoc @param');
}

var jsDocType = match[1];
var jsDocName = match[3];
var jsDocOptional = match[2] === '[';

// checking existance
if (options.requireParamTypes && !jsDocType) {
return err('Missing JsDoc @param type');
}

var jsDocParsedType = jsDocHelpers.parse(jsDocType);
if (options.checkTypes && jsDocParsedType.invalid) {
return err('Invalid JsDoc type definition');
}

// skip if there is dot in param name (object's inner param)
if (jsDocName.indexOf('.') !== -1) {
return;
}

// checking redudant
var param = node.params[node.jsDoc.paramIndex];
if (options.checkRedundantParams && !jsDocOptional && !param) {
return err('Redundant JsDoc @param');
}

// checking name
if (options.checkParamNames && jsDocName !== param.name) {
return err('Invalid JsDoc @param argument name');
}

node.jsDoc.paramIndex++;
}
69 changes: 69 additions & 0 deletions lib/rules/validate-jsdoc/returns.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@

var jsDocHelpers = require('../../jsdoc-helpers');
var esprimaHelpers = require('../../esprima-helpers');

module.exports = validateReturnsLine;
module.exports.coveredOptions = [
'checkReturnTypes',
'requireReturnTypes',
'checkRedundantReturns',
'checkTypes',
];

/**
* validator for @return/@returns
* @param {(FunctionDeclaration|FunctionExpression)} node
* @param {Number} line
* @param {Function} err
*/
function validateReturnsLine(node, line, err) {
var options = this._options;
if (line.indexOf('@return') !== 0) {
return;
}

// checking validity
var match = line.match(/^@returns?\s+(?:{(.+?)})?/);
if (!match) {
return err('Invalid JsDoc @returns');
}

var jsDocType = match[1];

// checking existance
if (options.requireReturnTypes && !jsDocType) {
err('Missing JsDoc @returns type');
}

var jsDocParsedType = jsDocHelpers.parse(jsDocType);
if (options.checkTypes && jsDocParsedType.invalid) {
return err('Invalid JsDoc type definition');
}

if (!options.checkRedundantReturns && !options.checkReturnTypes) {
return;
}

var returnsArgumentStatements = [];
esprimaHelpers.treeIterator.iterate(node, function(n/*, parentNode, parentCollection*/) {
if (n && n.type === 'ReturnStatement' && n.argument) {
if (node === esprimaHelpers.closestScopeNode(n)) {
returnsArgumentStatements.push(n.argument);
}
}
});

// checking redundant
if (options.checkRedundantReturns && !returnsArgumentStatements.length) {
err('Redundant JsDoc @returns');
}

// try to check returns types
if (options.checkReturnTypes && jsDocParsedType) {
returnsArgumentStatements.forEach(function (argument) {
if (!jsDocHelpers.match(jsDocParsedType, argument)) {
err('Wrong returns value', argument.loc.start);
}
});
}
}

0 comments on commit 4b2dfad

Please sign in to comment.