mirror of
https://github.com/remarkjs/remark-lint.git
synced 2024-09-11 19:57:15 +03:00
Add support for external rules
* Add example rule, and rule object file, to `test/external`; * Add fixtures for external tests; * Move `mdast.js`, `mdast.min.js` to `mdast-lint.js`, `mdast-lint.min.js`; * Add docs for external rules. Closes GH-14.
This commit is contained in:
parent
6d2ba6557e
commit
5162a0982b
20
doc/rules.md
20
doc/rules.md
@ -8,6 +8,7 @@ fix their warnings.
|
||||
|
||||
* [Rules](#rules)
|
||||
|
||||
* [externals](#externals)
|
||||
* [reset](#reset)
|
||||
* [blockquote-indentation](#blockquote-indentation)
|
||||
* [code-block-style](#code-block-style)
|
||||
@ -69,10 +70,27 @@ Remember that rules can always be turned off by
|
||||
passing false. In addition, when reset is given, values can
|
||||
be null or undefined in order to be ignored.
|
||||
|
||||
### externals
|
||||
|
||||
````md
|
||||
<!-- Load more rules -->
|
||||
```json
|
||||
{
|
||||
"externals": ["foo", "bar", "baz"]
|
||||
}
|
||||
```
|
||||
````
|
||||
|
||||
Externals contains a list of extra rules to load.
|
||||
These are, or refer to, an object mapping `ruleId`s to rules.
|
||||
|
||||
Note that in node.js, a string can be given (a module
|
||||
name or a file), but in the browser an object must be passed in.
|
||||
|
||||
### reset
|
||||
|
||||
````md
|
||||
<!-- Explicitly activate rules: -->
|
||||
<!-- Explicitly activate rules: -->
|
||||
```json
|
||||
{
|
||||
"reset": true,
|
||||
|
85
lib/index.js
85
lib/index.js
@ -14,10 +14,20 @@
|
||||
|
||||
var range = require('mdast-range');
|
||||
var zone = require('mdast-zone');
|
||||
var rules = require('./rules');
|
||||
var internals = require('./rules');
|
||||
var sort = require('./sort');
|
||||
var filter = require('./filter');
|
||||
|
||||
/*
|
||||
* Needed for plug-in resolving.
|
||||
*/
|
||||
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var exists = fs && fs.existsSync;
|
||||
var resolve = path && path.resolve;
|
||||
var cwd = process && process.cwd();
|
||||
|
||||
/**
|
||||
* Factory to create a plugin from a rule.
|
||||
*
|
||||
@ -85,6 +95,78 @@ function attachFactory(id, rule, options) {
|
||||
return attach;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require an external. Checks, in this order:
|
||||
*
|
||||
* - `$cwd/$pathlike`;
|
||||
* - `$cwd/$pathlike.js`;
|
||||
* - `$cwd/node_modules/$pathlike`;
|
||||
* - `$pathlike`.
|
||||
*
|
||||
* Where `$cwd` is the current working directory.
|
||||
*
|
||||
* @example
|
||||
* var plugin = findPlugin('foo');
|
||||
*
|
||||
* @throws {Error} - Fails when `pathlike` cannot be
|
||||
* resolved.
|
||||
* @param {string} pathlike - Reference to external.
|
||||
* @return {Object} - Result of `require`ing external.
|
||||
*/
|
||||
function loadExternal(pathlike) {
|
||||
var local = resolve(cwd, pathlike);
|
||||
var current = resolve(cwd, 'node_modules', pathlike);
|
||||
var plugin;
|
||||
|
||||
if (exists(local) || exists(local + '.js')) {
|
||||
plugin = local;
|
||||
/* istanbul ignore else - for globals */
|
||||
} else if (exists(current)) {
|
||||
plugin = current;
|
||||
} else {
|
||||
plugin = pathlike;
|
||||
}
|
||||
|
||||
return require(plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all externals. Merges them into a single rule
|
||||
* object.
|
||||
*
|
||||
* In node, accepts externals as strings, otherwise,
|
||||
* externals should be a list of objects.
|
||||
*
|
||||
* @param {Array.<string|Object>} externals
|
||||
* @return {Array.<Object>}
|
||||
* @throws {Error} - When an external cannot be resolved.
|
||||
*/
|
||||
function loadExternals(externals) {
|
||||
var index = -1;
|
||||
var rules = {};
|
||||
var external;
|
||||
var ruleId;
|
||||
var mapping = externals ? externals.concat() : [];
|
||||
var length;
|
||||
|
||||
mapping.push(internals);
|
||||
length = mapping.length;
|
||||
|
||||
while (++index < length) {
|
||||
external = mapping[index];
|
||||
|
||||
if (typeof external === 'string') {
|
||||
external = loadExternal(external);
|
||||
}
|
||||
|
||||
for (ruleId in external) {
|
||||
rules[ruleId] = external[ruleId];
|
||||
}
|
||||
}
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lint attacher.
|
||||
*
|
||||
@ -105,6 +187,7 @@ function attachFactory(id, rule, options) {
|
||||
function lint(mdast, options) {
|
||||
var settings = options || {};
|
||||
var reset = settings.reset;
|
||||
var rules = loadExternals(settings.external);
|
||||
var id;
|
||||
var setting;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.mdast = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.mdastLint = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
'use strict';
|
||||
|
||||
module.exports = require('./lib');
|
||||
@ -94,10 +94,20 @@ module.exports = attacher;
|
||||
|
||||
var range = require('mdast-range');
|
||||
var zone = require('mdast-zone');
|
||||
var rules = require('./rules');
|
||||
var internals = require('./rules');
|
||||
var sort = require('./sort');
|
||||
var filter = require('./filter');
|
||||
|
||||
/*
|
||||
* Needed for plug-in resolving.
|
||||
*/
|
||||
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var exists = fs && fs.existsSync;
|
||||
var resolve = path && path.resolve;
|
||||
var cwd = process && process.cwd();
|
||||
|
||||
/**
|
||||
* Factory to create a plugin from a rule.
|
||||
*
|
||||
@ -165,6 +175,78 @@ function attachFactory(id, rule, options) {
|
||||
return attach;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require an external. Checks, in this order:
|
||||
*
|
||||
* - `$cwd/$pathlike`;
|
||||
* - `$cwd/$pathlike.js`;
|
||||
* - `$cwd/node_modules/$pathlike`;
|
||||
* - `$pathlike`.
|
||||
*
|
||||
* Where `$cwd` is the current working directory.
|
||||
*
|
||||
* @example
|
||||
* var plugin = findPlugin('foo');
|
||||
*
|
||||
* @throws {Error} - Fails when `pathlike` cannot be
|
||||
* resolved.
|
||||
* @param {string} pathlike - Reference to external.
|
||||
* @return {Object} - Result of `require`ing external.
|
||||
*/
|
||||
function loadExternal(pathlike) {
|
||||
var local = resolve(cwd, pathlike);
|
||||
var current = resolve(cwd, 'node_modules', pathlike);
|
||||
var plugin;
|
||||
|
||||
if (exists(local) || exists(local + '.js')) {
|
||||
plugin = local;
|
||||
/* istanbul ignore else - for globals */
|
||||
} else if (exists(current)) {
|
||||
plugin = current;
|
||||
} else {
|
||||
plugin = pathlike;
|
||||
}
|
||||
|
||||
return require(plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all externals. Merges them into a single rule
|
||||
* object.
|
||||
*
|
||||
* In node, accepts externals as strings, otherwise,
|
||||
* externals should be a list of objects.
|
||||
*
|
||||
* @param {Array.<string|Object>} externals
|
||||
* @return {Array.<Object>}
|
||||
* @throws {Error} - When an external cannot be resolved.
|
||||
*/
|
||||
function loadExternals(externals) {
|
||||
var index = -1;
|
||||
var rules = {};
|
||||
var external;
|
||||
var ruleId;
|
||||
var mapping = externals ? externals.concat() : [];
|
||||
var length;
|
||||
|
||||
mapping.push(internals);
|
||||
length = mapping.length;
|
||||
|
||||
while (++index < length) {
|
||||
external = mapping[index];
|
||||
|
||||
if (typeof external === 'string') {
|
||||
external = loadExternal(external);
|
||||
}
|
||||
|
||||
for (ruleId in external) {
|
||||
rules[ruleId] = external[ruleId];
|
||||
}
|
||||
}
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lint attacher.
|
||||
*
|
||||
@ -185,6 +267,7 @@ function attachFactory(id, rule, options) {
|
||||
function lint(mdast, options) {
|
||||
var settings = options || {};
|
||||
var reset = settings.reset;
|
||||
var rules = loadExternals(settings.external);
|
||||
var id;
|
||||
var setting;
|
||||
|
||||
@ -322,7 +405,7 @@ function lint(mdast, options) {
|
||||
|
||||
module.exports = lint;
|
||||
|
||||
},{"./filter":2,"./rules":18,"./sort":58,"mdast-range":64,"mdast-zone":65}],4:[function(require,module,exports){
|
||||
},{"./filter":2,"./rules":18,"./sort":58,"fs":undefined,"mdast-range":64,"mdast-zone":65,"path":undefined}],4:[function(require,module,exports){
|
||||
/**
|
||||
* @author Titus Wormer
|
||||
* @copyright 2015 Titus Wormer. All rights reserved.
|
1
mdast-lint.min.js
vendored
Normal file
1
mdast-lint.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
mdast.min.js
vendored
1
mdast.min.js
vendored
File diff suppressed because one or more lines are too long
@ -55,8 +55,8 @@
|
||||
"make": "npm run lint && npm run test-coverage",
|
||||
"build-rules": "node script/build-rule-documentation.js",
|
||||
"build-md": "mdast . LICENSE --output",
|
||||
"build-bundle": "browserify index.js -s mdast > mdast.js",
|
||||
"postbuild-bundle": "esmangle mdast.js > mdast.min.js",
|
||||
"build-bundle": "browserify index.js --bare -s mdastLint > mdast-lint.js",
|
||||
"postbuild-bundle": "esmangle mdast-lint.js > mdast-lint.min.js",
|
||||
"build": "npm run build-rules && npm run build-md && npm run build-bundle",
|
||||
"prepublish": "npm run build"
|
||||
}
|
||||
|
@ -2,5 +2,9 @@
|
||||
"reset": {
|
||||
"description": "By default, all rules are turned on unless explicitly set to `false`.\nWhen `reset: true`, the opposite is true: all rules are turned off,\nunless when given a non-nully and non-false value.\n\nOptions: `boolean`, default: `false`.",
|
||||
"example": " <!-- Explicitly activate rules: -->\n ```json\n {\n \"reset\": true,\n \"final-newline\": true\n }\n ```\n"
|
||||
},
|
||||
"externals": {
|
||||
"description": "Externals contains a list of extra rules to load.\nThese are, or refer to, an object mapping `ruleId`s to rules.\n\nNote that in node.js, a string can be given (a module\nname or a file), but in the browser an object must be passed in.",
|
||||
"example": " <!-- Load more rules -->\n ```json\n {\n \"externals\": [\"foo\", \"bar\", \"baz\"]\n }\n ```\n"
|
||||
}
|
||||
}
|
||||
|
16
test/external/index.js
vendored
Normal file
16
test/external/index.js
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @author Titus Wormer
|
||||
* @copyright 2015 Titus Wormer. All rights reserved.
|
||||
* @module External
|
||||
* @fileoverview Map of example external rules.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* Expose.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
'no-lorem': require('./no-lorem')
|
||||
};
|
40
test/external/no-lorem.js
vendored
Normal file
40
test/external/no-lorem.js
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @author Titus Wormer
|
||||
* @copyright 2015 Titus Wormer. All rights reserved.
|
||||
* @module no-tabs
|
||||
* @fileoverview
|
||||
* Warn when `lorem` is used in a document.
|
||||
* @example
|
||||
* <!-- Invalid: -->
|
||||
* lorem
|
||||
*
|
||||
* <!-- Valid: -->
|
||||
* ipsum
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Warn when `lorem` is used in a document.
|
||||
*
|
||||
* @param {Node} ast - Root node.
|
||||
* @param {File} file - Virtual file.
|
||||
* @param {*} preferred - Ignored.
|
||||
* @param {Function} done - Callback.
|
||||
*/
|
||||
function noLorem(ast, file, preferred, done) {
|
||||
var content = file.toString();
|
||||
var expression = /\blorem\b/gi;
|
||||
|
||||
while (expression.exec(content)) {
|
||||
file.warn('Do not use lorem', file.offsetToPosition(expression.lastIndex));
|
||||
}
|
||||
|
||||
done();
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose.
|
||||
*/
|
||||
|
||||
module.exports = noLorem;
|
1
test/fixtures/lorem-invalid.md
vendored
Normal file
1
test/fixtures/lorem-invalid.md
vendored
Normal file
@ -0,0 +1 @@
|
||||
Lorem.
|
1
test/fixtures/lorem-valid.md
vendored
Normal file
1
test/fixtures/lorem-valid.md
vendored
Normal file
@ -0,0 +1 @@
|
||||
Ipsum.
|
@ -206,6 +206,60 @@ describe('mdast-lint', function () {
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* Validate external rules.
|
||||
*/
|
||||
|
||||
describe('External', function () {
|
||||
describe('Load external rules with require', function () {
|
||||
assertFile('lorem-invalid.md', [
|
||||
'lorem-invalid.md:1:1: Do not use lorem'
|
||||
], null, {
|
||||
'external': ['test/external']
|
||||
});
|
||||
});
|
||||
|
||||
describe('Load external rules with require and without `.js` extension', function () {
|
||||
assertFile('lorem-invalid.md', [
|
||||
'lorem-invalid.md:1:1: Do not use lorem'
|
||||
], null, {
|
||||
'external': ['test/external/index']
|
||||
});
|
||||
});
|
||||
|
||||
describe('Load external rules with require and with `.js` extension', function () {
|
||||
assertFile('lorem-invalid.md', [
|
||||
'lorem-invalid.md:1:1: Do not use lorem'
|
||||
], null, {
|
||||
'external': ['test/external/index.js']
|
||||
});
|
||||
});
|
||||
|
||||
describe('Load local external rules', function () {
|
||||
it('should fail on invalid external rules', function () {
|
||||
assert.throws(function () {
|
||||
process('lorem-valid.md', {
|
||||
'external': ['mdast']
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Load external rules by passing in', function () {
|
||||
var external = require('./external');
|
||||
|
||||
assertFile('lorem-invalid.md', [
|
||||
'lorem-invalid.md:1:1: Do not use lorem'
|
||||
], null, {
|
||||
'external': [external]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* Validate inline en- and disabling.
|
||||
*/
|
||||
|
||||
describe('Comments', function () {
|
||||
describe('Disable and re-enable rules based on markers', function () {
|
||||
assertFile('comments-disable.md', [
|
||||
|
Loading…
Reference in New Issue
Block a user