Skip to content

Commit 571abb9

Browse files
committed
Refactor to allow instantiating an ESLint engine before linting
1 parent 260625e commit 571abb9

File tree

5 files changed

+179
-113
lines changed

5 files changed

+179
-113
lines changed

tools/remark/plugins/remark-lint-eslint/README.md

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
var plugin = require( '/path/to/@stdlib/tools/remark/plugins/remark-lint-eslint' );
1212
```
1313

14-
#### plugin( tree, file, options )
14+
#### plugin()
1515

16-
Provided a Markdown abstract syntax `tree`, lints JavaScript code blocks.
16+
A [remark][remark] plugin, which when provided a Markdown abstract syntax `tree`, lints JavaScript code blocks using the default [ESLint][eslint] configuration.
1717

1818
``` javascript
1919
var remark = require( 'remark' );
@@ -25,7 +25,21 @@ var linter = remark().use( plugin ).procecssSync;
2525
var vfile = linter( '``` javascript\nvar beep = \'boop\';\n```' );
2626
```
2727

28-
The plugin recognizes the following `options`:
28+
#### plugin.factory( \[options\] )
29+
30+
Returns a configured [remark][remark] plugin for linting JavaScript code blocks.
31+
32+
``` javascript
33+
var remark = require( 'remark' );
34+
35+
// Create a synchronous Markdown text linter:
36+
var linter = remark().use( plugin.factory() ).procecssSync;
37+
38+
// Lint Markdown:
39+
var vfile = linter( '``` javascript\nvar beep = \'boop\';\n```' );
40+
```
41+
42+
The function recognizes the following `options`:
2943

3044
* __config__: path to an [ESLint][eslint] configuration file. A configuration path is resolved relative to the current working directory of the calling process.
3145

@@ -39,8 +53,10 @@ var opts = {
3953
'config': '/path/to/.eslintrc'
4054
};
4155

56+
var lint = plugin.factory( opts );
57+
4258
// Create a synchronous Markdown text linter:
43-
var linter = remark().use( plugin, opts ).procecssSync;
59+
var linter = remark().use( lint ).procecssSync;
4460

4561
// Lint Markdown:
4662
var vfile = linter( '``` javascript\nvar beep = \'boop\';\n```' );
@@ -162,7 +178,7 @@ var join = require( 'path' ).join;
162178
var resolve = require( 'path' ).resolve;
163179
var remark = require( 'remark' );
164180
var readFileSync = require( '@stdlib/fs/read-file' ).sync;
165-
var plugin = require( '/path/to/@stdlib/tools/remark/plugins/remark-lint-eslint' );
181+
var factory = require( '/path/to/@stdlib/tools/remark/plugins/remark-lint-eslint' ).factory;
166182

167183
// Define path to an ESLint config file:
168184
var config = resolve( __dirname, '..', '..', '..', '..', 'etc', 'eslint', '.eslintrc.markdown.js' );
@@ -176,8 +192,11 @@ var opts = {
176192
'config': config
177193
};
178194

195+
// Create a plugin:
196+
var plugin = factory( opts );
197+
179198
// Lint code blocks:
180-
var out = remark().use( plugin, opts ).processSync( file.toString() ); // eslint-disable-line no-sync
199+
var out = remark().use( plugin ).processSync( file.toString() ); // eslint-disable-line no-sync
181200

182201
console.log( out );
183202
```

tools/remark/plugins/remark-lint-eslint/examples/index.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ var join = require( 'path' ).join;
44
var resolve = require( 'path' ).resolve;
55
var remark = require( 'remark' );
66
var readFileSync = require( '@stdlib/fs/read-file' ).sync;
7-
var plugin = require( './../lib' );
7+
var factory = require( './../lib' ).factory;
88

99
// Define path to an ESLint config file:
1010
var config = resolve( __dirname, '..', '..', '..', '..', '..', 'etc', 'eslint', '.eslintrc.markdown.js' );
@@ -18,7 +18,10 @@ var opts = {
1818
'config': config
1919
};
2020

21+
// Create a plugin:
22+
var plugin = factory( opts );
23+
2124
// Lint code blocks:
22-
var out = remark().use( plugin, opts ).processSync( file.toString() ); // eslint-disable-line no-sync
25+
var out = remark().use( plugin ).processSync( file.toString() ); // eslint-disable-line no-sync
2326

2427
console.log( out );
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
'use strict';
2+
3+
// MODULES //
4+
5+
var resolve = require( 'path' ).resolve;
6+
var rule = require( 'unified-lint-rule' );
7+
var visit = require( 'unist-util-visit' );
8+
var Engine = require( 'eslint' ).CLIEngine;
9+
var cwd = require( '@stdlib/utils/cwd' );
10+
var hasOwnProp = require( '@stdlib/assert/has-own-property' );
11+
var isObject = require( '@stdlib/assert/is-plain-object' );
12+
var transformHTML = require( './transform_html.js' );
13+
14+
15+
// MAIN //
16+
17+
/**
18+
* Returns a plugin for linting Markdown code blocks using ESLint.
19+
*
20+
* @param {Object} [options] - options
21+
* @param {string} [options.config] - path to an ESLint configuration file
22+
* @throws {TypeError} options arguments must be an object
23+
* @returns {Function} plugin
24+
*
25+
* @example
26+
* var remark = require( 'remark' );
27+
*
28+
* var plugin = factory();
29+
* var linter = remark().use( plugin ).processSync;
30+
*
31+
* var vfile = linter( '``` javascript\nvar beep = \'boop\';\n```' );
32+
*/
33+
function factory( options ) {
34+
var opts;
35+
var cli;
36+
37+
opts = {};
38+
if ( arguments.length ) {
39+
if ( !isObject( options ) ) {
40+
throw new TypeError( 'invalid input argument. Options argument must be an object. Value: `'+options+'`.' );
41+
}
42+
if ( hasOwnProp( options, 'config' ) ) {
43+
opts.configFile = resolve( cwd(), options.config );
44+
}
45+
// TODO: add support for other ESLint options
46+
}
47+
cli = new Engine( opts );
48+
return rule( 'remark-lint:eslint', lint );
49+
50+
/**
51+
* Lints Markdown code blocks using ESLint.
52+
*
53+
* @private
54+
* @param {Object} tree - abstract syntax tree (AST)
55+
* @param {Object} file - file being linted
56+
* @param {Object} options - options
57+
*
58+
* @example
59+
* var remark = require( 'remark' );
60+
* var linter = remark().use( lint ).processSync;
61+
*
62+
* var vfile = linter( '``` javascript\nvar beep = \'boop\';\n```' );
63+
*/
64+
function lint( tree, file ) {
65+
visit( tree, 'code', onNode );
66+
67+
/**
68+
* Callback invoked upon encountering a code block.
69+
*
70+
* @private
71+
* @param {Object} node - AST node
72+
* @param {number} idx - position of node in parent
73+
* @param {Object} parent - parent AST node
74+
* @returns {void}
75+
*/
76+
function onNode( node, idx, parent ) {
77+
var comments;
78+
var comment;
79+
var report;
80+
var result;
81+
var offset;
82+
var prev;
83+
var code;
84+
var msg;
85+
var str;
86+
var i;
87+
var j;
88+
89+
if ( node.lang === 'javascript' || node.lang === 'js' ) {
90+
// Look for HTML comments immediately preceding a code block which may contain ESLint configuration...
91+
idx -= 1;
92+
prev = parent.children[ idx ];
93+
comments = [];
94+
while ( prev && prev.type === 'html' ) {
95+
comment = transformHTML( prev.value );
96+
if ( !comment ) {
97+
break;
98+
}
99+
if ( comment === '/* eslint-skip */' ) {
100+
return;
101+
}
102+
comments.unshift( comment );
103+
idx -= 1;
104+
prev = parent.children[ idx ];
105+
}
106+
offset = comments.length;
107+
comments.push( node.value );
108+
code = comments.join( '\n' );
109+
110+
// Lint the code block...
111+
report = cli.executeOnText( code );
112+
for ( i = 0; i < report.results.length; i++ ) {
113+
result = report.results[ i ];
114+
result.filePath = file.path || result.filePath;
115+
for ( j = 0; j < result.messages.length; j++ ) {
116+
msg = result.messages[ j ];
117+
str = '';
118+
str += (msg.line-offset) + ':' + msg.column;
119+
str += ' ';
120+
if ( msg.severity === 2 ) {
121+
str += 'error';
122+
} else {
123+
str += 'warning';
124+
}
125+
str += ' ';
126+
str += msg.message;
127+
str += ' ';
128+
str += msg.ruleId;
129+
file.message( str, node );
130+
}
131+
}
132+
}
133+
} // end FUNCTION onNode()
134+
} // end FUNCTION lint()
135+
} // end FUNCTION factory()
136+
137+
138+
// EXPORTS //
139+
140+
module.exports = factory;

tools/remark/plugins/remark-lint-eslint/lib/index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,14 @@
1616

1717
// MODULES //
1818

19+
var setReadOnly = require( '@stdlib/utils/define-read-only-property' );
1920
var plugin = require( './main.js' );
21+
var factory = require( './factory.js' );
22+
23+
24+
// MAIN //
25+
26+
setReadOnly( plugin, 'factory', factory );
2027

2128

2229
// EXPORTS //

tools/remark/plugins/remark-lint-eslint/lib/main.js

Lines changed: 2 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -2,112 +2,9 @@
22

33
// MODULES //
44

5-
var resolve = require( 'path' ).resolve;
6-
var rule = require( 'unified-lint-rule' );
7-
var visit = require( 'unist-util-visit' );
8-
var Engine = require( 'eslint' ).CLIEngine;
9-
var cwd = require( '@stdlib/utils/cwd' );
10-
var hasOwnProp = require( '@stdlib/assert/has-own-property' );
11-
var transformHTML = require( './transform_html.js' );
12-
13-
14-
// MAIN //
15-
16-
/**
17-
* Lints Markdown code blocks using ESLint.
18-
*
19-
* @param {Object} tree - abstract syntax tree (AST)
20-
* @param {Object} file - file being linted
21-
* @param {Object} options - options
22-
* @param {string} [options.config] - path to an ESLint configuration file
23-
*
24-
* @example
25-
* var remark = require( 'remark' );
26-
* var linter = remark().use( lint ).processSync;
27-
*
28-
* var vfile = linter( '``` javascript\nvar beep = \'boop\';\n```' );
29-
*/
30-
function lint( tree, file, options ) {
31-
var opts;
32-
var cli;
33-
34-
opts = {};
35-
if ( hasOwnProp( options, 'config' ) ) {
36-
opts.configFile = resolve( cwd(), options.config );
37-
}
38-
cli = new Engine( opts );
39-
visit( tree, 'code', onNode );
40-
41-
/**
42-
* Callback invoked upon encountering a code block.
43-
*
44-
* @private
45-
* @param {Object} node - AST node
46-
* @param {number} idx - position of node in parent
47-
* @param {Object} parent - parent AST node
48-
* @returns {void}
49-
*/
50-
function onNode( node, idx, parent ) {
51-
var comments;
52-
var comment;
53-
var report;
54-
var result;
55-
var offset;
56-
var prev;
57-
var code;
58-
var msg;
59-
var str;
60-
var i;
61-
var j;
62-
63-
if ( node.lang === 'javascript' || node.lang === 'js' ) {
64-
// Look for HTML comments immediately preceding a code block which may contain ESLint configuration...
65-
idx -= 1;
66-
prev = parent.children[ idx ];
67-
comments = [];
68-
while ( prev && prev.type === 'html' ) {
69-
comment = transformHTML( prev.value );
70-
if ( !comment ) {
71-
break;
72-
}
73-
if ( comment === '/* eslint-skip */' ) {
74-
return;
75-
}
76-
comments.unshift( comment );
77-
idx -= 1;
78-
prev = parent.children[ idx ];
79-
}
80-
offset = comments.length;
81-
comments.push( node.value );
82-
code = comments.join( '\n' );
83-
84-
// Lint the code block...
85-
report = cli.executeOnText( code );
86-
for ( i = 0; i < report.results.length; i++ ) {
87-
result = report.results[ i ];
88-
result.filePath = file.path || result.filePath;
89-
for ( j = 0; j < result.messages.length; j++ ) {
90-
msg = result.messages[ j ];
91-
str = '';
92-
str += (msg.line-offset) + ':' + msg.column;
93-
str += ' ';
94-
if ( msg.severity === 2 ) {
95-
str += 'error';
96-
} else {
97-
str += 'warning';
98-
}
99-
str += ' ';
100-
str += msg.message;
101-
str += ' ';
102-
str += msg.ruleId;
103-
file.message( str, node );
104-
}
105-
}
106-
}
107-
} // end FUNCTION onNode()
108-
} // end FUNCTION lint()
5+
var factory = require( './factory.js' );
1096

1107

1118
// EXPORTS //
1129

113-
module.exports = rule( 'remark-lint:eslint', lint );
10+
module.exports = factory();

0 commit comments

Comments
 (0)