Skip to content

Commit e1f22dd

Browse files
committed
Merge branch 'develop' of https://github.com/stdlib-js/stdlib into develop
2 parents 76d37b9 + d3ef934 commit e1f22dd

21 files changed

Lines changed: 906 additions & 8 deletions

File tree

etc/eslint/rules/stdlib.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2179,6 +2179,50 @@ rules[ 'stdlib/jsdoc-table-pipes' ] = 'error';
21792179
*/
21802180
rules[ 'stdlib/jsdoc-tag-names' ] = 'error';
21812181

2182+
/**
2183+
* Require that JSDoc tags follow a specified ordering.
2184+
*
2185+
* @name jsdoc-tag-ordering
2186+
* @memberof rules
2187+
* @type {string}
2188+
* @default 'error'
2189+
*
2190+
* @example
2191+
* // Bad...
2192+
*
2193+
* /**
2194+
* * Squares a number.
2195+
* *
2196+
* * @arg {number} x - input number
2197+
* * @return {number} x squared
2198+
* *
2199+
* * @examples
2200+
* * var y = square( 2.0 );
2201+
* * // returns 4.0
2202+
* *\/
2203+
* function square( x ) {
2204+
* return x*x;
2205+
* }
2206+
*
2207+
* @example
2208+
* // Good...
2209+
*
2210+
* /**
2211+
* * Squares a number.
2212+
* *
2213+
* * @return {number} x squared
2214+
* * @arg {number} x - input number
2215+
* *
2216+
* * @examples
2217+
* * var y = square( 2.0 );
2218+
* * // returns 4.0
2219+
* *\/
2220+
* function square( x ) {
2221+
* return x*x;
2222+
* }
2223+
*/
2224+
rules[ 'stdlib/jsdoc-tag-ordering' ] = 'error';
2225+
21822226
/**
21832227
* Require that the unordered list marker be a dash `-`.
21842228
*
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# jsdoc-tag-ordering
2+
3+
> [ESLint rule][eslint-rules] to enforce that JSDoc tags follow a specified ordering.
4+
5+
<section class="intro">
6+
7+
</section>
8+
9+
<!-- /.intro -->
10+
11+
<section class="usage">
12+
13+
## Usage
14+
15+
```javascript
16+
var rule = require( '@stdlib/_tools/eslint/rules/jsdoc-tag-ordering' );
17+
```
18+
19+
#### rule
20+
21+
[ESLint rule][eslint-rules] to enforce that JSDoc tags follow a specified ordering.
22+
23+
**Bad**:
24+
25+
<!-- eslint-disable stdlib/jsdoc-tag-ordering, valid-jsdoc -->
26+
27+
```javascript
28+
/**
29+
* Calculates the square root of a number.
30+
*
31+
* @param {NonNegativeNumber} x - input number
32+
* @returns {number} x squared
33+
* @throws {RangeError} x must be a non-negative number
34+
*
35+
* @example
36+
* var y = square( 2.0 );
37+
* // returns 4.0
38+
*/
39+
function sqrt( x ) {
40+
if ( x < 0 ) {
41+
throw new RangeError( 'argument must be a non-negative number. Value: '+x );
42+
}
43+
return Math.sqrt( x );
44+
}
45+
```
46+
47+
**Good**:
48+
49+
```javascript
50+
/**
51+
* Calculates the square root of a number.
52+
*
53+
* @param {NonNegativeNumber} x - input number
54+
* @throws {RangeError} x must be a non-negative number
55+
* @returns {number} x squared
56+
*
57+
* @example
58+
* var y = sqrt( 4.0 );
59+
* // returns 2.0
60+
*/
61+
function sqrt( x ) {
62+
if ( x < 0 ) {
63+
throw new RangeError( 'argument must be a non-negative number. Value: '+x );
64+
}
65+
return Math.sqrt( x );
66+
}
67+
```
68+
69+
</section>
70+
71+
<!-- /.usage -->
72+
73+
<section class="examples">
74+
75+
## Examples
76+
77+
<!-- eslint no-undef: "error" -->
78+
79+
```javascript
80+
var Linter = require( 'eslint' ).Linter;
81+
var rule = require( '@stdlib/_tools/eslint/rules/jsdoc-tag-ordering' );
82+
83+
var linter = new Linter();
84+
var result;
85+
var code;
86+
87+
code = [
88+
'/**',
89+
'* Squares a number.',
90+
'* ',
91+
'* @returns {number} x squared',
92+
'* @param {number} x - input number',
93+
'*',
94+
'* @examples',
95+
'* var y = square( 2.0 );',
96+
'* // returns 4.0',
97+
'*/',
98+
'function square( x ) {',
99+
' return x*x;',
100+
'}'
101+
].join( '\n' );
102+
103+
linter.defineRule( 'jsdoc-tag-ordering', rule );
104+
105+
result = linter.verify( code, {
106+
'rules': {
107+
'jsdoc-tag-ordering': 'error'
108+
}
109+
});
110+
console.log( result );
111+
/* =>
112+
[
113+
{
114+
'ruleId': 'jsdoc-tag-ordering',
115+
'severity': 2,
116+
'message': '"@param" tag may not follow "@returns',
117+
'line': 1,
118+
'column': 1,
119+
'nodeType': null,
120+
'source': '/**',
121+
'endLine': 10,
122+
'endColumn': 3
123+
}
124+
]
125+
*/
126+
```
127+
128+
</section>
129+
130+
<!-- /.examples -->
131+
132+
<section class="links">
133+
134+
[eslint-rules]: https://eslint.org/docs/developer-guide/working-with-rules
135+
136+
</section>
137+
138+
<!-- /.links -->
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use strict';
2+
3+
var Linter = require( 'eslint' ).Linter;
4+
var rule = require( './../lib' );
5+
6+
var linter = new Linter();
7+
var result;
8+
var code;
9+
10+
code = [
11+
'/**',
12+
'* Squares a number.',
13+
'* ',
14+
'* @returns {number} x squared',
15+
'* @param {number} x - input number',
16+
'*',
17+
'* @example',
18+
'* var y = square( 2.0 );',
19+
'* // returns 4.0',
20+
'*/',
21+
'function square( x ) {',
22+
' return x*x;',
23+
'}'
24+
].join( '\n' );
25+
26+
linter.defineRule( 'jsdoc-tag-ordering', rule );
27+
28+
result = linter.verify( code, {
29+
'rules': {
30+
'jsdoc-tag-ordering': 'error'
31+
}
32+
});
33+
console.log( result );
34+
/* =>
35+
[
36+
{
37+
'ruleId': 'jsdoc-tag-ordering',
38+
'severity': 2,
39+
'message': '"@param" tag may not follow "@returns',
40+
'line': 1,
41+
'column': 1,
42+
'nodeType': null,
43+
'source': '/**',
44+
'endLine': 10,
45+
'endColumn': 3
46+
}
47+
]
48+
*/
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"^": [ "constant", "constructor", "example", "function", "memberof", "module", "name", "namespace", "param", "private", "returns", "see", "throws" ],
3+
"constant": [ "type", "default", "see" ],
4+
"default": [ "example", "see" ],
5+
"function": [ "example", "param", "returns", "throws", "type" ],
6+
"memberof": [ "constructor", "example", "function", "param", "readonly", "returns", "throws", "type" ],
7+
"module": [ "example" ],
8+
"name": [ "memberof", "readonly", "type" ],
9+
"private": [ "constant", "constructor", "example", "function", "memberof", "param", "returns", "throws" ],
10+
"param": [ "example", "param", "returns", "throws" ],
11+
"readonly": [ "constant", "constructor", "function", "type" ],
12+
"returns": [ "example", "see" ],
13+
"throws": [ "example", "returns", "throws" ],
14+
"type": [ "default", "example", "param", "returns", "throws", "see" ],
15+
"see": [ "example", "see" ],
16+
"example": [ "example" ]
17+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use strict';
2+
3+
/**
4+
* ESLint rule to enforce that JSDoc tags follow a specified ordering.
5+
*
6+
* @module @stdlib/_tools/eslint/rules/jsdoc-tag-ordering
7+
*
8+
* @example
9+
* var rule = require( '@stdlib/_tools/eslint/rules/jsdoc-tag-ordering' );
10+
*
11+
* console.log( rule );
12+
*/
13+
14+
// MODULES //
15+
16+
var main = require( './main.js' );
17+
18+
19+
// EXPORTS //
20+
21+
module.exports = main;
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
'use strict';
2+
3+
// MODULES //
4+
5+
var parseJSDoc = require( 'doctrine' ).parse;
6+
var contains = require( '@stdlib/assert/contains' );
7+
var isArray = require( '@stdlib/assert/is-array' );
8+
var isObject = require( '@stdlib/assert/is-object' );
9+
var findJSDoc = require( '@stdlib/_tools/eslint/utils/find-jsdoc' );
10+
var ALLOWED = require( './allowed.json' );
11+
12+
13+
// VARIABLES //
14+
15+
var DOPTS = {
16+
'sloppy': true,
17+
'unwrap': true
18+
};
19+
20+
21+
// MAIN //
22+
23+
/**
24+
* Rule for validating that tags inside of JSDoc comments follow a specified ordering.
25+
*
26+
* @param {Object} context - ESLint context
27+
* @returns {Object} validators
28+
*/
29+
function main( context ) {
30+
var source = context.getSourceCode();
31+
return {
32+
'FunctionExpression:exit': validate,
33+
'FunctionDeclaration:exit': validate,
34+
'VariableDeclaration:exit': validate,
35+
'ExpressionStatement:exit': validate
36+
};
37+
38+
/**
39+
* Checks whether JSDoc comments contain only allowed JSDoc tags.
40+
*
41+
* @private
42+
* @param {ASTNode} node - AST node
43+
*/
44+
function validate( node ) {
45+
var allowed;
46+
var current;
47+
var jsdoc;
48+
var last;
49+
var tags;
50+
var ast;
51+
var i;
52+
53+
jsdoc = findJSDoc( source, node );
54+
if ( isObject( jsdoc ) ) {
55+
ast = parseJSDoc( jsdoc.value, DOPTS );
56+
tags = ast.tags;
57+
last = '^';
58+
for ( i = 0; i < tags.length; i++ ) {
59+
current = tags[ i ].title;
60+
allowed = ALLOWED[ last ];
61+
if ( isArray( allowed ) && !contains( allowed, current ) ) {
62+
report( last, current, jsdoc.loc );
63+
}
64+
last = current;
65+
}
66+
}
67+
} // end FUNCTION validate()
68+
69+
/**
70+
* Reports the error message.
71+
*
72+
* @private
73+
* @param {string} last - name of last tag
74+
* @param {string} current - name of current tag
75+
* @param {Object} loc - lines of code (object with `start` and `end` properties)
76+
*/
77+
function report( last, current, loc ) {
78+
var msg = '"@'+current+'" tag may not ';
79+
if ( last === '^' ) {
80+
msg += 'be the first tag';
81+
} else {
82+
msg += 'follow "@'+last+'"';
83+
}
84+
context.report({
85+
'node': null,
86+
'message': msg,
87+
'loc': loc
88+
});
89+
} // end FUNCTION report()
90+
} // end FUNCTION main()
91+
92+
93+
// EXPORTS //
94+
95+
module.exports = {
96+
'meta': {
97+
'docs': {
98+
'description': 'enforce that JSDoc tags follow a specified ordering'
99+
},
100+
'schema': []
101+
},
102+
'create': main
103+
};

0 commit comments

Comments
 (0)