Skip to content

Commit 4659458

Browse files
committed
Add rule to prevent too many spaces from being used to hard break
1 parent 47a81cf commit 4659458

9 files changed

Lines changed: 866 additions & 0 deletions

File tree

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Hard Break Spaces
2+
3+
> [ESLint rule][eslint-rules] to prevent too many spaces from being used to hard break in JSDoc descriptions containing Markdown.
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-hard-break-spaces' );
17+
```
18+
19+
#### rule
20+
21+
[ESLint rule][eslint-rules] to prevent too many spaces from being used to hard break in JSDoc descriptions containing Markdown.
22+
23+
**Bad**:
24+
25+
<!-- eslint-disable stdlib/jsdoc-hard-break-spaces, stdlib/jsdoc-markdown-remark -->
26+
27+
```javascript
28+
/**
29+
* Beep boop.
30+
*
31+
* Dots represent⋅⋅⋅
32+
* spaces.
33+
*
34+
* @returns {string} a value
35+
*
36+
* @example
37+
* var str = beep();
38+
* // returns 'boop'
39+
*/
40+
function beep() {
41+
return 'boop';
42+
}
43+
```
44+
45+
**Good**:
46+
47+
```javascript
48+
/**
49+
* Beep boop.
50+
*
51+
* Dots represent⋅⋅
52+
* spaces.
53+
*
54+
* @returns {string} a value
55+
*
56+
* @example
57+
* var str = beep();
58+
* // returns 'boop'
59+
*/
60+
function beep() {
61+
return 'boop';
62+
}
63+
```
64+
65+
</section>
66+
67+
<!-- /.usage -->
68+
69+
<section class="examples">
70+
71+
## Examples
72+
73+
```javascript
74+
var Linter = require( 'eslint' ).Linter;
75+
var rule = require( '@stdlib/_tools/eslint/rules/jsdoc-hard-break-spaces' );
76+
77+
var linter = new Linter();
78+
var result;
79+
var code;
80+
81+
// Generate our source code:
82+
code = [
83+
'/**',
84+
'* Beep boop.',
85+
'*',
86+
'* Additional ',
87+
'* information.',
88+
'*',
89+
'* @param {string} str - input value',
90+
'* @returns {string} output value',
91+
'*',
92+
'* @example',
93+
'* var out = beep( "boop" );',
94+
'* // returns "beepboop"',
95+
'*/',
96+
'function beep( str ) {',
97+
'\treturn "beep" + str;',
98+
'}'
99+
].join( '\n' );
100+
101+
// Register the ESLint rule:
102+
linter.defineRule( 'jsdoc-hard-break-spaces', rule );
103+
104+
// Lint the code:
105+
result = linter.verify( code, {
106+
'rules': {
107+
'jsdoc-hard-break-spaces': 'error'
108+
}
109+
});
110+
console.log( result );
111+
/* =>
112+
[
113+
{
114+
'ruleId': 'jsdoc-hard-break-spaces',
115+
'severity': 2,
116+
'message': 'Use two spaces for hard line breaks',
117+
'line': 4,
118+
'column': 13,
119+
'nodeType': null,
120+
'source': '* Additional ',
121+
'endLine': 13,
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: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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+
// Generate our source code:
11+
code = [
12+
'/**',
13+
'* Beep boop.',
14+
'*',
15+
'* Additional ',
16+
'* information.',
17+
'*',
18+
'* @param {string} str - input value',
19+
'* @returns {string} output value',
20+
'*',
21+
'* @example',
22+
'* var out = beep( "boop" );',
23+
'* // returns "beepboop"',
24+
'*/',
25+
'function beep( str ) {',
26+
'\treturn "beep" + str;',
27+
'}'
28+
].join( '\n' );
29+
30+
// Register the ESLint rule:
31+
linter.defineRule( 'jsdoc-hard-break-spaces', rule );
32+
33+
// Lint the code:
34+
result = linter.verify( code, {
35+
'rules': {
36+
'jsdoc-hard-break-spaces': 'error'
37+
}
38+
});
39+
console.log( result );
40+
/* =>
41+
[
42+
{
43+
'ruleId': 'jsdoc-hard-break-spaces',
44+
'severity': 2,
45+
'message': 'Use two spaces for hard line breaks',
46+
'line': 4,
47+
'column': 13,
48+
'nodeType': null,
49+
'source': '* Additional ',
50+
'endLine': 13,
51+
'endColumn': 3
52+
}
53+
]
54+
*/
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 prevent too many spaces from being used to hard break in JSDoc descriptions containing Markdown.
5+
*
6+
* @module @stdlib/_tools/eslint/rules/jsdoc-hard-break-spaces
7+
*
8+
* @example
9+
* var rule = require( '@stdlib/_tools/eslint/rules/jsdoc-hard-break-spaces' );
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: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
'use strict';
2+
3+
// MODULES //
4+
5+
var parseJSDoc = require( 'doctrine' ).parse;
6+
var remark = require( 'remark' );
7+
var remarkLint = require( 'remark-lint' );
8+
var remarkPlugin = require( 'remark-lint-hard-break-spaces' );
9+
var isObject = require( '@stdlib/assert/is-object' );
10+
var findJSDoc = require( '@stdlib/_tools/eslint/utils/find-jsdoc' );
11+
12+
13+
// VARIABLES //
14+
15+
var DOPTS = {
16+
'sloppy': true,
17+
'unwrap': true
18+
};
19+
20+
21+
// MAIN //
22+
23+
/**
24+
* Rule to prevent too many spaces from being used to hard break in JSDoc descriptions containing Markdown.
25+
*
26+
* @param {Object} context - ESLint context
27+
* @returns {Object} validators
28+
*/
29+
function main( context ) {
30+
var source;
31+
var config;
32+
var lint;
33+
34+
config = {
35+
'plugins': [
36+
remarkLint,
37+
[ remarkPlugin, 'error' ]
38+
]
39+
};
40+
lint = remark().use( config ).processSync;
41+
source = context.getSourceCode();
42+
43+
return {
44+
'FunctionExpression:exit': validate,
45+
'FunctionDeclaration:exit': validate,
46+
'VariableDeclaration:exit': validate,
47+
'ExpressionStatement:exit': validate
48+
};
49+
50+
/**
51+
* Lints JSDoc descriptions.
52+
*
53+
* @private
54+
* @param {ASTNode} node - AST node
55+
*/
56+
function validate( node ) {
57+
var jsdoc;
58+
var vfile;
59+
var ast;
60+
61+
jsdoc = findJSDoc( source, node );
62+
if ( isObject( jsdoc ) ) {
63+
ast = parseJSDoc( jsdoc.value, DOPTS );
64+
if ( ast.description ) {
65+
vfile = lint( ast.description );
66+
if ( vfile.messages.length ) {
67+
reportErrors( vfile.messages, jsdoc.loc );
68+
}
69+
}
70+
}
71+
} // end FUNCTION validate()
72+
73+
/**
74+
* Reports Markdown lint errors.
75+
*
76+
* @private
77+
* @param {ObjectArray} errors - Markdown lint errors
78+
* @param {Object} location - JSDoc location information
79+
*/
80+
function reportErrors( errors, location ) {
81+
var err;
82+
var msg;
83+
var loc;
84+
var i;
85+
86+
for ( i = 0; i < errors.length; i++ ) {
87+
err = errors[ i ];
88+
msg = err.message;
89+
loc = copyLocationInfo( location );
90+
loc.start.line = err.location.start.line + 1; // Note: we assume `/**` is on its own line
91+
loc.start.column = err.location.start.column + 1; // Note: we assume that 1 space separates `*` from JSDoc description content (e.g., `* ## Beep`)
92+
report( msg, loc );
93+
}
94+
} // end FUNCTION reportErrors()
95+
96+
/**
97+
* Copies AST node location info.
98+
*
99+
* @private
100+
* @param {Object} loc - AST node location
101+
* @returns {Object} copied location info
102+
*/
103+
function copyLocationInfo( loc ) {
104+
return {
105+
'start': {
106+
'line': loc.start.line,
107+
'column': loc.start.column
108+
},
109+
'end': {
110+
'line': loc.end.line,
111+
'column': loc.end.column
112+
}
113+
};
114+
} // end FUNCTION copyLocationInfo()
115+
116+
/**
117+
* Reports an error message.
118+
*
119+
* @private
120+
* @param {string} msg - error message
121+
* @param {Object} loc - error location info
122+
*/
123+
function report( msg, loc ) {
124+
context.report({
125+
'node': null,
126+
'message': msg,
127+
'loc': loc
128+
});
129+
} // end FUNCTION report()
130+
} // end FUNCTION main()
131+
132+
133+
// EXPORTS //
134+
135+
module.exports = {
136+
'meta': {
137+
'docs': {
138+
'description': 'prevent too many spaces from being used to hard break in JSDoc descriptions containing Markdown'
139+
},
140+
'schema': []
141+
},
142+
'create': main
143+
};

0 commit comments

Comments
 (0)