Add support for passing severities

Previously, messages could be either turned on (as warnings), or
turned off.  This update adds support for setting these through an
integer (`1` and `0` respectivly), in array form:

```js
remark().use(lint, {finalNewline:[0]});
```

Additionally, the integer `2` can be passed to turn a message into
a fatal error:

```js
remark().use(lint, {maximumLineLength: [2, 72]});
```

Closes GH-65.
This commit is contained in:
Titus Wormer 2016-08-15 12:36:55 +02:00
parent 9bdd69e653
commit 77709f56ac
4 changed files with 137 additions and 29 deletions

View File

@ -6,7 +6,7 @@ This document describes all (57)
available rules, what they check for, examples of
what they warn for, and how to fix their warnings.
Note: both camel-cased and dash-cases versions of rule ids
Both camel-cased and dash-cases versions of rule ids
are supported in configuration objects:
```json
@ -23,6 +23,33 @@ are supported in configuration objects:
}
```
Additionally, each rule can be configured with a severity
instead of a boolean as well. The following is handled the
same as passing `false`:
```json
{
"final-newline": [0]
}
```
...and passing `[1]` is as passing `true`. To trigger an
error instead of a warning, pass `2`:
```json
{
"final-newline": [2]
}
```
Its also possible to pass both a severity and configuration:
```json
{
"maximum-line-length": [2, 70]
}
```
## Table of Contents
- [reset](#reset)

View File

@ -51,30 +51,17 @@ function lint(remark, options) {
var disable = [];
var known = [];
var pipeline = trough();
var setting;
var config;
var id;
/* Add each rule. */
for (id in rules) {
setting = settings[id];
known.push(id);
config = coerce(id, settings[id], reset);
if (setting != null) {
/* Pass turned on rules `undefined`. */
if (reset && setting === true) {
setting = undefined;
}
(config[0] ? enable : disable).push(id);
if (setting === false) {
setting = undefined;
disable.push(id);
} else {
enable.push(id);
}
}
pipeline.use(ruleFactory(id, rules[id], setting));
pipeline.use(ruleFactory(id, rules[id], config[0], config[1]));
}
/* Run all rules. */
@ -155,11 +142,13 @@ function loadExternals(externals) {
*
* @param {string} id - Identifier.
* @param {Function} rule - Rule
* @param {number} severity - Severity.
* @param {*} options - Options for respective rule.
* @return {Function} - Trough ware.
*/
function ruleFactory(id, rule, options) {
function ruleFactory(id, rule, severity, options) {
var fn = wrapped(rule);
var fatal = severity === 2;
return function (ast, file, next) {
var scope = file.namespace('remark-lint');
@ -169,10 +158,13 @@ function ruleFactory(id, rule, options) {
fn(ast, file, options, function (err) {
var messages = file.messages;
var message;
while (scope.index < messages.length) {
messages[scope.index].ruleId = id;
messages[scope.index].source = SOURCE;
message = messages[scope.index];
message.ruleId = id;
message.source = SOURCE;
message.fatal = fatal;
scope.index++;
}
@ -182,13 +174,7 @@ function ruleFactory(id, rule, options) {
};
}
/**
* Helper to ensure ruleIds are dash-cased instead of
* camel-cased.
*
* @param {Object} source - Original settings.
* @return {Object} - Dash-cased settings.
*/
/* Ensure ruleIds are dash-cased. */
function decamelizeSettings(source) {
var result = {};
var key;
@ -199,3 +185,35 @@ function decamelizeSettings(source) {
return result;
}
/* Coerce a value to a severity--options tuple. */
function coerce(name, value, reset) {
var def = reset ? 0 : 1;
var result;
if (value == null) {
result = [def];
} else if (typeof value === 'boolean') {
result = [value];
} else if (
typeof value === 'object' &&
(typeof value[0] === 'number' || typeof value[0] === 'boolean')
) {
result = value.concat();
} else {
result = [1, value];
}
if (typeof result[0] === 'boolean') {
result[0] = result[0] ? 1 : 0;
}
if (result[0] < 0 || result[0] > 2) {
throw new Error(
'Invalid severity `' + result[0] + '` for `' + name + '`, ' +
'expected 0, 1, or 2'
);
}
return result;
}

View File

@ -104,7 +104,7 @@ var markdown = remark().use(toc);
'available rules, what they check for, examples of',
'what they warn for, and how to fix their warnings.',
'',
'Note: both camel-cased and dash-cases versions of rule ids',
'Both camel-cased and dash-cases versions of rule ids',
'are supported in configuration objects:',
'',
'```json',
@ -121,6 +121,33 @@ var markdown = remark().use(toc);
'}',
'```',
'',
'Additionally, each rule can be configured with a severity',
'instead of a boolean as well. The following is handled the',
'same as passing `false`:',
'',
'```json',
'{',
' "final-newline": [0]',
'}',
'```',
'',
'...and passing `[1]` is as passing `true`. To trigger an',
'error instead of a warning, pass `2`:',
'',
'```json',
'{',
' "final-newline": [2]',
'}',
'```',
'',
'Its also possible to pass both a severity and configuration:',
'',
'```json',
'{',
' "maximum-line-length": [2, 70]',
'}',
'```',
'',
'## Table of Contents',
'',
'## `reset`',

View File

@ -85,6 +85,42 @@ test('core', function (t) {
});
});
t.test('should support a list with a severity', function (st) {
st.plan(3);
remark()
.use(lint, {reset: true, finalNewline: [2]})
.process('.', function (err, file) {
st.ifErr(err, 'should not fail');
st.equal(
file.messages.join(),
'1:1: Missing newline character at end of file',
'should trigger fatally (1)'
);
st.equal(file.messages[0].fatal, true, 'should trigger fatally (2)');
});
});
t.test('should fail on invalid severities', function (st) {
st.throws(
function () {
remark().use(lint, {finalNewline: [3]});
},
/^Error: Invalid severity `3` for `final-newline`, expected 0, 1, or 2$/,
'should throw when too high'
);
st.throws(
function () {
remark().use(lint, {finalNewline: [-1]});
},
/^Error: Invalid severity `-1` for `final-newline`, expected 0, 1, or 2$/,
'should throw too low'
);
st.end();
});
t.end();
});