From 4b2dfadc4a8c93f0ae7846868b53c1de2939e34a Mon Sep 17 00:00:00 2001 From: Alexej Yaroshevich Date: Wed, 20 Aug 2014 06:28:14 +0400 Subject: [PATCH] little refactoring of jsdoc validators --- lib/rules/validate-jsdoc.js | 142 ++++++---------------------- lib/rules/validate-jsdoc/param.js | 61 ++++++++++++ lib/rules/validate-jsdoc/returns.js | 69 ++++++++++++++ 3 files changed, 157 insertions(+), 115 deletions(-) create mode 100644 lib/rules/validate-jsdoc/param.js create mode 100644 lib/rules/validate-jsdoc/returns.js diff --git a/lib/rules/validate-jsdoc.js b/lib/rules/validate-jsdoc.js index 12ed2c2..551e262 100644 --- a/lib/rules/validate-jsdoc.js +++ b/lib/rules/validate-jsdoc.js @@ -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() {}; @@ -10,6 +9,7 @@ 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() { @@ -17,17 +17,7 @@ module.exports.prototype = { }, 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) { @@ -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; @@ -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; } - }; diff --git a/lib/rules/validate-jsdoc/param.js b/lib/rules/validate-jsdoc/param.js new file mode 100644 index 0000000..3ebbbfa --- /dev/null +++ b/lib/rules/validate-jsdoc/param.js @@ -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++; +} diff --git a/lib/rules/validate-jsdoc/returns.js b/lib/rules/validate-jsdoc/returns.js new file mode 100644 index 0000000..f1c268f --- /dev/null +++ b/lib/rules/validate-jsdoc/returns.js @@ -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); + } + }); + } +}