Skip to content

Commit

Permalink
Add rule to require angle-bracketed links to include a protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
kgryte committed Dec 29, 2017
1 parent c84aa31 commit 6026576
Show file tree
Hide file tree
Showing 9 changed files with 861 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Link Protocol

> [ESLint rule][eslint-rules] to require angle-bracketed Markdown links to contain protocols in JSDoc descriptions.
<section class="intro">

</section>

<!-- /.intro -->

<section class="usage">

## Usage

```javascript
var rule = require( '@stdlib/_tools/eslint/rules/jsdoc-no-auto-link-without-protocol' );
```

#### rule

[ESLint rule][eslint-rules] to require angle-bracketed Markdown links to contain protocols in JSDoc descriptions.

**Bad**:

<!-- eslint-disable stdlib/jsdoc-no-auto-link-without-protocol, stdlib/jsdoc-markdown-remark -->

```javascript
/**
* Beep.
*
* <foo@bar.com>
*
* @returns {string} a value
*
* @example
* var str = beep();
* // returns 'boop'
*/
function beep() {
return 'boop';
}
```

**Good**:

```javascript
/**
* Beep.
*
* <mailto:foo@bar.com>
*
* @returns {string} a value
*
* @example
* var str = beep();
* // returns 'boop'
*/
function beep() {
return 'boop';
}
```

</section>

<!-- /.usage -->

<section class="examples">

## Examples

<!-- eslint no-undef: "error" -->

```javascript
var Linter = require( 'eslint' ).Linter;
var rule = require( '@stdlib/_tools/eslint/rules/jsdoc-no-auto-link-without-protocol' );

var linter = new Linter();
var result;
var code;

// Generate our source code:
code = [
'/**',
'* Beep boop.',
'*',
'* <foo@bar.com>',
'*',
'*',
'* @param {string} str - input value',
'* @returns {string} output value',
'*',
'* @example',
'* var out = beep( "boop" );',
'* // returns "beepboop"',
'*/',
'function beep( str ) {',
'\treturn "beep" + str;',
'}'
].join( '\n' );

// Register the ESLint rule:
linter.defineRule( 'jsdoc-no-auto-link-without-protocol', rule );

// Lint the code:
result = linter.verify( code, {
'rules': {
'jsdoc-no-auto-link-without-protocol': 'error'
}
});
console.log( result );
/* =>
[
{
'ruleId': 'jsdoc-no-auto-link-without-protocol',
'severity': 2,
'message': 'All automatic links must start with a protocol',
'line': 4,
'column': 3,
'nodeType': null,
'source': '* <foo@bar.com>',
'endLine': 13,
'endColumn': 3
}
]
*/
```

</section>

<!-- /.examples -->

<section class="links">

[eslint-rules]: https://eslint.org/docs/developer-guide/working-with-rules

</section>

<!-- /.links -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use strict';

var Linter = require( 'eslint' ).Linter;
var rule = require( './../lib' );

var linter = new Linter();
var result;
var code;

// Generate our source code:
code = [
'/**',
'* Beep boop.',
'*',
'* <foo@bar.com>',
'*',
'*',
'* @param {string} str - input value',
'* @returns {string} output value',
'*',
'* @example',
'* var out = beep( "boop" );',
'* // returns "beepboop"',
'*/',
'function beep( str ) {',
'\treturn "beep" + str;',
'}'
].join( '\n' );

// Register the ESLint rule:
linter.defineRule( 'jsdoc-no-auto-link-without-protocol', rule );

// Lint the code:
result = linter.verify( code, {
'rules': {
'jsdoc-no-auto-link-without-protocol': 'error'
}
});
console.log( result );
/* =>
[
{
'ruleId': 'jsdoc-no-auto-link-without-protocol',
'severity': 2,
'message': 'All automatic links must start with a protocol',
'line': 4,
'column': 3,
'nodeType': null,
'source': '* <foo@bar.com>',
'endLine': 13,
'endColumn': 3
}
]
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

/**
* ESLint rule to require angle-bracketed Markdown links to contain protocols in JSDoc descriptions.
*
* @module @stdlib/_tools/eslint/rules/jsdoc-no-auto-link-without-protocol
*
* @example
* var rule = require( '@stdlib/_tools/eslint/rules/jsdoc-no-auto-link-without-protocol' );
*
* console.log( rule );
*/

// MODULES //

var main = require( './main.js' );


// EXPORTS //

module.exports = main;
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
'use strict';

// MODULES //

var parseJSDoc = require( 'doctrine' ).parse;
var remark = require( 'remark' );
var remarkLint = require( 'remark-lint' );
var remarkPlugin = require( 'remark-lint-no-auto-link-without-protocol' );
var isObject = require( '@stdlib/assert/is-object' );
var findJSDoc = require( '@stdlib/_tools/eslint/utils/find-jsdoc' );


// VARIABLES //

var DOPTS = {
'sloppy': true,
'unwrap': true
};


// MAIN //

/**
* Rule to require angle-bracketed Markdown links to contain protocols in JSDoc descriptions.
*
* @param {Object} context - ESLint context
* @returns {Object} validators
*/
function main( context ) {
var source;
var config;
var lint;

config = {
'plugins': [
remarkLint,
[ remarkPlugin, 'error' ]
]
};
lint = remark().use( config ).processSync;
source = context.getSourceCode();

return {
'FunctionExpression:exit': validate,
'FunctionDeclaration:exit': validate,
'VariableDeclaration:exit': validate,
'ExpressionStatement:exit': validate
};

/**
* Lints JSDoc descriptions.
*
* @private
* @param {ASTNode} node - AST node
*/
function validate( node ) {
var jsdoc;
var vfile;
var ast;

jsdoc = findJSDoc( source, node );
if ( isObject( jsdoc ) ) {
ast = parseJSDoc( jsdoc.value, DOPTS );
if ( ast.description ) {
vfile = lint( ast.description );
if ( vfile.messages.length ) {
reportErrors( vfile.messages, jsdoc.loc );
}
}
}
} // end FUNCTION validate()

/**
* Reports Markdown lint errors.
*
* @private
* @param {ObjectArray} errors - Markdown lint errors
* @param {Object} location - JSDoc location information
*/
function reportErrors( errors, location ) {
var err;
var msg;
var loc;
var i;

for ( i = 0; i < errors.length; i++ ) {
err = errors[ i ];
msg = err.message;
loc = copyLocationInfo( location );
loc.start.line = err.location.start.line + 1; // Note: we assume `/**` is on its own line
loc.start.column = err.location.start.column + 1; // Note: we assume that 1 space separates `*` from JSDoc description content (e.g., `* ## Beep`)
report( msg, loc );
}
} // end FUNCTION reportErrors()

/**
* Copies AST node location info.
*
* @private
* @param {Object} loc - AST node location
* @returns {Object} copied location info
*/
function copyLocationInfo( loc ) {
return {
'start': {
'line': loc.start.line,
'column': loc.start.column
},
'end': {
'line': loc.end.line,
'column': loc.end.column
}
};
} // end FUNCTION copyLocationInfo()

/**
* Reports an error message.
*
* @private
* @param {string} msg - error message
* @param {Object} loc - error location info
*/
function report( msg, loc ) {
context.report({
'node': null,
'message': msg,
'loc': loc
});
} // end FUNCTION report()
} // end FUNCTION main()


// EXPORTS //

module.exports = {
'meta': {
'docs': {
'description': 'require angle-bracketed Markdown links to contain protocols in JSDoc descriptions'
},
'schema': []
},
'create': main
};
Loading

0 comments on commit 6026576

Please sign in to comment.