11var assert = require ( 'assert' ) ;
22
33var jsDocHelpers = require ( '../jsdoc-helpers' ) ;
4+ var esprimaHelpers = require ( '../esprima-helpers' ) ;
45var validators = require ( './validate-jsdoc/index' ) ;
56
67module . exports = function ( ) { } ;
@@ -11,62 +12,141 @@ module.exports.prototype = {
1112 configure : function ( options ) {
1213 assert ( typeof options === 'object' , 'jsDoc option requires object value' ) ;
1314
15+ // rules structured by scopes-tags for jsdoc-tags
16+ var rulesForTags = this . _rulesForTags = { } ;
17+ // rules structured by scopes for nodes
18+ var rulesForNodes = this . _rulesForNodes = { } ;
19+
1420 this . _options = options ;
1521 this . _optionsList = Object . keys ( options ) ;
16- this . _validators = validators . load ( this . _optionsList ) ;
1722
23+ // load validators
24+ this . _validators = validators . load ( this . _optionsList ) ;
1825 assert ( this . _validators . length , 'jsDoc was not configured properly' ) ;
1926
27+ // registering validators
2028 this . _validators . forEach ( function ( v ) {
21- if ( v . configure ) {
22- v . configure . call ( this , options ) ;
23- }
29+ // check options
2430 if ( v . options ) {
2531 validators . checkOptions ( v , options ) ;
2632 }
27- } . bind ( this ) ) ;
33+ // index rules by tags and scopes
34+ ( v . scopes || [ '' ] ) . forEach ( function ( scope ) {
35+ if ( ! v . tags ) {
36+ assert ( v . length === 2 , 'jsDoc rules: Wrong arity in ' + v . _name + ' validator' ) ;
37+ rulesForNodes [ scope ] = rulesForNodes [ scope ] || [ ] ;
38+ rulesForNodes [ scope ] . push ( v ) ;
39+ return ;
40+ }
41+ assert ( v . length === 3 , 'jsDoc rules: Wrong arity in ' + v . _name + ' validator' ) ;
42+ rulesForTags [ scope ] = rulesForTags [ scope ] || { } ;
43+ v . tags . forEach ( function ( tag ) {
44+ rulesForTags [ scope ] [ tag ] = rulesForTags [ scope ] [ tag ] || [ ] ;
45+ rulesForTags [ scope ] [ tag ] . push ( v ) ;
46+ } ) ;
47+ } ) ;
48+ } , this ) ;
2849 } ,
2950
3051 getOptionName : function ( ) {
3152 return 'jsDoc' ;
3253 } ,
3354
3455 check : function ( file , errors ) {
35- var activeValidators = this . _validators ;
36-
37- // skip if there is no validators
38- if ( ! activeValidators . length ) {
39- return ;
40- }
41-
4256 var jsDocs = jsDocHelpers . parseComments ( file . getComments ( ) ) ;
4357 var _this = this ;
4458
45- file . iterateNodesByType ( [
46- 'FunctionDeclaration' ,
47- 'FunctionExpression'
59+ var scopes = {
60+ 'function' : [
61+ 'FunctionDeclaration' ,
62+ 'FunctionExpression'
63+ ]
64+ } ;
65+
66+ Object . keys ( scopes ) . forEach ( function ( scope ) {
67+ // skip unused scopes
68+ if ( ! _this . _rulesForNodes [ scope ] && ! _this . _rulesForTags [ scope ] ) {
69+ return ;
70+ }
4871
49- ] , function ( node ) {
50- node . jsDoc = jsDocs . forNode ( node ) ;
51- var commentStart = ( node . jsDoc || node ) . loc . start ;
72+ // traverse ast tree and search scope node types
73+ file . iterateNodesByType ( scopes [ scope ] , function ( node ) {
74+ // init
75+ node . jsDoc = node . jsDoc || jsDocs . forNode ( node ) ;
76+ var commentStart = ( node . jsDoc || node ) . loc . start ;
77+ var commentStartLine = commentStart . line ;
78+ var validators ;
79+
80+ // call node checkers
81+ validators = _this . _rulesForNodes [ scope ] ;
82+ if ( validators ) {
83+ for ( var j = 0 , k = validators . length ; j < k ; j += 1 ) {
84+ validators [ j ] . call ( _this , node , addError ) ;
85+ }
86+ }
5287
53- for ( var j = 0 , k = activeValidators . length ; j < k ; j += 1 ) {
54- activeValidators [ j ] . call ( _this , node , addError ) ;
55- }
88+ validators = _this . _rulesForTags [ scope ] ;
89+ if ( ! node . jsDoc || ! validators ) {
90+ return ;
91+ }
92+
93+ // call rule checkers
94+ node . jsDoc . forEachTag ( function ( tag , i ) {
95+ if ( ! validators [ tag . tag ] ) {
96+ return ;
97+ }
98+ // call tag validator
99+ commentStart . line = commentStartLine + i ;
100+ validators [ tag . tag ] . forEach ( function ( v ) {
101+ v . call ( _this , node , tag , fixErrLocation ( addError , tag ) ) ;
102+ } ) ;
103+ } ) ;
104+
105+ function addError ( text , relLine , relColumn ) {
106+ var line ;
107+ var column ;
108+ if ( typeof relLine === 'object' ) {
109+ line = relLine . line ;
110+ column = relLine . column ;
111+ } else {
112+ line = commentStart . line + ( relLine || 0 ) ;
113+ column = commentStart . column + ( relColumn || 0 ) ;
114+ }
115+ errors . add ( text , line , column ) ;
116+ }
117+
118+ function fixErrLocation ( err , tag ) {
119+ return function ( text , line , column ) {
120+ line = line || tag . line ;
121+ // probably buggy. multiline comment could resolved to 0
122+ column = column || node . jsDoc . lines [ tag . line ] . indexOf ( tag . value ) ;
123+ err ( text , line , column ) ;
124+ } ;
125+ }
126+ } ) ;
127+
128+ } ) ;
129+
130+ } ,
131+
132+ /**
133+ * caching scope search
134+ */
135+ _getReturnStatementsForNode : function ( node ) {
136+ if ( node . jsDoc . returnStatements ) {
137+ return node . jsDoc . returnStatements ;
138+ }
56139
57- function addError ( text , relLine , relColumn ) {
58- var line ;
59- var column ;
60- if ( typeof relLine === 'object' ) {
61- line = relLine . line ;
62- column = relLine . column ;
63- } else {
64- line = commentStart . line + ( relLine || 0 ) ;
65- column = commentStart . column + ( relColumn || 0 ) ;
140+ var statements = [ ] ;
141+ esprimaHelpers . treeIterator . iterate ( node , function ( n /*, parentNode, parentCollection*/ ) {
142+ if ( n && n . type === 'ReturnStatement' && n . argument ) {
143+ if ( node === esprimaHelpers . closestScopeNode ( n ) ) {
144+ statements . push ( n . argument ) ;
66145 }
67- errors . add ( text , line , column ) ;
68146 }
69147 } ) ;
70148
149+ node . jsDoc . returnStatements = statements ;
150+ return statements ;
71151 }
72152} ;
0 commit comments