From 41f1dbbebbd20f94836f82f31adbed80d941be49 Mon Sep 17 00:00:00 2001 From: Alexej Yaroshevich Date: Tue, 11 Nov 2014 07:27:01 +0400 Subject: [PATCH] checkAnnotations: add tag checking by presets Fixes #18 --- lib/rules/validate-jsdoc.js | 4 + lib/rules/validate-jsdoc/check-annotations.js | 77 +++++++++++++++++++ lib/rules/validate-jsdoc/index.js | 3 +- lib/tags/index.js | 5 ++ .../rules/validate-jsdoc/check-annotations.js | 25 ++++++ 5 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 lib/rules/validate-jsdoc/check-annotations.js create mode 100644 lib/tags/index.js create mode 100644 test/lib/rules/validate-jsdoc/check-annotations.js diff --git a/lib/rules/validate-jsdoc.js b/lib/rules/validate-jsdoc.js index 1776d5c..5925f30 100644 --- a/lib/rules/validate-jsdoc.js +++ b/lib/rules/validate-jsdoc.js @@ -30,6 +30,10 @@ module.exports.prototype = { if (v.options) { validators.checkOptions(v, options); } + // configure + if (v.configure) { + v.configure(options); + } // index rules by tags and scopes (v.scopes || ['']).forEach(function(scope) { if (!v.tags) { diff --git a/lib/rules/validate-jsdoc/check-annotations.js b/lib/rules/validate-jsdoc/check-annotations.js new file mode 100644 index 0000000..849b104 --- /dev/null +++ b/lib/rules/validate-jsdoc/check-annotations.js @@ -0,0 +1,77 @@ +var assert = require('assert'); + +var availablePresets = require('../../tags'); +var jsdoc = require('../../jsdoc'); + +module.exports = validateAnnotations; +module.exports.scopes = ['file']; +module.exports.options = { + checkAnnotations: true +}; + +var tags; + +validateAnnotations.configure = function(options) { + var o = options.checkAnnotations; + + assert(o === true || typeof o === 'string' || typeof o === 'object', + 'jsDoc.checkAnnotation rule was not configured properly'); + + if (typeof o === 'string') { + o = {preset: o}; + } + + tags = {}; + + if (o === true) { + Object.keys(availablePresets).forEach(function(preset) { + var presetTags = availablePresets[preset]; + Object.keys(presetTags).forEach(function(tag) { + tags[tag] = tags[tag] || presetTags[tag]; + }); + }); + + } else if (typeof o === 'object') { + if (o.preset) { + assert(typeof o.preset === 'string', 'jsDoc.checkAnnotation.preset should be preset name'); + assert(availablePresets[o.preset], 'Unknown tag preset ' + o.preset); + Object.keys(availablePresets[o.preset]).forEach(function(tag) { + tags[tag] = tags[tag] || availablePresets[o.preset][tag]; + }); + } + if (o.extra) { + Object.keys(o.extra).forEach(function(tag) { + tags[tag] = o.extra[tag]; + }); + } + } +}; + +/** + * validator for annotations + * @param {JSCS.JSFile} file + * @param {JSCS.Errors} errors + */ +function validateAnnotations(file, errors) { + var comments = file.getComments(); + comments.forEach(function(commentNode) { + if (commentNode.type !== 'Block' || commentNode.value[0] !== '*') { + return; + } + + // trying to create DocComment object + var node = jsdoc.createDocCommentByCommentNode(commentNode); + if (!node.valid) { + return; + } + + node.iterate(function(tag) { + if (!tags.hasOwnProperty[tag.id]) { + errors.add('unavailable tag ' + tag.id, tag.loc); + } + else if (tags[tag.id] && (!tag.name || !tag.type)) { + errors.add('incomplete tag ' + tag.id + ' data', tag.loc); + } + }); + }); +} diff --git a/lib/rules/validate-jsdoc/index.js b/lib/rules/validate-jsdoc/index.js index 1788a69..fc86bf6 100644 --- a/lib/rules/validate-jsdoc/index.js +++ b/lib/rules/validate-jsdoc/index.js @@ -12,7 +12,8 @@ var validatorsByName = module.exports = { requireReturnTypes: require('./require-return-types'), checkRedundantReturns: require('./check-redundant-returns'), - //returns: require('./returns'), + checkAnnotations: require('./check-annotations'), + checkRedundantAccess: require('./check-redundant-access'), enforceExistence: require('./enforce-existence'), leadingUnderscoreAccess: require('./leading-underscore-access') diff --git a/lib/tags/index.js b/lib/tags/index.js new file mode 100644 index 0000000..4f1fcc5 --- /dev/null +++ b/lib/tags/index.js @@ -0,0 +1,5 @@ +module.exports = { + jsdoc3: require('./jsdoc3'), + jsduck5: require('./jsduck5'), + closurecompiler: require('./closurecompiler') +}; diff --git a/test/lib/rules/validate-jsdoc/check-annotations.js b/test/lib/rules/validate-jsdoc/check-annotations.js new file mode 100644 index 0000000..1f4e357 --- /dev/null +++ b/test/lib/rules/validate-jsdoc/check-annotations.js @@ -0,0 +1,25 @@ +describe('lib/rules/validate-jsdoc/check-annotations', function () { + var checker = global.checker({ + additionalRules: ['lib/rules/validate-jsdoc.js'] + }); + + describe('with true', function () { + checker.rules({checkAnnotations: true}); + + checker.cases([ + /* jshint ignore:start */ + { + it: 'should throw unavailable tag', + errors: {message: 'unavailable tag ppublic'}, + code: function() { + /** + * @ppublic + */ + } + } + /* jshint ignore:end */ + ]); + + }); + +});