Socket
Socket
Sign inDemoInstall

remark-lint

Package Overview
Dependencies
Maintainers
1
Versions
40
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

remark-lint - npm Package Compare versions

Comparing version 4.0.2 to 4.1.0

lib/rules.js

74

history.md

@@ -5,2 +5,11 @@ <!--remark setext-->

4.1.0 / 2016-08-02
==================
* Add better external rule support, and refactor module ([`8dedd19`](https://github.com/wooorm/remark-lint/commit/8dedd19))
* Refactor code-style to use `xo` ([`60907ad`](https://github.com/wooorm/remark-lint/commit/60907ad))
* Add support for rules as any trough ware ([`d272ff2`](https://github.com/wooorm/remark-lint/commit/d272ff2))
* Add `load-plugin` to load external rules ([`31f82b2`](https://github.com/wooorm/remark-lint/commit/31f82b2))
* Update dependencies ([`5a70271`](https://github.com/wooorm/remark-lint/commit/5a70271))
4.0.2 / 2016-07-06

@@ -128,66 +137,1 @@ ==================

==================
* Update dependencies ([`4936d35`](https://github.com/wooorm/remark-lint/commit/4936d35))
0.4.5 / 2015-08-08
==================
* Fix typo in definition-case warning ([`490ac16`](https://github.com/wooorm/remark-lint/commit/490ac16))
0.4.4 / 2015-08-05
==================
* Remove maximum-line-length warnings on definitions ([`1119ca8`](https://github.com/wooorm/remark-lint/commit/1119ca8))
* Add `linter-markdown` to `readme.md` ([`7de8847`](https://github.com/wooorm/remark-lint/commit/7de8847))
0.4.3 / 2015-08-04
==================
* Fix block-quotes without children ([`a9aaff7`](https://github.com/wooorm/remark-lint/commit/a9aaff7))
* Update mdast dev-dependency ([`34df79c`](https://github.com/wooorm/remark-lint/commit/34df79c))
* Add vfile as a dev-dependency ([`84d1ce3`](https://github.com/wooorm/remark-lint/commit/84d1ce3))
0.4.2 / 2015-07-12
==================
* Remove peer-dependencies ([`8d70fcf`](https://github.com/wooorm/remark-lint/commit/8d70fcf))
0.4.1 / 2015-07-05
==================
* Remove component support ([`58d7e6b`](https://github.com/wooorm/remark-lint/commit/58d7e6b))
* Refactor to externalise `lib/utilities/` ([`eb78529`](https://github.com/wooorm/remark-lint/commit/eb78529))
0.4.0 / 2015-06-29
==================
* Add gap support ([`136e760`](https://github.com/wooorm/remark-lint/commit/136e760))
* Update mdast ([`2d122e4`](https://github.com/wooorm/remark-lint/commit/2d122e4))
0.3.0 / 2015-06-20
==================
* Add checkbox-content-indent rule ([`7b55519`](https://github.com/wooorm/remark-lint/commit/7b55519))
* Fix dot-files from being read as fixtures ([`ecbec2c`](https://github.com/wooorm/remark-lint/commit/ecbec2c))
* Add checkbox-character-style rule ([`7ed4579`](https://github.com/wooorm/remark-lint/commit/7ed4579))
* Fix tests for invalid position given my mdast-range ([`55d1128`](https://github.com/wooorm/remark-lint/commit/55d1128))
* Add missing jsdoc comment ([`63b83b9`](https://github.com/wooorm/remark-lint/commit/63b83b9))
* Update eslint ([`a3b0829`](https://github.com/wooorm/remark-lint/commit/a3b0829))
* Update mdast, mdast-yaml-config ([`52bac04`](https://github.com/wooorm/remark-lint/commit/52bac04))
0.2.0 / 2015-06-13
==================
* Remove mdast-usage, add mdast-yaml-config as dependencies ([`053674f`](https://github.com/wooorm/remark-lint/commit/053674f))
* Add images to blacklist for maximum-line-length ([`ba6d270`](https://github.com/wooorm/remark-lint/commit/ba6d270))
* Refactor to use rawgit references to images in `readme.md` ([`3f6344c`](https://github.com/wooorm/remark-lint/commit/3f6344c))
* Add support for external rules ([`5162a09`](https://github.com/wooorm/remark-lint/commit/5162a09))
* Refactor additional, fileless, rules support ([`6d2ba65`](https://github.com/wooorm/remark-lint/commit/6d2ba65))
* Adds support for automatic doc generation for file-less rules ([`29965a3`](https://github.com/wooorm/remark-lint/commit/29965a3))
* Add `reset` docs to rule generation script ([`77b8bfd`](https://github.com/wooorm/remark-lint/commit/77b8bfd))
* Adds `reset` rule to docs ([`90a5f8a`](https://github.com/wooorm/remark-lint/commit/90a5f8a))
* Update wording re rules in `readme.md` ([`00d9ba4`](https://github.com/wooorm/remark-lint/commit/00d9ba4))
* Update rule count in `readme.md` ([`f937cf4`](https://github.com/wooorm/remark-lint/commit/f937cf4))
0.1.0 / 2015-06-11
==================

@@ -14,162 +14,94 @@ /**

/*
* Constants.
*/
var SOURCE = 'remark-lint';
/*
* Dependencies.
*/
/* Dependencies. */
var decamelize = require('decamelize');
var sort = require('vfile-sort');
var control = require('remark-message-control');
var loadPlugin = require('load-plugin');
var trough = require('trough');
var wrapped = require('wrapped');
var internals = require('./rules');
var npmPrefix = require('npm-prefix')();
/*
* Needed for plug-in resolving.
*/
/* Expose. */
module.exports = lint;
var path = require('path');
var fs = require('fs');
var exists = fs && fs.existsSync;
var resolve = path && path.resolve;
var isWindows;
var isElectron;
var isGlobal;
var globals;
var cwd;
/* Constants. */
var SOURCE = 'remark-lint';
var MODULES = 'node_modules';
/* istanbul ignore else */
if (typeof global !== 'undefined') {
/* global global */
cwd = global.process.cwd();
/* Detect whether we’re running as a globally installed package. */
isWindows = global.process.platform === 'win32';
isElectron = global.process.versions.electron !== undefined;
isGlobal = isElectron || global.process.argv[1].indexOf(npmPrefix) === 0;
/* istanbul ignore next */
globals = resolve(npmPrefix, isWindows ? '' : 'lib', MODULES);
}
/**
* Factory to create a plugin from a rule.
* Lint attacher.
*
* By default, all rules are turned on unless explicitly
* set to `false`. When `reset: true`, the opposite is
* true: all rules are turned off, unless when given
* a non-nully and non-false value.
*
* @example
* attachFactory('foo', console.log, false)() // null
* attachFactory('foo', console.log, {})() // plugin
* var processor = lint(remark, {
* html: false // Ignore HTML warnings.
* });
*
* @param {string} id - Identifier.
* @param {Function} rule - Rule
* @param {*} options - Options for respective rule.
* @return {Function} - See `attach` below.
* @param {Remark} remark - Host object.
* @param {Object?} options - Hash of rule names mapping to
* rule options.
*/
function attachFactory(id, rule, options) {
/**
* Attach the rule to a remark instance, unless `false`
* is passed as an option.
*
* @return {Function?} - See `plugin` below.
*/
function attach() {
/**
* Attach the rule to a remark instance, unless `false`
* is passed as an option.
*
* @param {Node} ast - Root node.
* @param {File} [file] - Virtual file.
* @param {Function} next - Signal end.
*/
function plugin(ast, file, next) {
var scope = file.namespace('remark-lint');
function lint(remark, options) {
var settings = decamelizeSettings(options || {});
var rules = loadExternals(settings.external);
var reset = options && options.reset;
var enable = [];
var disable = [];
var known = [];
var pipeline = trough();
var setting;
var id;
/*
* Track new messages per file.
*/
/* Add each rule. */
for (id in rules) {
setting = settings[id];
if (scope.index === undefined || scope.index === null) {
scope.index = file.messages.length;
}
known.push(id);
/**
* Add `ruleId` to each new message.
*
* @param {Error?} err - Optional failure.
*/
function done(err) {
var messages = file.messages;
if (setting != null) {
/* Pass turned on rules `undefined`. */
if (reset && setting === true) {
setting = undefined;
}
while (scope.index < messages.length) {
messages[scope.index].ruleId = id;
messages[scope.index].source = SOURCE;
scope.index++;
}
next(err);
}
/*
* Invoke `rule`, with `options`
*/
rule(ast, file, options, done);
}
return plugin;
if (setting === false) {
setting = undefined;
disable.push(id);
} else {
enable.push(id);
}
}
return attach;
}
pipeline.use(ruleFactory(id, rules[id], setting));
}
/**
* Require an external. Checks, in this order:
*
* - `$cwd/$pathlike`;
* - `$cwd/$pathlike.js`;
* - `$cwd/node_modules/$pathlike`;
* - `$pathlike`.
*
* Where `$cwd` is the current working directory.
*
* When using a globally installed executable, the
* following are also included:
*
* - `$globals/$pathlike`.
*
* Where `$globals` is the directory of globally installed
* npm packages.
*
* @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 globalPath = resolve(globals, pathlike);
var plugin;
/* Run all rules. */
remark.use(function () {
return function (node, file, next) {
pipeline.run(node, file, next);
};
});
if (exists(local) || exists(local + '.js')) {
plugin = local;
/* istanbul ignore else - for globals */
} else if (exists(current)) {
plugin = current;
} else if (isGlobal && exists(globalPath)) {
plugin = globalPath;
} else {
plugin = pathlike;
}
/* Allow comments to toggle messages. */
remark.use(control, {
name: 'lint',
source: SOURCE,
reset: reset,
known: known,
enable: enable,
disable: disable
});
return require(plugin);
/**
* Transformer to sort messages.
*
* @param {Node} node - Syntax tree.
* @param {VFile} file - Virtual file.
*/
return function (node, file) {
sort(file);
};
}

@@ -191,126 +123,81 @@

function loadExternals(externals) {
var index = -1;
var rules = {};
var external;
var ruleId;
var mapping = externals ? externals.concat() : [];
var length;
var index = -1;
var rules = {};
var external;
var ruleId;
var mapping = externals ? externals.concat() : [];
var length;
mapping.push(internals);
length = mapping.length;
mapping.push(internals);
length = mapping.length;
while (++index < length) {
external = mapping[index];
while (++index < length) {
external = mapping[index];
if (typeof external === 'string') {
external = loadExternal(external);
}
for (ruleId in external) {
rules[ruleId] = external[ruleId];
}
if (typeof external === 'string') {
external = loadPlugin(external, {
prefix: 'remark-lint-'
});
}
return rules;
}
/**
* Helper to ensure ruleId’s are dash-cased instead of
* camel-cased.
*
* @param {Object} source - Original settings.
* @return {Object} - Dash-cased settings.
*/
function decamelizeSettings(source) {
var result = {};
var key;
for (key in source) {
result[decamelize(key, '-')] = source[key];
for (ruleId in external) {
rules[ruleId] = external[ruleId];
}
}
return result;
return rules;
}
/**
* Lint attacher.
* Factory to create a plugin from a rule.
*
* By default, all rules are turned on unless explicitly
* set to `false`. When `reset: true`, the opposite is
* true: all rules are turned off, unless when given
* a non-nully and non-false value.
*
* @example
* var processor = lint(remark, {
* 'html': false // Ignore HTML warnings.
* });
* attachFactory('foo', console.log, false)() // null
* attachFactory('foo', console.log, {})() // plugin
*
* @param {Remark} remark - Host object.
* @param {Object?} options - Hash of rule names mapping to
* rule options.
* @param {string} id - Identifier.
* @param {Function} rule - Rule
* @param {*} options - Options for respective rule.
* @return {Function} - Trough ware.
*/
function lint(remark, options) {
var settings = decamelizeSettings(options || {});
var rules = loadExternals(settings.external);
var reset = options && options.reset;
var enable = [];
var disable = [];
var known = [];
var setting;
var id;
function ruleFactory(id, rule, options) {
var fn = wrapped(rule);
/*
* Add each rule as a seperate plugin.
*/
return function (ast, file, next) {
var scope = file.namespace('remark-lint');
for (id in rules) {
setting = settings[id];
/* Track new messages per file. */
scope.index = file.messages.length;
known.push(id);
fn(ast, file, options, function (err) {
var messages = file.messages;
if (!(setting === null || setting === undefined)) {
/* Pass turned on rules `undefined`. */
if (reset && setting === true) {
setting = undefined;
}
while (scope.index < messages.length) {
messages[scope.index].ruleId = id;
messages[scope.index].source = SOURCE;
if (setting === false) {
setting = undefined;
disable.push(id);
} else {
enable.push(id);
}
}
scope.index++;
}
remark.use(attachFactory(id, rules[id], setting));
}
/*
* Allow comments to toggle messages.
*/
remark.use(control, {
'name': 'lint',
'source': SOURCE,
'reset': reset,
'known': known,
'enable': enable,
'disable': disable
next(err);
});
/**
* Transformer sort messages.
*
* @param {Node} node - Syntax tree.
* @param {VFile} file - Virtual file.
*/
return function (node, file) {
sort(file);
};
};
}
/*
* Expose.
/**
* Helper to ensure ruleId’s are dash-cased instead of
* camel-cased.
*
* @param {Object} source - Original settings.
* @return {Object} - Dash-cased settings.
*/
function decamelizeSettings(source) {
var result = {};
var key;
module.exports = lint;
for (key in source) {
result[decamelize(key, '-')] = source[key];
}
return result;
}

@@ -13,17 +13,39 @@ /**

* and will warn when other blockquotes use a different indentation.
* @example
* <!-- Valid, when set to `4`, invalid when set to `2` -->
*
* @example {"name": "valid.md", "setting": 4}
*
* <!--This file is also valid by default-->
*
* > Hello
* ...
*
* Paragraph.
*
* > World
*
* <!-- Valid, when set to `2`, invalid when set to `4` -->
* @example {"name": "valid.md", "setting": 2}
*
* <!--This file is also valid by default-->
*
* > Hello
* ...
*
* Paragraph.
*
* > World
*
* <!-- Always invalid -->
* > Hello
* ...
* @example {"name": "invalid.md", "label": "input"}
*
* > Hello
*
* Paragraph.
*
* > World
*
* Paragraph.
*
* > World
*
* @example {"name": "invalid.md", "label": "output"}
*
* 5:3: Remove 1 space between blockquote and content
* 9:3: Add 1 space between blockquote and content
*/

@@ -33,32 +55,12 @@

/* eslint-env commonjs */
/* Expose. */
module.exports = blockquoteIndentation;
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var toString = require('mdast-util-to-string');
var plural = require('plur');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/**
* Get the indent of a blockquote.
*
* @param {Node} node - Node to test.
* @return {number} - Indentation.
*/
function check(node) {
var head = node.children[0];
var indentation = position.start(head).column - position.start(node).column;
var padding = toString(head).match(/^ +/);
if (padding) {
indentation += padding[0].length;
}
return indentation;
}
/**
* Warn when a blockquote has a too large or too small

@@ -73,42 +75,51 @@ * indentation.

* indentation.
* @param {Function} done - Callback.
*/
function blockquoteIndentation(ast, file, preferred, done) {
preferred = isNaN(preferred) || typeof preferred !== 'number' ? null : preferred;
function blockquoteIndentation(ast, file, preferred) {
preferred = isNaN(preferred) || typeof preferred !== 'number' ? null : preferred;
visit(ast, 'blockquote', function (node) {
var indent;
var diff;
var word;
visit(ast, 'blockquote', function (node) {
var indent;
var diff;
var word;
if (position.generated(node) || !node.children.length) {
return;
}
if (position.generated(node) || !node.children.length) {
return;
}
if (preferred) {
indent = check(node);
diff = preferred - indent;
word = diff > 0 ? 'Add' : 'Remove';
if (preferred) {
indent = check(node);
diff = preferred - indent;
word = diff > 0 ? 'Add' : 'Remove';
diff = Math.abs(diff);
diff = Math.abs(diff);
if (diff !== 0) {
file.warn(
word + ' ' + diff + ' ' + plural('space', diff) +
' between blockquote and content',
position.start(node.children[0])
);
}
} else {
preferred = check(node);
}
});
done();
if (diff !== 0) {
file.warn(
word + ' ' + diff + ' ' + plural('space', diff) +
' between blockquote and content',
position.start(node.children[0])
);
}
} else {
preferred = check(node);
}
});
}
/*
* Expose.
/**
* Get the indent of a blockquote.
*
* @param {Node} node - Node to test.
* @return {number} - Indentation.
*/
function check(node) {
var head = node.children[0];
var indentation = position.start(head).column - position.start(node).column;
var padding = toString(head).match(/^ +/);
module.exports = blockquoteIndentation;
if (padding) {
indentation += padding[0].length;
}
return indentation;
}

@@ -15,32 +15,57 @@ /**

*
* ```json
* {
* "checked": "x",
* "unchecked": " "
* }
* ```js
* {checked: 'x', unchecked: ' '}
* ```
* @example
* <!-- Note: the double guillemet (`»`) and middle-dots represent a tab -->
*
* <!-- Valid by default, `'consistent'`, or `{'checked': 'x'}` -->
* @example {"name": "valid.md", "setting": {"checked": "x"}}
*
* <!--This file is also valid by default-->
*
* - [x] List item
* - [x] List item
*
* <!-- Valid by default, `'consistent'`, or `{'checked': 'X'}` -->
* @example {"name": "valid.md", "setting": {"checked": "X"}}
*
* <!--This file is also valid by default-->
*
* - [X] List item
* - [X] List item
*
* <!-- Valid by default, `'consistent'`, or `{'unchecked': ' '}` -->
* @example {"name": "valid.md", "setting": {"unchecked": " "}}
*
* <!--This file is also valid by default-->
*
* - [ ] List item
* - [ ] List item
* - [ ]··
* - [ ]
*
* <!-- Valid by default, `'consistent'`, or `{'unchecked': '»'}` -->
* - [»···] List item
* - [»···] List item
* @example {"name": "valid.md", "setting": {"unchecked": "\t"}}
*
* <!-- Always invalid -->
* <!--Also valid by default (note: `»` represents `\t`)-->
*
* - [»] List item
* - [»] List item
*
* @example {"name": "invalid.md", "label": "input"}
*
* <!--Note: `»` represents `\t`-->
*
* - [x] List item
* - [X] List item
* - [ ] List item
* - [»···] List item
* - [»] List item
*
* @example {"name": "invalid.md", "label": "output"}
*
* 4:4-4:5: Checked checkboxes should use `x` as a marker
* 6:4-6:5: Unchecked checkboxes should use ` ` as a marker
*
* @example {"setting": {"unchecked": "!"}, "name": "invalid.md", "label": "output", "config": {"positionless": true}}
*
* 1:1: Invalid unchecked checkbox marker `!`: use either `'\t'`, or `' '`
*
* @example {"setting": {"checked": "!"}, "name": "invalid.md", "label": "output", "config": {"positionless": true}}
*
* 1:1: Invalid checked checkbox marker `!`: use either `'x'`, or `'X'`
*/

@@ -50,29 +75,18 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var vfileLocation = require('vfile-location');
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Methods.
*/
/* Expose. */
module.exports = checkboxCharacterStyle;
/* Methods. */
var start = position.start;
var end = position.end;
var CHECKED = {
'x': true,
'X': true
};
/* Constants. */
var CHECKED = {x: true, X: true};
var UNCHECKED = {' ': true, '\t': true};
var UNCHECKED = {
' ': true,
' ': true
};
/**

@@ -87,105 +101,87 @@ * Warn when list item checkboxes violate a given style.

* or `' '` (space) or `'\t'` (tab) for unchecked.
* @param {Function} done - Callback.
*/
function checkboxCharacterStyle(ast, file, preferred, done) {
var contents = file.toString();
var location = vfileLocation(file);
function checkboxCharacterStyle(ast, file, preferred) {
var contents = file.toString();
var location = vfileLocation(file);
if (preferred === 'consistent' || typeof preferred !== 'object') {
preferred = {};
}
if (preferred === 'consistent' || typeof preferred !== 'object') {
preferred = {};
}
if (!preferred.unchecked) {
preferred.unchecked = null;
}
if (!preferred.unchecked) {
preferred.unchecked = null;
}
if (!preferred.checked) {
preferred.checked = null;
}
if (!preferred.checked) {
preferred.checked = null;
}
if (
preferred.unchecked !== null &&
UNCHECKED[preferred.unchecked] !== true
) {
file.fail(
'Invalid unchecked checkbox marker `' +
preferred.unchecked +
'`: use either `\'\\t\'`, or `\' \'`'
);
}
if (
preferred.unchecked !== null &&
UNCHECKED[preferred.unchecked] !== true
) {
file.fail(
'Invalid unchecked checkbox marker `' +
preferred.unchecked +
'`: use either `\'\\t\'`, or `\' \'`'
);
}
if (
preferred.checked !== null &&
CHECKED[preferred.checked] !== true
) {
file.fail(
'Invalid checked checkbox marker `' +
preferred.checked +
'`: use either `\'x\'`, or `\'X\'`'
);
}
visit(ast, 'listItem', function (node) {
var type;
var initial;
var final;
var stop;
var value;
var style;
var character;
/* Exit early for items without checkbox. */
if (
preferred.checked !== null &&
CHECKED[preferred.checked] !== true
node.checked !== Boolean(node.checked) ||
position.generated(node)
) {
file.fail(
'Invalid checked checkbox marker `' +
preferred.checked +
'`: use either `\'x\'`, or `\'X\'`'
);
return;
}
visit(ast, 'listItem', function (node) {
var type;
var initial;
var final;
var stop;
var value;
var style;
var character;
type = node.checked ? 'checked' : 'unchecked';
/*
* Exit early for items without checkbox.
*/
initial = start(node).offset;
final = (node.children.length ? start(node.children[0]) : end(node)).offset;
if (
node.checked !== Boolean(node.checked) ||
position.generated(node)
) {
return;
}
/* For a checkbox to be parsed, it must be followed
* by a white space. */
value = contents.slice(initial, final).trimRight().slice(0, -1);
type = node.checked ? 'checked' : 'unchecked';
/* The checkbox character is behind a square
* bracket. */
character = value.charAt(value.length - 1);
style = preferred[type];
initial = start(node).offset;
final = (node.children.length ? start(node.children[0]) : end(node)).offset;
if (style === null) {
preferred[type] = character;
} else if (character !== style) {
stop = initial + value.length;
/*
* For a checkbox to be parsed, it must be followed
* by a white space.
*/
value = contents.slice(initial, final).trimRight().slice(0, -1);
/*
* The checkbox character is behind a square
* bracket.
*/
character = value.charAt(value.length - 1);
style = preferred[type];
if (style === null) {
preferred[type] = character;
} else if (character !== style) {
stop = initial + value.length;
file.warn(
type.charAt(0).toUpperCase() + type.slice(1) +
' checkboxes should use `' + style + '` as a marker',
{
'start': location.toPosition(stop - 1),
'end': location.toPosition(stop)
}
);
file.warn(
type.charAt(0).toUpperCase() + type.slice(1) +
' checkboxes should use `' + style + '` as a marker',
{
start: location.toPosition(stop - 1),
end: location.toPosition(stop)
}
});
done();
);
}
});
}
/*
* Expose.
*/
module.exports = checkboxCharacterStyle;

@@ -8,4 +8,5 @@ /**

* Warn when list item checkboxes are followed by too much white-space.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* - [ ] List item

@@ -16,3 +17,4 @@ * + [x] List item

*
* <!-- Invalid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* - [ ] List item

@@ -22,2 +24,8 @@ * + [x] List item

* - [ ] List item
*
* @example {"name": "invalid.md", "label": "output"}
*
* 2:7-2:8: Checkboxes should be followed by a single character
* 3:7-3:9: Checkboxes should be followed by a single character
* 4:7-4:10: Checkboxes should be followed by a single character
*/

@@ -27,16 +35,11 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var vfileLocation = require('vfile-location');
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Methods.
*/
/* Expose. */
module.exports = checkboxContentIndent;
/* Methods. */
var start = position.start;

@@ -51,57 +54,42 @@ var end = position.end;

* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function checkboxContentIndent(ast, file, preferred, done) {
var contents = file.toString();
var location = vfileLocation(file);
function checkboxContentIndent(ast, file) {
var contents = file.toString();
var location = vfileLocation(file);
visit(ast, 'listItem', function (node) {
var initial;
var final;
var value;
visit(ast, 'listItem', function (node) {
var initial;
var final;
var value;
/*
* Exit early for items without checkbox.
*/
/* Exit early for items without checkbox. */
if (
node.checked !== Boolean(node.checked) ||
position.generated(node)
) {
return;
}
if (
node.checked !== Boolean(node.checked) ||
position.generated(node)
) {
return;
}
initial = start(node).offset;
final = (node.children.length ? start(node.children[0]) : end(node)).offset;
initial = start(node).offset;
final = (node.children.length ? start(node.children[0]) : end(node)).offset;
while (/[^\S\n]/.test(contents.charAt(final))) {
final++;
}
while (/[^\S\n]/.test(contents.charAt(final))) {
final++;
}
/* For a checkbox to be parsed, it must be followed
* by a white space. */
value = contents.slice(initial, final);
/*
* For a checkbox to be parsed, it must be followed
* by a white space.
*/
value = value.slice(value.indexOf(']') + 1);
value = contents.slice(initial, final);
if (value.length === 1) {
return;
}
value = value.slice(value.indexOf(']') + 1);
if (value.length === 1) {
return;
}
file.warn('Checkboxes should be followed by a single character', {
'start': location.toPosition(final - value.length + 1),
'end': location.toPosition(final)
});
file.warn('Checkboxes should be followed by a single character', {
start: location.toPosition(final - value.length + 1),
end: location.toPosition(final)
});
done();
});
}
/*
* Expose.
*/
module.exports = checkboxContentIndent;

@@ -15,25 +15,76 @@ /**

* style.
* @example
* <!-- Valid, when set to `indented` or `consistent`, invalid when set to `fenced` -->
* Hello
*
* ...
* @example {"setting": "indented", "name": "valid.md"}
*
* World
* <!-- This is also valid when `'consistent'` -->
*
* <!-- Valid, when set to `fenced` or `consistent`, invalid when set to `indented` -->
* alpha();
*
* Paragraph.
*
* bravo();
*
* @example {"setting": "indented", "name": "invalid.md", "label": "input"}
*
* ```
* Hello
* alpha();
* ```
* ...
* ```bar
* World
*
* Paragraph.
*
* ```
* bravo();
* ```
*
* <!-- Always invalid -->
* Hello
* ...
* @example {"setting": "indented", "name": "invalid.md", "label": "output"}
*
* 1:1-3:4: Code blocks should be indented
* 7:1-9:4: Code blocks should be indented
*
* @example {"setting": "fenced", "name": "valid.md"}
*
* <!-- This is also valid when `'consistent'` -->
*
* ```
* World
* ```
* alpha();
* ```
*
* Paragraph.
*
* ```
* bravo();
* ```
*
* @example {"setting": "fenced", "name": "invalid.md", "label": "input"}
*
* alpha();
*
* Paragraph.
*
* bravo();
*
* @example {"setting": "fenced", "name": "invalid.md", "label": "output"}
*
* 1:1-1:13: Code blocks should be fenced
* 5:1-5:13: Code blocks should be fenced
*
* @example {"name": "invalid.md", "label": "input"}
*
* <!-- This is always invalid -->
*
* alpha();
*
* Paragraph.
*
* ```
* bravo();
* ```
*
* @example {"name": "invalid.md", "label": "output"}
*
* 7:1-9:4: Code blocks should be indented
*
* @example {"setting": "invalid", "name": "invalid.md", "label": "output", "config": {"positionless": true}}
*
* 1:1: Invalid code block style `invalid`: use either `'consistent'`, `'fenced'`, or `'indented'`
*/

@@ -43,26 +94,18 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Methods.
*/
/* Expose. */
module.exports = codeBlockStyle;
/* Methods. */
var start = position.start;
var end = position.end;
/*
* Valid styles.
*/
/* Valid styles. */
var STYLES = {
'null': true,
'fenced': true,
'indented': true
null: true,
fenced: true,
indented: true
};

@@ -79,61 +122,53 @@

* `'fenced'` or `'indented'`.
* @param {Function} done - Callback.
*/
function codeBlockStyle(ast, file, preferred, done) {
var contents = file.toString();
function codeBlockStyle(ast, file, preferred) {
var contents = file.toString();
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
if (STYLES[preferred] !== true) {
file.fail('Invalid code block style `' + preferred + '`: use either `\'consistent\'`, `\'fenced\'`, or `\'indented\'`');
done();
return;
if (STYLES[preferred] !== true) {
file.fail('Invalid code block style `' + preferred + '`: use either `\'consistent\'`, `\'fenced\'`, or `\'indented\'`');
return;
}
visit(ast, 'code', function (node) {
var current = check(node);
if (!current) {
return;
}
/**
* Get the style of `node`.
*
* @param {Node} node - Node.
* @return {string?} - `'fenced'`, `'indented'`, or
* `null`.
*/
function check(node) {
var initial = start(node).offset;
var final = end(node).offset;
if (!preferred) {
preferred = current;
} else if (preferred !== current) {
file.warn('Code blocks should be ' + preferred, node);
}
});
if (position.generated(node)) {
return null;
}
return;
if (
node.lang ||
/^\s*([~`])\1{2,}/.test(contents.slice(initial, final))
) {
return 'fenced';
}
/**
* Get the style of `node`.
*
* @param {Node} node - Node.
* @return {string?} - `'fenced'`, `'indented'`, or
* `null`.
*/
function check(node) {
var initial = start(node).offset;
var final = end(node).offset;
return 'indented';
if (position.generated(node)) {
return null;
}
visit(ast, 'code', function (node) {
var current = check(node);
if (
node.lang ||
/^\s*([~`])\1{2,}/.test(contents.slice(initial, final))
) {
return 'fenced';
}
if (!current) {
return;
}
if (!preferred) {
preferred = current;
} else if (preferred !== current) {
file.warn('Code blocks should be ' + preferred, node);
}
});
done();
return 'indented';
}
}
/*
* Expose.
*/
module.exports = codeBlockStyle;

@@ -8,8 +8,14 @@ /**

* Warn when definition labels are not lower-case.
* @example
* <!-- Valid -->
* [example] http://example.com "Example Domain"
*
* <!-- Invalid -->
* ![Example] http://example.com/favicon.ico "Example image"
* @example {"name": "valid.md"}
*
* [example]: http://example.com "Example Domain"
*
* @example {"name": "invalid.md", "label": "input"}
*
* [Example]: http://example.com "Example Domain"
*
* @example {"name": "invalid.md", "label": "output"}
*
* 1:1-1:47: Do not use upper-case characters in definition labels
*/

@@ -19,15 +25,10 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Expressions.
*/
/* Expose. */
module.exports = definitionCase;
/* Expressions. */
var LABEL = /^\s*\[((?:\\[\s\S]|[^\[\]])+)\]/;

@@ -41,40 +42,32 @@

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function definitionCase(ast, file, preferred, done) {
var contents = file.toString();
function definitionCase(ast, file) {
var contents = file.toString();
/**
* Validate a node, either a normal definition or
* a footnote definition.
*
* @param {Node} node - Node.
*/
function validate(node) {
var start = position.start(node).offset;
var end = position.end(node).offset;
var label;
visit(ast, 'definition', validate);
visit(ast, 'footnoteDefinition', validate);
if (position.generated(node)) {
return;
}
return;
label = contents.slice(start, end).match(LABEL)[1];
/**
* Validate a node, either a normal definition or
* a footnote definition.
*
* @param {Node} node - Node.
*/
function validate(node) {
var start = position.start(node).offset;
var end = position.end(node).offset;
var label;
if (label !== label.toLowerCase()) {
file.warn('Do not use upper-case characters in definition labels', node);
}
if (position.generated(node)) {
return;
}
visit(ast, 'definition', validate);
visit(ast, 'footnoteDefinition', validate);
label = contents.slice(start, end).match(LABEL)[1];
done();
if (label !== label.toLowerCase()) {
file.warn('Do not use upper-case characters in definition labels', node);
}
}
}
/*
* Expose.
*/
module.exports = definitionCase;

@@ -8,8 +8,14 @@ /**

* Warn when consecutive white space is used in a definition.
* @example
* <!-- Valid -->
* [example domain] http://example.com "Example Domain"
*
* <!-- Invalid -->
* ![example image] http://example.com/favicon.ico "Example image"
* @example {"name": "valid.md"}
*
* [example domain]: http://example.com "Example Domain"
*
* @example {"name": "invalid.md", "label": "input"}
*
* [example domain]: http://example.com "Example Domain"
*
* @example {"name": "invalid.md", "label": "output"}
*
* 1:1-1:57: Do not use consecutive white-space in definition labels
*/

@@ -19,15 +25,10 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Expressions.
*/
/* Expose. */
module.exports = definitionSpacing;
/* Expressions. */
var LABEL = /^\s*\[((?:\\[\s\S]|[^\[\]])+)\]/;

@@ -41,40 +42,32 @@

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function definitionSpacing(ast, file, preferred, done) {
var contents = file.toString();
function definitionSpacing(ast, file) {
var contents = file.toString();
/**
* Validate a node, either a normal definition or
* a footnote definition.
*
* @param {Node} node - Node.
*/
function validate(node) {
var start = position.start(node).offset;
var end = position.end(node).offset;
var label;
visit(ast, 'definition', validate);
visit(ast, 'footnoteDefinition', validate);
if (position.generated(node)) {
return;
}
return;
label = contents.slice(start, end).match(LABEL)[1];
/**
* Validate a node, either a normal definition or
* a footnote definition.
*
* @param {Node} node - Node.
*/
function validate(node) {
var start = position.start(node).offset;
var end = position.end(node).offset;
var label;
if (/[ \t\n]{2,}/.test(label)) {
file.warn('Do not use consecutive white-space in definition labels', node);
}
if (position.generated(node)) {
return;
}
visit(ast, 'definition', validate);
visit(ast, 'footnoteDefinition', validate);
label = contents.slice(start, end).match(LABEL)[1];
done();
if (/[ \t\n]{2,}/.test(label)) {
file.warn('Do not use consecutive white-space in definition labels', node);
}
}
}
/*
* Expose.
*/
module.exports = definitionSpacing;

@@ -15,10 +15,41 @@ /**

* style.
* @example
* <!-- Valid when set to `consistent` or `*` -->
*
* @example {"setting": "*", "name": "valid.md"}
*
* *foo*
* *bar*
*
* <!-- Valid when set to `consistent` or `_` -->
* @example {"setting": "*", "name": "invalid.md", "label": "input"}
*
* _foo_
*
* @example {"setting": "*", "name": "invalid.md", "label": "output"}
*
* 1:1-1:6: Emphasis should use `*` as a marker
*
* @example {"setting": "_", "name": "valid.md"}
*
* _foo_
*
* @example {"setting": "_", "name": "invalid.md", "label": "input"}
*
* *foo*
*
* @example {"setting": "_", "name": "invalid.md", "label": "output"}
*
* 1:1-1:6: Emphasis should use `_` as a marker
*
* @example {"setting": "consistent", "name": "invalid.md", "label": "input"}
*
* <!-- This is never valid -->
*
* *foo*
* _bar_
*
* @example {"setting": "consistent", "name": "invalid.md", "label": "output"}
*
* 4:1-4:6: Emphasis should use `*` as a marker
*
* @example {"setting": "invalid", "name": "invalid.md", "label": "output", "config": {"positionless": true}}
*
* 1:1: Invalid emphasis marker `invalid`: use either `'consistent'`, `'*'`, or `'_'`
*/

@@ -30,17 +61,14 @@

/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Map of valid markers.
*/
/* Expose. */
module.exports = emphasisMarker;
/* Map of valid markers. */
var MARKERS = {
'*': true,
'_': true,
'null': true
'*': true,
'_': true,
'null': true
};

@@ -55,37 +83,26 @@

* marker, either `'*'` or `'_'`, or `'consistent'`.
* @param {Function} done - Callback.
*/
function emphasisMarker(ast, file, preferred, done) {
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
function emphasisMarker(ast, file, preferred) {
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
if (MARKERS[preferred] !== true) {
file.fail('Invalid emphasis marker `' + preferred + '`: use either `\'consistent\'`, `\'*\'`, or `\'_\'`');
done();
if (MARKERS[preferred] !== true) {
file.fail('Invalid emphasis marker `' + preferred + '`: use either `\'consistent\'`, `\'*\'`, or `\'_\'`');
return;
}
return;
visit(ast, 'emphasis', function (node) {
var marker = file.toString().charAt(position.start(node).offset);
if (position.generated(node)) {
return;
}
visit(ast, 'emphasis', function (node) {
var marker = file.toString().charAt(position.start(node).offset);
if (position.generated(node)) {
return;
}
if (preferred) {
if (marker !== preferred) {
file.warn('Emphasis should use `' + preferred + '` as a marker', node);
}
} else {
preferred = marker;
}
});
done();
if (preferred) {
if (marker !== preferred) {
file.warn('Emphasis should use `' + preferred + '` as a marker', node);
}
} else {
preferred = marker;
}
});
}
/*
* Expose.
*/
module.exports = emphasisMarker;

@@ -18,25 +18,50 @@ /**

* languge flags.
* @example
* <!-- Valid: -->
* ```hello
* world();
*
* @example {"name": "valid.md"}
*
* ```alpha
* bravo();
* ```
*
* <!-- Valid: -->
* Hello
* @example {"name": "invalid.md", "label": "input"}
*
* <!-- Invalid: -->
* ```
* world();
* alpha();
* ```
*
* <!-- Valid when given `{allowEmpty: true}`: -->
* @example {"name": "invalid.md", "label": "output"}
*
* 1:1-3:4: Missing code-language flag
*
* @example {"name": "valid.md", "setting": {"allowEmpty": true}}
*
* ```
* world();
* alpha();
* ```
*
* <!-- Invalid when given `["world"]`: -->
* ```hello
* world();
* @example {"name": "invalid.md", "setting": {"allowEmpty": false}, "label": "input"}
*
* ```
* alpha();
* ```
*
* @example {"name": "invalid.md", "setting": {"allowEmpty": false}, "label": "output"}
*
* 1:1-3:4: Missing code-language flag
*
* @example {"name": "valid.md", "setting": ["alpha"]}
*
* ```alpha
* bravo();
* ```
*
* @example {"name": "invalid.md", "setting": ["charlie"], "label": "input"}
*
* ```alpha
* bravo();
* ```
*
* @example {"name": "invalid.md", "setting": ["charlie"], "label": "output"}
*
* 1:1-3:4: Invalid code-language flag
*/

@@ -46,15 +71,10 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Methods.
*/
/* Expose. */
module.exports = fencedCodeFlag;
/* Methods. */
var start = position.start;

@@ -70,42 +90,33 @@ var end = position.end;

* of flags deemed valid.
* @param {Function} done - Callback.
*/
function fencedCodeFlag(ast, file, preferred, done) {
var contents = file.toString();
var allowEmpty = false;
var flags = [];
function fencedCodeFlag(ast, file, preferred) {
var contents = file.toString();
var allowEmpty = false;
var flags = [];
if (typeof preferred === 'object' && !('length' in preferred)) {
allowEmpty = Boolean(preferred.allowEmpty);
if (typeof preferred === 'object' && !('length' in preferred)) {
allowEmpty = Boolean(preferred.allowEmpty);
preferred = preferred.flags;
}
preferred = preferred.flags;
}
if (typeof preferred === 'object' && 'length' in preferred) {
flags = String(preferred).split(',');
}
if (typeof preferred === 'object' && 'length' in preferred) {
flags = String(preferred).split(',');
}
visit(ast, 'code', function (node) {
var value = contents.slice(start(node).offset, end(node).offset);
visit(ast, 'code', function (node) {
var value = contents.slice(start(node).offset, end(node).offset);
if (position.generated(node)) {
return;
}
if (position.generated(node)) {
return;
}
if (node.lang) {
if (flags.length && flags.indexOf(node.lang) === -1) {
file.warn('Invalid code-language flag', node);
}
} else if (/^\ {0,3}([~`])\1{2,}/.test(value) && !allowEmpty) {
file.warn('Missing code-language flag', node);
}
});
done();
if (node.lang) {
if (flags.length && flags.indexOf(node.lang) === -1) {
file.warn('Invalid code-language flag', node);
}
} else if (/^ {0,3}([~`])\1{2,}/.test(value) && !allowEmpty) {
file.warn('Missing code-language flag', node);
}
});
}
/*
* Expose.
*/
module.exports = fencedCodeFlag;

@@ -14,29 +14,46 @@ /**

* different style.
* @example
* <!-- Valid by default and `` '`' ``: -->
* ```foo
* bar();
*
* @example {"name": "valid.md", "setting": "`"}
*
* <!-- This is also valid by default. -->
*
* ```alpha
* bravo();
* ```
*
* ```
* baz();
* charlie();
* ```
*
* <!-- Valid by default and `'~'`: -->
* ~~~foo
* bar();
* @example {"name": "valid.md", "setting": "~"}
*
* <!-- This is also valid by default. -->
*
* ~~~alpha
* bravo();
* ~~~
*
* ~~~
* baz();
* charlie();
* ~~~
*
* <!-- Always invalid: -->
* ~~~foo
* bar();
* @example {"name": "invalid.md", "label": "input"}
*
* <!-- This is always invalid. -->
*
* ```alpha
* bravo();
* ```
*
* ~~~
* charlie();
* ~~~
*
* ```
* baz();
* ```
* @example {"name": "invalid.md", "label": "output"}
*
* 7:1-9:4: Fenced code should use ` as a marker
*
* @example {"name": "invalid.md", "setting": "!", "label": "output", "config": {"positionless": true}}
*
* 1:1: Invalid fenced code marker `!`: use either `'consistent'`, `` '`' ``, or `'~'`
*/

@@ -46,19 +63,14 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Map of valid markers.
*/
/* Expose. */
module.exports = fencedCodeMarker;
/* Map of valid markers. */
var MARKERS = {
'`': true,
'~': true,
'null': true
'`': true,
'~': true,
'null': true
};

@@ -73,49 +85,35 @@

* marker, either `` '`' `` or `~`, or `'consistent'`.
* @param {Function} done - Callback.
*/
function fencedCodeMarker(ast, file, preferred, done) {
var contents = file.toString();
function fencedCodeMarker(ast, file, preferred) {
var contents = file.toString();
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
if (MARKERS[preferred] !== true) {
file.fail('Invalid fenced code marker `' + preferred + '`: use either `\'consistent\'`, `` \'\`\' ``, or `\'~\'`');
done();
if (MARKERS[preferred] !== true) {
file.fail('Invalid fenced code marker `' + preferred + '`: use either `\'consistent\'`, `` \'`\' ``, or `\'~\'`');
return;
}
return;
visit(ast, 'code', function (node) {
var marker = contents.substr(position.start(node).offset, 4);
if (position.generated(node)) {
return;
}
visit(ast, 'code', function (node) {
var marker = contents.substr(position.start(node).offset, 4);
marker = marker.trimLeft().charAt(0);
if (position.generated(node)) {
return;
}
/* Ignore unfenced code blocks. */
if (MARKERS[marker] !== true) {
return;
}
marker = marker.trimLeft().charAt(0);
/*
* Ignore unfenced code blocks.
*/
if (MARKERS[marker] !== true) {
return;
}
if (preferred) {
if (marker !== preferred) {
file.warn('Fenced code should use ' + preferred + ' as a marker', node);
}
} else {
preferred = marker;
}
});
done();
if (preferred) {
if (marker !== preferred) {
file.warn('Fenced code should use ' + preferred + ' as a marker', node);
}
} else {
preferred = marker;
}
});
}
/*
* Expose.
*/
module.exports = fencedCodeMarker;

@@ -14,5 +14,12 @@ /**

* Options: `string`, default: `'md'` — Expected file extension.
* @example
* Invalid (when `'md'`): readme.mkd, readme.markdown, etc.
* Valid (when `'md'`): readme, readme.md
*
* @example {"name": "readme.md"}
*
* @example {"name": "readme"}
*
* @example {"name": "readme.mkd", "label": "output", "config": {"positionless": true}}
*
* 1:1: Invalid extension: use `md`
*
* @example {"name": "readme.mkd", "setting": "mkd"}
*/

@@ -22,3 +29,4 @@

/* eslint-env commonjs */
/* Expose. */
module.exports = fileExtension;

@@ -32,20 +40,11 @@ /**

* extension.
* @param {Function} done - Callback.
*/
function fileExtension(ast, file, preferred, done) {
var ext = file.extension;
function fileExtension(ast, file, preferred) {
var ext = file.extension;
preferred = typeof preferred === 'string' ? preferred : 'md';
preferred = typeof preferred === 'string' ? preferred : 'md';
if (ext !== '' && ext !== preferred) {
file.warn('Invalid extension: use `' + preferred + '`');
}
done();
if (ext !== '' && ext !== preferred) {
file.warn('Invalid extension: use `' + preferred + '`');
}
}
/*
* Expose.
*/
module.exports = fileExtension;

@@ -8,14 +8,20 @@ /**

* Warn when definitions are not placed at the end of the file.
* @example
* <!-- Valid: -->
* ...
*
* [example] http://example.com "Example Domain"
* @example {"name": "valid.md"}
*
* <!-- Invalid: -->
* ...
* Paragraph.
*
* [example] http://example.com "Example Domain"
* [example]: http://example.com "Example Domain"
*
* A trailing paragraph.
* @example {"name": "invalid.md", "label": "input"}
*
* Paragraph.
*
* [example]: http://example.com "Example Domain"
*
* Another paragraph.
*
* @example {"name": "invalid.md", "label": "output"}
*
* 3:1-3:47: Move definitions to the end of the file (after the node at line `5`)
*/

@@ -25,15 +31,10 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Methods.
*/
/* Expose. */
module.exports = finalDefinition;
/* Methods. */
var start = position.start;

@@ -47,35 +48,22 @@

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function finalDefinition(ast, file, preferred, done) {
var last = null;
function finalDefinition(ast, file) {
var last = null;
visit(ast, function (node) {
var line = start(node).line;
visit(ast, function (node) {
var line = start(node).line;
/*
* Ignore generated nodes.
*/
/* Ignore generated nodes. */
if (node.type === 'root' || position.generated(node)) {
return;
}
if (node.type === 'root' || position.generated(node)) {
return;
}
if (node.type === 'definition') {
if (last !== null && last > line) {
file.warn('Move definitions to the end of the file (after the node at line `' + last + '`)', node);
}
} else if (last === null) {
last = line;
}
}, true);
done();
if (node.type === 'definition') {
if (last !== null && last > line) {
file.warn('Move definitions to the end of the file (after the node at line `' + last + '`)', node);
}
} else if (last === null) {
last = line;
}
}, true);
}
/*
* Expose.
*/
module.exports = finalDefinition;

@@ -15,3 +15,4 @@ /**

/* eslint-env commonjs */
/* Expose. */
module.exports = finalNewline;

@@ -24,20 +25,10 @@ /**

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function finalNewline(ast, file, preferred, done) {
var contents = file.toString();
var last = contents.length - 1;
function finalNewline(ast, file) {
var contents = file.toString();
var last = contents.length - 1;
if (last > 0 && contents.charAt(last) !== '\n') {
file.warn('Missing newline character at end of file');
}
done();
if (last > -1 && contents.charAt(last) !== '\n') {
file.warn('Missing newline character at end of file');
}
}
/*
* Expose.
*/
module.exports = finalNewline;

@@ -10,12 +10,38 @@ /**

* Options: `number`, default: `1`.
* @example
* <!-- Valid, when set to `1` -->
* # Foo
*
* ## Bar
* @example {"name": "valid.md", "setting": 1}
*
* <!-- Invalid, when set to `1` -->
* ## Foo
* <!-- Also valid by default. -->
*
* # Bar
* # Alpha
*
* Paragraph.
*
* @example {"name": "invalid.md", "setting": 1, "label": "input"}
*
* <!-- Also invalid by default. -->
*
* ## Bravo
*
* Paragraph.
*
* @example {"name": "invalid.md", "setting": 1, "label": "output"}
*
* 3:1-3:9: First heading level should be `1`
*
* @example {"name": "valid.md", "setting": 2}
*
* ## Bravo
*
* Paragraph.
*
* @example {"name": "invalid.md", "setting": 2, "label": "input"}
*
* # Bravo
*
* Paragraph.
*
* @example {"name": "invalid.md", "setting": 2, "label": "output"}
*
* 1:1-1:8: First heading level should be `2`
*/

@@ -25,11 +51,9 @@

/* eslint-env commonjs */
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('unist-util-position');
/*
* Dependencies.
*/
/* Expose. */
module.exports = firstHeadingLevel;
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
/**

@@ -41,22 +65,17 @@ * Warn when the first heading has a level other than a specified value.

* @param {number?} [preferred=1] - First heading level.
* @param {Function} done - Callback.
*/
function firstHeadingLevel(ast, file, preferred, done) {
var style = preferred && preferred !== true ? preferred : 1;
function firstHeadingLevel(ast, file, preferred) {
var style = preferred && preferred !== true ? preferred : 1;
visit(ast, 'heading', function (node) {
if (position.generated(node)) {
return null;
}
visit(ast, 'heading', function (node) {
if (position.generated(node)) {
return;
}
if (node.depth !== style) {
file.warn('First heading level should be `' + style + '`', node);
}
if (node.depth !== style) {
file.warn('First heading level should be `' + style + '`', node);
}
return false;
});
done();
return false;
});
}
module.exports = firstHeadingLevel;

@@ -8,12 +8,20 @@ /**

* Warn when too many spaces are used to create a hard break.
* @example
* <!-- Note: the middle-dots represent spaces -->
*
* <!-- Valid: -->
* @example {"name": "valid.md"}
*
* <!--Note: `·` represents ` `-->
*
* Lorem ipsum··
* dolor sit amet
*
* <!-- Invalid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* <!--Note: `·` represents ` `-->
*
* Lorem ipsum···
* dolor sit amet.
*
* @example {"name": "invalid.md", "label": "output"}
*
* 3:12-4:1: Use two spaces for hard line breaks
*/

@@ -25,9 +33,9 @@

/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/* Expose. */
module.exports = hardBreakSpaces;
/**

@@ -39,31 +47,21 @@ * Warn when too many spaces are used to create a

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function hardBreakSpaces(ast, file, preferred, done) {
var contents = file.toString();
function hardBreakSpaces(ast, file) {
var contents = file.toString();
visit(ast, 'break', function (node) {
var start = position.start(node).offset;
var end = position.end(node).offset;
var value;
visit(ast, 'break', function (node) {
var start = position.start(node).offset;
var end = position.end(node).offset;
var value;
if (position.generated(node)) {
return;
}
if (position.generated(node)) {
return;
}
value = contents.slice(start, end).split('\n', 1)[0].replace(/\r$/, '');
value = contents.slice(start, end).split('\n', 1)[0].replace(/\r$/, '');
if (value.length > 2) {
file.warn('Use two spaces for hard line breaks', node);
}
});
done();
if (value.length > 2) {
file.warn('Use two spaces for hard line breaks', node);
}
});
}
/*
* Expose.
*/
module.exports = hardBreakSpaces;

@@ -8,12 +8,18 @@ /**

* Warn when headings increment with more than 1 level at a time.
* @example
* <!-- Valid: -->
* # Foo
*
* ## Bar
* @example {"name": "valid.md"}
*
* <!-- Invalid: -->
* # Foo
* # Alpha
*
* ### Bar
* ## Bravo
*
* @example {"name": "invalid.md", "label": "input"}
*
* # Charlie
*
* ### Delta
*
* @example {"name": "invalid.md", "label": "output"}
*
* 3:1-3:10: Heading levels should increment by one level at a time
*/

@@ -23,11 +29,9 @@

/* eslint-env commonjs */
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('unist-util-position');
/*
* Dependencies.
*/
/* Expose. */
module.exports = headingIncrement;
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
/**

@@ -41,29 +45,19 @@ * Warn when headings increment with more than 1 level at

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function headingIncrement(ast, file, preferred, done) {
var prev = null;
function headingIncrement(ast, file) {
var prev = null;
visit(ast, 'heading', function (node) {
var depth = node.depth;
visit(ast, 'heading', function (node) {
var depth = node.depth;
if (position.generated(node)) {
return;
}
if (position.generated(node)) {
return;
}
if (prev && depth > prev + 1) {
file.warn('Heading levels should increment by one level at a time', node);
}
if (prev && depth > prev + 1) {
file.warn('Heading levels should increment by one level at a time', node);
}
prev = depth;
});
done();
prev = depth;
});
}
/*
* Expose.
*/
module.exports = headingIncrement;

@@ -15,33 +15,50 @@ /**

* style.
* @example
* <!-- Valid when `consistent` or `atx` -->
* # Foo
*
* ## Bar
* @example {"name": "valid.md", "setting": "atx"}
*
* ### Baz
* <!--Also valid when `consistent`-->
*
* <!-- Valid when `consistent` or `atx-closed` -->
* # Foo #
* # Alpha
*
* ## Bar #
* ## Bravo
*
* ### Baz ###
* ### Charlie
*
* <!-- Valid when `consistent` or `setext` -->
* Foo
* ===
* @example {"name": "valid.md", "setting": "atx-closed"}
*
* Bar
* ---
* <!--Also valid when `consistent`-->
*
* ### Baz
* # Delta ##
*
* <!-- Invalid -->
* Foo
* ===
* ## Echo ##
*
* ## Bar
* ### Foxtrot ###
*
* ### Baz ###
* @example {"name": "valid.md", "setting": "setext"}
*
* <!--Also valid when `consistent`-->
*
* Golf
* ====
*
* Hotel
* -----
*
* ### India
*
* @example {"name": "invalid.md", "label": "input"}
*
* <!--Always invalid.-->
*
* Juliett
* =======
*
* ## Kilo
*
* ### Lima ###
*
* @example {"name": "invalid.md", "label": "output"}
*
* 6:1-6:8: Headings should use setext
* 8:1-8:13: Headings should use setext
*/

@@ -53,14 +70,11 @@

/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var style = require('mdast-util-heading-style');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Types.
*/
/* Expose. */
module.exports = headingStyle;
/* Types. */
var TYPES = ['atx', 'atx-closed', 'setext'];

@@ -79,26 +93,18 @@

*/
function headingStyle(ast, file, preferred, done) {
preferred = TYPES.indexOf(preferred) === -1 ? null : preferred;
function headingStyle(ast, file, preferred) {
preferred = TYPES.indexOf(preferred) === -1 ? null : preferred;
visit(ast, 'heading', function (node) {
if (position.generated(node)) {
return;
}
visit(ast, 'heading', function (node) {
if (position.generated(node)) {
return;
}
if (preferred) {
if (style(node, preferred) !== preferred) {
file.warn('Headings should use ' + preferred, node);
}
} else {
preferred = style(node, preferred);
}
});
done();
if (preferred) {
if (style(node, preferred) !== preferred) {
file.warn('Headings should use ' + preferred, node);
}
} else {
preferred = style(node, preferred);
}
});
}
/*
* Expose.
*/
module.exports = headingStyle;

@@ -15,19 +15,50 @@ /**

* style.
* @example
* <!-- Valid when `consistent` or `"` -->
*
* @example {"name": "valid.md", "setting": "\""}
*
* <!--Also valid when `consistent`-->
*
* [Example](http://example.com "Example Domain")
* [Example](http://example.com "Example Domain")
*
* <!-- Valid when `consistent` or `'` -->
* @example {"name": "valid.md", "setting": "'"}
*
* <!--Also valid when `consistent`-->
*
* [Example](http://example.com 'Example Domain')
* [Example](http://example.com 'Example Domain')
*
* <!-- Valid when `consistent` or `()` -->
* [Example](http://example.com (Example Domain))
* [Example](http://example.com (Example Domain))
* @example {"name": "valid.md", "setting": "()"}
*
* <!-- Always invalid -->
* <!--Also valid when `consistent`-->
*
* [Example](http://example.com (Example Domain) )
* [Example](http://example.com (Example Domain) )
*
* @example {"name": "invalid.md", "label": "input"}
*
* <!--Always invalid-->
*
* [Example](http://example.com "Example Domain")
* [Example](http://example.com#without-title)
* [Example](http://example.com 'Example Domain')
*
* @example {"name": "invalid.md", "label": "output"}
*
* 5:46: Titles should use `"` as a quote
*
* @example {"name": "invalid.md", "label": "input", "setting": "()"}
*
* <!--Always invalid-->
*
* [Example](http://example.com (Example Domain))
* [Example](http://example.com 'Example Domain')
*
* @example {"name": "invalid.md", "label": "output", "setting": "()"}
*
* 4:46: Titles should use `()` as a quote
*
* @example {"name": "invalid.md", "setting": ".", "label": "output", "config": {"positionless": true}}
*
* 1:1: Invalid link title style marker `.`: use either `'consistent'`, `'"'`, `'\''`, or `'()'`
*/

@@ -37,29 +68,21 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var vfileLocation = require('vfile-location');
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Map of valid markers.
*/
/* Expose. */
module.exports = linkTitleStyle;
/* Methods. */
var end = position.end;
/* Map of valid markers. */
var MARKERS = {
'"': true,
'\'': true,
')': true,
'null': true
'"': true,
'\'': true,
')': true,
'null': true
};
/*
* Methods.
*/
var end = position.end;
/**

@@ -72,76 +95,64 @@ * Warn for fenced code blocks without language flag.

* marker, either `'"'`, `'\''`, `'()'`, or `'consistent'`.
* @param {Function} done - Callback.
*/
function linkTitleStyle(ast, file, preferred, done) {
var contents = file.toString();
var location = vfileLocation(file);
function linkTitleStyle(ast, file, preferred) {
var contents = file.toString();
var location = vfileLocation(file);
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
if (preferred === '()' || preferred === '(') {
preferred = ')';
}
if (preferred === '()' || preferred === '(') {
preferred = ')';
}
if (MARKERS[preferred] !== true) {
file.fail('Invalid link title style marker `' + preferred + '`: use either `\'consistent\'`, `\'"\'`, `\'\\\'\'`, or `\'()\'`');
done();
if (MARKERS[preferred] !== true) {
file.fail('Invalid link title style marker `' + preferred + '`: use either `\'consistent\'`, `\'"\'`, `\'\\\'\'`, or `\'()\'`');
return;
}
return;
}
visit(ast, 'link', validate);
visit(ast, 'image', validate);
visit(ast, 'definition', validate);
/**
* Validate a single node.
*
* @param {Node} node - Node.
*/
function validate(node) {
var last = end(node).offset - 1;
var character;
var pos;
return;
if (position.generated(node)) {
return;
}
/**
* Validate a single node.
*
* @param {Node} node - Node.
*/
function validate(node) {
var last = end(node).offset - 1;
var character;
var pos;
if (node.type !== 'definition') {
last--;
}
if (position.generated(node)) {
return;
}
while (last) {
character = contents.charAt(last);
if (node.type !== 'definition') {
last--;
}
if (/\s/.test(character)) {
last--;
} else {
break;
}
}
while (last) {
character = contents.charAt(last);
/*
* Not a title.
*/
if (/\s/.test(character)) {
last--;
} else {
break;
}
}
if (!(character in MARKERS)) {
return;
}
/* Not a title. */
if (!(character in MARKERS)) {
return;
}
if (!preferred) {
preferred = character;
} else if (preferred !== character) {
pos = location.toPosition(last + 1);
file.warn('Titles should use `' + (preferred === ')' ? '()' : preferred) + '` as a quote', pos);
}
if (!preferred) {
preferred = character;
} else if (preferred !== character) {
pos = location.toPosition(last + 1);
file.warn('Titles should use `' + (preferred === ')' ? '()' : preferred) + '` as a quote', pos);
}
visit(ast, 'link', validate);
visit(ast, 'image', validate);
visit(ast, 'definition', validate);
done();
}
}
/*
* Expose.
*/
module.exports = linkTitleStyle;

@@ -8,10 +8,21 @@ /**

* Warn when list item bullets are indented.
* @example
* <!-- Valid -->
*
* @example {"name": "valid.md"}
*
* Paragraph.
*
* * List item
* * List item
*
* <!-- Invalid -->
* * List item
* * List item
* @example {"name": "invalid.md", "label": "input"}
*
* Paragraph.
*
* * List item
* * List item
*
* @example {"name": "invalid.md", "label": "output"}
*
* 3:3: Incorrect indentation before bullet: remove 1 space
* 4:3: Incorrect indentation before bullet: remove 1 space
*/

@@ -21,16 +32,11 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
var plural = require('plur');
/*
* Methods.
*/
/* Expose. */
module.exports = listItemBulletIndent;
/* Methods. */
var start = position.start;

@@ -43,41 +49,31 @@

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function listItemBulletIndent(ast, file, preferred, done) {
var contents = file.toString();
function listItemBulletIndent(ast, file) {
var contents = file.toString();
visit(ast, 'list', function (node) {
var items = node.children;
visit(ast, 'list', function (node) {
var items = node.children;
items.forEach(function (item) {
var head = item.children[0];
var initial = start(item).offset;
var final = start(head).offset;
var indent;
items.forEach(function (item) {
var head = item.children[0];
var initial = start(item).offset;
var final = start(head).offset;
var indent;
if (position.generated(node)) {
return;
}
if (position.generated(node)) {
return;
}
indent = contents.slice(initial, final).match(/^\s*/)[0].length;
indent = contents.slice(initial, final).match(/^\s*/)[0].length;
if (indent !== 0) {
initial = start(head);
if (indent !== 0) {
initial = start(head);
file.warn('Incorrect indentation before bullet: remove ' + indent + ' ' + plural('space', indent), {
'line': initial.line,
'column': initial.column - indent
});
}
file.warn('Incorrect indentation before bullet: remove ' + indent + ' ' + plural('space', indent), {
line: initial.line,
column: initial.column - indent
});
}
});
done();
});
}
/*
* Expose.
*/
module.exports = listItemBulletIndent;

@@ -8,12 +8,16 @@ /**

* Warn when the content of a list item has mixed indentation.
* @example
* <!-- Valid -->
* * List item
*
* * Nested list item indented by 4 spaces
* @example {"name": "valid.md"}
*
* <!-- Invalid -->
* * List item
* 1. [x] Alpha
* 1. Bravo
*
* * Nested list item indented by 3 spaces
* @example {"name": "invalid.md", "label": "input"}
*
* 1. [x] Charlie
* 1. Delta
*
* @example {"name": "invalid.md", "label": "output"}
*
* 2:5: Don’t use mixed indentation for children, remove 1 space
*/

@@ -23,16 +27,11 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
var plural = require('plur');
/*
* Methods.
*/
/* Expose. */
module.exports = listItemContentIndent;
/* Methods. */
var start = position.start;

@@ -46,79 +45,60 @@

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function listItemContentIndent(ast, file, preferred, done) {
var contents = file.toString();
function listItemContentIndent(ast, file) {
var contents = file.toString();
visit(ast, 'listItem', function (node) {
var style;
visit(ast, 'listItem', function (node) {
var style;
node.children.forEach(function (item, index) {
var begin = start(item);
var column = begin.column;
var char;
var diff;
var word;
node.children.forEach(function (item, index) {
var begin = start(item);
var column = begin.column;
var char;
var diff;
var word;
if (position.generated(item)) {
return;
}
if (position.generated(item)) {
return;
}
/*
* Get indentation for the first child.
* Only the first item can have a checkbox,
* so here we remove that from the column.
*/
/* Get indentation for the first child.
* Only the first item can have a checkbox,
* so here we remove that from the column. */
if (index === 0) {
/* If there’s a checkbox before the content,
* look backwards to find the start of that
* checkbox. */
if (Boolean(node.checked) === node.checked) {
char = begin.offset - 1;
if (index === 0) {
/*
* If there’s a checkbox before the content,
* look backwards to find the start of that
* checkbox.
*/
while (contents.charAt(char) !== '[') {
char--;
}
if (Boolean(node.checked) === node.checked) {
char = begin.offset - 1;
column -= begin.offset - char;
}
while (contents.charAt(char) !== '[') {
char--;
}
style = column;
column -= begin.offset - char;
}
return;
}
style = column;
/* Warn for violating children. */
if (column !== style) {
diff = style - column;
word = diff > 0 ? 'add' : 'remove';
return;
}
diff = Math.abs(diff);
/*
* Warn for violating children.
*/
if (column !== style) {
diff = style - column;
word = diff > 0 ? 'add' : 'remove';
diff = Math.abs(diff);
file.warn(
'Don’t use mixed indentation for children, ' + word +
' ' + diff + ' ' + plural('space', diff),
{
'line': start(item).line,
'column': column
}
);
}
});
file.warn(
'Don’t use mixed indentation for children, ' + word +
' ' + diff + ' ' + plural('space', diff),
{
line: start(item).line,
column: column
}
);
}
});
done();
});
}
/*
* Expose.
*/
module.exports = listItemContentIndent;

@@ -12,31 +12,56 @@ /**

* default: `'tab-size'`.
* @example
* <!-- Valid when `tab-size` -->
*
* @example {"name": "valid.md", "setting": "tab-size"}
*
* * List
* item.
*
* Paragraph.
*
* 11. List
* item.
*
* <!-- Valid when `mixed` -->
* Paragraph.
*
* * List
* item.
*
* * List
* item.
*
* @example {"name": "valid.md", "setting": "mixed"}
*
* * List item.
*
* Paragraph.
*
* 11. List item
*
* Paragraph.
*
* * List
* item.
*
* 11. List
* * List
* item.
*
* <!-- Valid when `space` -->
* @example {"name": "valid.md", "setting": "space"}
*
* * List item.
*
* Paragraph.
*
* 11. List item
*
* Paragraph.
*
* * List
* item.
*
* 11. List
* item.
* * List
* item.
*
* @example {"name": "invalid.md", "setting": "invalid", "label": "output", "config": {"positionless": true}}
*
* 1:1: Invalid list-item indent style `invalid`: use either `'tab-size'`, `'space'`, or `'mixed'`
*/

@@ -48,24 +73,18 @@

/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
var plural = require('plur');
/*
* Methods.
*/
/* Expose. */
module.exports = listItemIndent;
/* Methods. */
var start = position.start;
/*
* Styles.
*/
/* Styles. */
var STYLES = {
'tab-size': true,
'mixed': true,
'space': true
'tab-size': true,
'mixed': true,
'space': true
};

@@ -84,73 +103,60 @@

*/
function listItemIndent(ast, file, preferred, done) {
var contents = file.toString();
function listItemIndent(ast, file, preferred) {
var contents = file.toString();
preferred = typeof preferred !== 'string' ? 'tab-size' : preferred;
preferred = typeof preferred === 'string' ? preferred : 'tab-size';
if (STYLES[preferred] !== true) {
file.fail('Invalid list-item indent style `' + preferred + '`: use either `\'tab-size\'`, `\'space\'`, or `\'mixed\'`');
done();
if (STYLES[preferred] !== true) {
file.fail('Invalid list-item indent style `' + preferred + '`: use either `\'tab-size\'`, `\'space\'`, or `\'mixed\'`');
return;
}
return;
visit(ast, 'list', function (node) {
var items = node.children;
if (position.generated(node)) {
return;
}
visit(ast, 'list', function (node) {
var items = node.children;
items.forEach(function (item) {
var head = item.children[0];
var initial = start(item).offset;
var final = start(head).offset;
var bulletSize;
var tab;
var marker;
var shouldBe;
var diff;
var word;
if (position.generated(node)) {
return;
}
marker = contents.slice(initial, final);
items.forEach(function (item) {
var head = item.children[0];
var initial = start(item).offset;
var final = start(head).offset;
var bulletSize;
var tab;
var marker;
var shouldBe;
var diff;
var word;
/* Support checkboxes. */
marker = marker.replace(/\[[x ]?\]\s*$/i, '');
marker = contents.slice(initial, final);
bulletSize = marker.trimRight().length;
tab = Math.ceil(bulletSize / 4) * 4;
/*
* Support checkboxes.
*/
if (preferred === 'tab-size') {
shouldBe = tab;
} else if (preferred === 'space') {
shouldBe = bulletSize + 1;
} else {
shouldBe = node.loose ? tab : bulletSize + 1;
}
marker = marker.replace(/\[[x ]?\]\s*$/i, '');
if (marker.length !== shouldBe) {
diff = shouldBe - marker.length;
word = diff > 0 ? 'add' : 'remove';
bulletSize = marker.trimRight().length;
tab = Math.ceil(bulletSize / 4) * 4;
diff = Math.abs(diff);
if (preferred === 'tab-size') {
shouldBe = tab;
} else if (preferred === 'space') {
shouldBe = bulletSize + 1;
} else {
shouldBe = node.loose ? tab : bulletSize + 1;
}
if (marker.length !== shouldBe) {
diff = shouldBe - marker.length;
word = diff > 0 ? 'add' : 'remove';
diff = Math.abs(diff);
file.warn(
'Incorrect list-item indent: ' + word +
' ' + diff + ' ' + plural('space', diff),
start(head)
);
}
});
file.warn(
'Incorrect list-item indent: ' + word +
' ' + diff + ' ' + plural('space', diff),
start(head)
);
}
});
done();
});
}
/*
* Expose.
*/
module.exports = listItemIndent;

@@ -9,4 +9,13 @@ /**

* when it should be loose, and vice versa.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* A tight list:
*
* - item 1
* - item 2
* - item 3
*
* A loose list:
*
* - Wrapped

@@ -19,8 +28,6 @@ * item

*
* <!-- Valid: -->
* - item 1
* - item 2
* - item 3
* @example {"name": "invalid.md", "label": "input"}
*
* <!-- Invalid: -->
* A tight list:
*
* - Wrapped

@@ -31,3 +38,4 @@ * item

*
* <!-- Invalid: -->
* A loose list:
*
* - item 1

@@ -38,2 +46,9 @@ *

* - item 3
*
* @example {"name": "invalid.md", "label": "output"}
*
* 4:9-5:1: Missing new line after list item
* 5:11-6:1: Missing new line after list item
* 11:1-12:1: Extraneous new line after list item
* 13:1-14:1: Extraneous new line after list item
*/

@@ -43,15 +58,10 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Methods.
*/
/* Expose. */
module.exports = listItemSpacing;
/* Methods. */
var start = position.start;

@@ -66,69 +76,53 @@ var end = position.end;

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function listItemSpacing(ast, file, preferred, done) {
visit(ast, 'list', function (node) {
var items = node.children;
var isTightList = true;
var indent = start(node).column;
var type;
function listItemSpacing(ast, file) {
visit(ast, 'list', function (node) {
var items = node.children;
var isTightList = true;
var indent = start(node).column;
var type;
if (position.generated(node)) {
return;
}
if (position.generated(node)) {
return;
}
items.forEach(function (item) {
var content = item.children;
var head = content[0];
var tail = content[content.length - 1];
var isLoose = (end(tail).line - start(head).line) > 0;
items.forEach(function (item) {
var content = item.children;
var head = content[0];
var tail = content[content.length - 1];
var isLoose = (end(tail).line - start(head).line) > 0;
if (isLoose) {
isTightList = false;
}
});
if (isLoose) {
isTightList = false;
}
});
type = isTightList ? 'tight' : 'loose';
type = isTightList ? 'tight' : 'loose';
items.forEach(function (item, index) {
var next = items[index + 1];
var isTight = end(item).column > indent;
items.forEach(function (item, index) {
var next = items[index + 1];
var isTight = end(item).column > indent;
/*
* Ignore last.
*/
/* Ignore last. */
if (!next) {
return;
}
if (!next) {
return;
}
/*
* Check if the list item's state does (not)
* match the list's state.
*/
if (isTight !== isTightList) {
if (type === 'loose') {
file.warn('Missing new line after list item', {
'start': end(item),
'end': start(next)
});
} else {
file.warn('Extraneous new line after list item', {
'start': end(item),
'end': start(next)
});
}
}
});
/* Check if the list item's state does (not)
* match the list's state. */
if (isTight !== isTightList) {
if (type === 'loose') {
file.warn('Missing new line after list item', {
start: end(item),
end: start(next)
});
} else {
file.warn('Extraneous new line after list item', {
start: end(item),
end: start(next)
});
}
}
});
done();
});
}
/*
* Expose.
*/
module.exports = listItemSpacing;

@@ -12,9 +12,16 @@ /**

* Ignores markdown syntax, only checks the plain text content.
* @example
* <!-- Valid, when set to `40` -->
* # Alpha bravo charlie delta echo
* # ![Alpha bravo charlie delta echo](http://example.com/nato.png)
*
* <!-- Invalid, when set to `40` -->
* # Alpha bravo charlie delta echo foxtrot
* @example {"name": "valid.md"}
*
* # Alpha bravo charlie delta echo foxtrot golf hotel
*
* # ![Alpha bravo charlie delta echo foxtrot golf hotel](http://example.com/nato.png)
*
* @example {"name": "invalid.md", "setting": 40, "label": "input"}
*
* # Alpha bravo charlie delta echo foxtrot golf hotel
*
* @example {"name": "invalid.md", "setting": 40, "label": "output"}
*
* 1:1-1:52: Use headings shorter than `40`
*/

@@ -24,12 +31,10 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var toString = require('mdast-util-to-string');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/* Expose. */
module.exports = maximumHeadingLength;
/**

@@ -44,22 +49,14 @@ * Warn when headings are too long.

*/
function maximumHeadingLength(ast, file, preferred, done) {
preferred = isNaN(preferred) || typeof preferred !== 'number' ? 60 : preferred;
function maximumHeadingLength(ast, file, preferred) {
preferred = isNaN(preferred) || typeof preferred !== 'number' ? 60 : preferred;
visit(ast, 'heading', function (node) {
if (position.generated(node)) {
return;
}
visit(ast, 'heading', function (node) {
if (position.generated(node)) {
return;
}
if (toString(node).length > preferred) {
file.warn('Use headings shorter than `' + preferred + '`', node);
}
});
done();
if (toString(node).length > preferred) {
file.warn('Use headings shorter than `' + preferred + '`', node);
}
});
}
/*
* Expose.
*/
module.exports = maximumHeadingLength;

@@ -13,20 +13,46 @@ /**

* code, link, images, and definitions.
* @example
* <!-- Valid, when set to `40` -->
* Alpha bravo charlie delta echo.
*
* Alpha bravo charlie delta echo [foxtrot](./foxtrot.html).
* @example {"name": "valid.md", "setting": 80, "config": {"positionless": true}}
*
* # Alpha bravo charlie delta echo foxtrot golf hotel.
* This line is simply not toooooooooooooooooooooooooooooooooooooooooooo
* long.
*
* # Alpha bravo charlie delta echo foxtrot golf hotel.
* This is also fine: <http://this-long-url-with-a-long-domain.co.uk/a-long-path?query=variables>
*
* | A | B | C | D | E | F | F | H |
* | ----- | ----- | ------- | ----- | ---- | ------- | ---- | ----- |
* | Alpha | bravo | charlie | delta | echo | foxtrot | golf | hotel |
* <http://this-link-is-fine.com>
*
* <!-- Invalid, when set to `40` -->
* Alpha bravo charlie delta echo foxtrot golf.
* [foo](http://this-long-url-with-a-long-domain-is-valid.co.uk/a-long-path?query=variables)
*
* Alpha bravo charlie delta echo [foxtrot](./foxtrot.html) golf.
* <http://this-long-url-with-a-long-domain-is-valid.co.uk/a-long-path?query=variables>
*
* ![foo](http://this-long-url-with-a-long-domain-is-valid.co.uk/a-long-path?query=variables)
*
* | An | exception | is | line | length | in | long | tables | because | those | can’t | just |
* | -- | --------- | -- | ---- | ------ | -- | ---- | ------ | ------- | ----- | ----- | ---- |
* | be | helped | | | | | | | | | | . |
*
* The following is also fine, because there is no white-space.
*
* <http://this-long-url-with-a-long-domain-is-invalid.co.uk/a-long-path?query=variables>.
*
* In addition, definitions are also fine:
*
* [foo]: <http://this-long-url-with-a-long-domain-is-invalid.co.uk/a-long-path?query=variables>
*
* @example {"name": "invalid.md", "setting": 80, "label": "input", "config": {"positionless": true}}
*
* This line is simply not tooooooooooooooooooooooooooooooooooooooooooooooooooooooo
* long.
*
* Just like thiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis one.
*
* And this one is also very wrong: because the link starts aaaaaaafter the column: <http://line.com>
*
* <http://this-long-url-with-a-long-domain-is-invalid.co.uk/a-long-path?query=variables> and such.
*
* @example {"name": "invalid.md", "setting": 80, "label": "output", "config": {"positionless": true}}
*
* 4:86: Line must be at most 80 characters
* 6:99: Line must be at most 80 characters
* 8:97: Line must be at most 80 characters
*/

@@ -36,15 +62,10 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Methods.
*/
/* Expose. */
module.exports = maximumLineLength;
/* Methods. */
var start = position.start;

@@ -54,17 +75,2 @@ var end = position.end;

/**
* Check if `node` is applicable, as in, if it should be
* ignored.
*
* @param {Node} node - Node to test.
* @return {boolean} - Whether or not `node` should be
* ignored.
*/
function isIgnored(node) {
return node.type === 'heading' ||
node.type === 'table' ||
node.type === 'code' ||
node.type === 'definition';
}
/**
* Warn when lines are too long. This rule is forgiving

@@ -77,116 +83,109 @@ * about lines which cannot be wrapped, such as code,

* @param {number?} [preferred=80] - Maximum line length.
* @param {Function} done - Callback.
*/
function maximumLineLength(ast, file, preferred, done) {
var style = preferred && preferred !== true ? preferred : 80;
var content = file.toString();
var matrix = content.split('\n');
var index = -1;
var length = matrix.length;
var lineLength;
function maximumLineLength(ast, file, preferred) {
var style = preferred && preferred !== true ? preferred : 80;
var content = file.toString();
var matrix = content.split('\n');
var index = -1;
var length = matrix.length;
var lineLength;
/**
* Whitelist from `initial` to `final`, zero-based.
*
* @param {number} initial - Start.
* @param {number} final - End.
*/
function whitelist(initial, final) {
initial--;
/* Next, white list nodes which cannot be wrapped. */
visit(ast, function (node) {
var applicable = isIgnored(node);
var initial = applicable && start(node).line;
var final = applicable && end(node).line;
while (++initial < final) {
matrix[initial] = '';
}
if (!applicable || position.generated(node)) {
return;
}
/*
* Next, white list nodes which cannot be wrapped.
*/
whitelist(initial - 1, final);
});
visit(ast, function (node) {
var applicable = isIgnored(node);
var initial = applicable && start(node).line;
var final = applicable && end(node).line;
visit(ast, 'link', validateLink);
visit(ast, 'image', validateLink);
if (!applicable || position.generated(node)) {
return;
}
/* Iterate over every line, and warn for
* violating lines. */
while (++index < length) {
lineLength = matrix[index].length;
whitelist(initial - 1, final);
});
if (lineLength > style) {
file.warn('Line must be at most ' + style + ' characters', {
line: index + 1,
column: lineLength + 1
});
}
}
/**
* Finally, whitelist URLs, but only if they occur at
* or after the wrap. However, when they do, and
* there’s white-space after it, they are not
* whitelisted.
*
* @param {Node} node - Node.
* @param {number} pos - Position of `node` in `parent`.
* @param {Node} parent - Parent of `node`.
*/
function validateLink(node, pos, parent) {
var next = parent.children[pos + 1];
var initial = start(node);
var final = end(node);
return;
/*
* Nothing to whitelist when generated.
*/
/**
* Whitelist from `initial` to `final`, zero-based.
*
* @param {number} initial - Start.
* @param {number} final - End.
*/
function whitelist(initial, final) {
initial--;
if (position.generated(node)) {
return;
}
while (++initial < final) {
matrix[initial] = '';
}
}
/*
* No whitelisting when starting after the border,
* or ending before it.
*/
/**
* Finally, whitelist URLs, but only if they occur at
* or after the wrap. However, when they do, and
* there’s white-space after it, they are not
* whitelisted.
*
* @param {Node} node - Node.
* @param {number} pos - Position of `node` in `parent`.
* @param {Node} parent - Parent of `node`.
*/
function validateLink(node, pos, parent) {
var next = parent.children[pos + 1];
var initial = start(node);
var final = end(node);
if (initial.column > style || final.column < style) {
return;
}
/* Nothing to whitelist when generated. */
if (position.generated(node)) {
return;
}
/*
* No whitelisting when there’s white-space after
* the link.
*/
if (
next &&
start(next).line === initial.line &&
(!next.value || /^(.+?[ \t].+?)/.test(next.value))
) {
return;
}
whitelist(initial.line - 1, final.line);
/* No whitelisting when starting after the border,
* or ending before it. */
if (initial.column > style || final.column < style) {
return;
}
visit(ast, 'link', validateLink);
visit(ast, 'image', validateLink);
/*
* Iterate over every line, and warn for
* violating lines.
*/
while (++index < length) {
lineLength = matrix[index].length;
if (lineLength > style) {
file.warn('Line must be at most ' + style + ' characters', {
'line': index + 1,
'column': lineLength + 1
});
}
/* No whitelisting when there’s white-space after
* the link. */
if (
next &&
start(next).line === initial.line &&
(!next.value || /^(.+?[ \t].+?)/.test(next.value))
) {
return;
}
done();
whitelist(initial.line - 1, final.line);
}
}
/*
* Expose.
/**
* Check if `node` is applicable, as in, if it should be
* ignored.
*
* @param {Node} node - Node to test.
* @return {boolean} - Whether or not `node` should be
* ignored.
*/
module.exports = maximumLineLength;
function isIgnored(node) {
return node.type === 'heading' ||
node.type === 'table' ||
node.type === 'code' ||
node.type === 'definition';
}

@@ -8,10 +8,16 @@ /**

* Warn for angle-bracketed links without protocol.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* <http://www.example.com>
* <mailto:foo@bar.com>
*
* <!-- Invalid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* <www.example.com>
* <foo@bar.com>
*
* @example {"name": "invalid.md", "label": "output"}
*
* 2:1-2:14: All automatic links must start with a protocol
*/

@@ -23,14 +29,11 @@

/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var toString = require('mdast-util-to-string');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Methods.
*/
/* Expose. */
module.exports = noAutoLinkWithoutProtocol;
/* Methods. */
var start = position.start;

@@ -49,12 +52,2 @@ var end = position.end;

/**
* Assert `node`s reference starts with a protocol.
*
* @param {Node} node - Node to test.
* @return {boolean} - Whether `node` has a protocol.
*/
function hasProtocol(node) {
return PROTOCOL.test(toString(node));
}
/**
* Warn for angle-bracketed links without protocol.

@@ -64,28 +57,28 @@ *

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noAutoLinkWithoutProtocol(ast, file, preferred, done) {
visit(ast, 'link', function (node) {
var head = start(node.children[0]).column;
var tail = end(node.children[node.children.length - 1]).column;
var initial = start(node).column;
var final = end(node).column;
function noAutoLinkWithoutProtocol(ast, file) {
visit(ast, 'link', function (node) {
var head = start(node.children[0]).column;
var tail = end(node.children[node.children.length - 1]).column;
var initial = start(node).column;
var final = end(node).column;
if (position.generated(node)) {
return;
}
if (position.generated(node)) {
return;
}
if (initial === head - 1 && final === tail + 1 && !hasProtocol(node)) {
file.warn('All automatic links must start with a protocol', node);
}
});
done();
if (initial === head - 1 && final === tail + 1 && !hasProtocol(node)) {
file.warn('All automatic links must start with a protocol', node);
}
});
}
/*
* Expose.
/**
* Assert `node`s reference starts with a protocol.
*
* @param {Node} node - Node to test.
* @return {boolean} - Whether `node` has a protocol.
*/
module.exports = noAutoLinkWithoutProtocol;
function hasProtocol(node) {
return PROTOCOL.test(toString(node));
}

@@ -8,4 +8,5 @@ /**

* Warn when blank lines without carets are found in a blockquote.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* > Foo...

@@ -15,6 +16,11 @@ * >

*
* <!-- Invalid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* > Foo...
*
* > ...Bar.
*
* @example {"name": "invalid.md", "label": "output"}
*
* 2:1: Missing caret in blockquote
*/

@@ -24,12 +30,10 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var vfileLocation = require('vfile-location');
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/* Expose. */
module.exports = noBlockquoteWithoutCaret;
/**

@@ -41,53 +45,43 @@ * Warn when blank lines without carets are found in a

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noBlockquoteWithoutCaret(ast, file, preferred, done) {
var contents = file.toString();
var location = vfileLocation(file);
var last = contents.length;
function noBlockquoteWithoutCaret(ast, file) {
var contents = file.toString();
var location = vfileLocation(file);
var last = contents.length;
visit(ast, 'blockquote', function (node) {
var start = position.start(node).line;
var indent = node.position && node.position.indent;
visit(ast, 'blockquote', function (node) {
var start = position.start(node).line;
var indent = node.position && node.position.indent;
if (position.generated(node) || !indent || !indent.length) {
return;
}
if (position.generated(node) || !indent || !indent.length) {
return;
}
indent.forEach(function (column, n) {
var character;
var line = start + n + 1;
var offset = location.toOffset({
'line': line,
'column': column
}) - 1;
indent.forEach(function (column, n) {
var character;
var line = start + n + 1;
var offset = location.toOffset({
line: line,
column: column
}) - 1;
while (++offset < last) {
character = contents.charAt(offset);
while (++offset < last) {
character = contents.charAt(offset);
if (character === '>') {
return;
}
if (character === '>') {
return;
}
/* istanbul ignore else - just for safety */
if (character !== ' ' && character !== '\t') {
break;
}
}
/* istanbul ignore else - just for safety */
if (character !== ' ' && character !== '\t') {
break;
}
}
file.warn('Missing caret in blockquote', {
'line': line,
'column': column
});
});
file.warn('Missing caret in blockquote', {
line: line,
column: column
});
});
done();
});
}
/*
* Expose.
*/
module.exports = noBlockquoteWithoutCaret;

@@ -9,4 +9,5 @@ /**

* needed between a list and indented code, and two lists.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* Foo...

@@ -16,3 +17,13 @@ *

*
* <!-- Invalid: -->
* @example {"name": "valid-for-code.md"}
*
* Paragraph.
*
* * List
*
*
* bravo();
*
* @example {"name": "invalid.md", "label": "input"}
*
* Foo...

@@ -22,2 +33,6 @@ *

* ...Bar.
*
* @example {"name": "invalid.md", "label": "output"}
*
* 4:1: Remove 1 line before node
*/

@@ -29,14 +44,11 @@

/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
var plural = require('plur');
/*
* Constants.
*/
/* Expose. */
module.exports = noConsecutiveBlankLines;
/* Constants. */
var MAX = 2;

@@ -51,91 +63,67 @@

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noConsecutiveBlankLines(ast, file, preferred, done) {
/**
* Compare the difference between `start` and `end`,
* and warn when that difference exceeds `max`.
*
* @param {Position} start - Initial.
* @param {Position} end - Final.
* @param {number} max - Threshold.
*/
function compare(start, end, max) {
var diff = end.line - start.line;
var word = diff > 0 ? 'before' : 'after';
function noConsecutiveBlankLines(ast, file) {
visit(ast, function (node) {
var children = node.children;
var head = children && children[0];
var tail = children && children[children.length - 1];
diff = Math.abs(diff) - max;
if (diff > 0) {
file.warn('Remove ' + diff + ' ' + plural('line', diff) + ' ' + word + ' node', end);
}
if (position.generated(node)) {
return;
}
visit(ast, function (node) {
var children = node.children;
var head = children && children[0];
var tail = children && children[children.length - 1];
if (head && !position.generated(head)) {
/* Compare parent and first child. */
compare(position.start(node), position.start(head), 0);
if (position.generated(node)) {
return;
/* Compare between each child. */
children.forEach(function (child, index) {
var prev = children[index - 1];
var max = MAX;
if (
!prev ||
position.generated(prev) ||
position.generated(child)
) {
return;
}
if (head && !position.generated(head)) {
/*
* Compare parent and first child.
*/
if (
(prev.type === 'list' && child.type === 'list') ||
(child.type === 'code' && prev.type === 'list' && !child.lang)
) {
max++;
}
compare(position.start(node), position.start(head), 0);
compare(position.end(prev), position.start(child), max);
});
/*
* Compare between each child.
*/
/* Compare parent and last child. */
if (tail !== head && !position.generated(tail)) {
compare(position.end(node), position.end(tail), 1);
}
}
});
children.forEach(function (child, index) {
var prev = children[index - 1];
var max = MAX;
return;
if (
!prev ||
position.generated(prev) ||
position.generated(child)
) {
return;
}
/**
* Compare the difference between `start` and `end`,
* and warn when that difference exceeds `max`.
*
* @param {Position} start - Initial.
* @param {Position} end - Final.
* @param {number} max - Threshold.
*/
function compare(start, end, max) {
var diff = end.line - start.line;
var word = diff > 0 ? 'before' : 'after';
if (
(
prev.type === 'list' &&
child.type === 'list'
) ||
(
child.type === 'code' &&
prev.type === 'list' &&
!child.lang
)
) {
max++;
}
diff = Math.abs(diff) - max;
compare(position.end(prev), position.start(child), max);
});
/*
* Compare parent and last child.
*/
if (tail !== head && !position.generated(tail)) {
compare(position.end(node), position.end(tail), 1);
}
}
});
done();
if (diff > 0) {
file.warn('Remove ' + diff + ' ' + plural('line', diff) + ' ' + word + ' node', end);
}
}
}
/*
* Expose.
*/
module.exports = noConsecutiveBlankLines;

@@ -8,10 +8,16 @@ /**

* Warn when duplicate definitions are found.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* [foo]: bar
* [baz]: qux
*
* <!-- Invalid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* [foo]: bar
* [foo]: qux
*
* @example {"name": "invalid.md", "label": "output"}
*
* 2:1-2:11: Do not use definitions with the same identifier (1:1)
*/

@@ -21,11 +27,9 @@

/* eslint-env commonjs */
/* Dependencies. */
var position = require('unist-util-position');
var visit = require('unist-util-visit');
/*
* Dependencies.
*/
/* Expose. */
module.exports = noDuplicateDefinitions;
var position = require('mdast-util-position');
var visit = require('unist-util-visit');
/**

@@ -38,44 +42,36 @@ * Warn when definitions with equal content are found.

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noDuplicateDefinitions(ast, file, preferred, done) {
var map = {};
function noDuplicateDefinitions(ast, file) {
var map = {};
/**
* Check `node`.
*
* @param {Node} node - Node.
*/
function validate(node) {
var duplicate = map[node.identifier];
var pos;
visit(ast, 'definition', validate);
visit(ast, 'footnoteDefinition', validate);
if (position.generated(node)) {
return;
}
return;
if (duplicate && duplicate.type) {
pos = position.start(duplicate);
/**
* Check `node`.
*
* @param {Node} node - Node.
*/
function validate(node) {
var duplicate = map[node.identifier];
var pos;
file.warn(
'Do not use definitions with the same identifier (' +
pos.line + ':' + pos.column + ')',
node
);
}
if (position.generated(node)) {
return;
}
map[node.identifier] = node;
if (duplicate && duplicate.type) {
pos = position.start(duplicate);
file.warn(
'Do not use definitions with the same identifier (' +
pos.line + ':' + pos.column + ')',
node
);
}
visit(ast, 'definition', validate);
visit(ast, 'footnoteDefinition', validate);
done();
map[node.identifier] = node;
}
}
/*
* Expose.
*/
module.exports = noDuplicateDefinitions;

@@ -8,4 +8,5 @@ /**

* Warn when duplicate headings are found.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* # Foo

@@ -15,3 +16,4 @@ *

*
* <!-- Invalid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* # Foo

@@ -22,2 +24,7 @@ *

* ## [Foo](http://foo.com/bar)
*
* @example {"name": "invalid.md", "label": "output"}
*
* 3:1-3:7: Do not use headings with similar content (1:1)
* 5:1-5:29: Do not use headings with similar content (3:1)
*/

@@ -29,10 +36,10 @@

/*
* Dependencies.
*/
var position = require('mdast-util-position');
/* Dependencies. */
var position = require('unist-util-position');
var visit = require('unist-util-visit');
var toString = require('mdast-util-to-string');
/* Expose. */
module.exports = noDuplicateHeadings;
/**

@@ -45,37 +52,27 @@ * Warn when headings with equal content are found.

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noDuplicateHeadings(ast, file, preferred, done) {
var map = {};
function noDuplicateHeadings(ast, file) {
var map = {};
visit(ast, 'heading', function (node) {
var value = toString(node).toUpperCase();
var duplicate = map[value];
var pos;
visit(ast, 'heading', function (node) {
var value = toString(node).toUpperCase();
var duplicate = map[value];
var pos;
if (position.generated(node)) {
return;
}
if (position.generated(node)) {
return;
}
if (duplicate && duplicate.type === 'heading') {
pos = position.start(duplicate);
if (duplicate && duplicate.type === 'heading') {
pos = position.start(duplicate);
file.warn(
'Do not use headings with similar content (' +
pos.line + ':' + pos.column + ')',
node
);
}
file.warn(
'Do not use headings with similar content (' +
pos.line + ':' + pos.column + ')',
node
);
}
map[value] = node;
});
done();
map[value] = node;
});
}
/*
* Expose.
*/
module.exports = noDuplicateHeadings;

@@ -9,4 +9,5 @@ /**

* a paragraph.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* # Foo

@@ -16,6 +17,11 @@ *

*
* <!-- Invalid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* *Foo*
*
* Bar.
*
* @example {"name": "invalid.md", "label": "output"}
*
* 1:1-1:6: Don’t use emphasis to introduce a section, use a heading
*/

@@ -25,11 +31,9 @@

/* eslint-env commonjs */
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('unist-util-position');
/*
* Dependencies.
*/
/* Expose. */
module.exports = noEmphasisAsHeading;
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
/**

@@ -41,34 +45,24 @@ * Warn when a section (a new paragraph) is introduced

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noEmphasisAsHeading(ast, file, preferred, done) {
visit(ast, 'paragraph', function (node, index, parent) {
var children = node.children;
var child = children[0];
var prev = parent.children[index - 1];
var next = parent.children[index + 1];
function noEmphasisAsHeading(ast, file) {
visit(ast, 'paragraph', function (node, index, parent) {
var children = node.children;
var child = children[0];
var prev = parent.children[index - 1];
var next = parent.children[index + 1];
if (position.generated(node)) {
return;
}
if (position.generated(node)) {
return;
}
if (
(!prev || prev.type !== 'heading') &&
next &&
next.type === 'paragraph' &&
children.length === 1 &&
(child.type === 'emphasis' || child.type === 'strong')
) {
file.warn('Don’t use emphasis to introduce a section, use a heading', node);
}
});
done();
if (
(!prev || prev.type !== 'heading') &&
next &&
next.type === 'paragraph' &&
children.length === 1 &&
(child.type === 'emphasis' || child.type === 'strong')
) {
file.warn('Don’t use emphasis to introduce a section, use a heading', node);
}
});
}
/*
* Expose.
*/
module.exports = noEmphasisAsHeading;

@@ -8,5 +8,16 @@ /**

* Warn when file name start with an article.
* @example
* Valid: article.md
* Invalid: an-article.md, a-article.md, , the-article.md
*
* @example {"name": "title.md"}
*
* @example {"name": "a-title.md", "label": "output", "config": {"positionless": true}}
*
* 1:1: Do not start file names with `a`
*
* @example {"name": "the-title.md", "label": "output", "config": {"positionless": true}}
*
* 1:1: Do not start file names with `the`
*
* @example {"name": "an-article.md", "label": "output", "config": {"positionless": true}}
*
* 1:1: Do not start file names with `an`
*/

@@ -16,3 +27,4 @@

/* eslint-env commonjs */
/* Expose. */
module.exports = noFileNameArticles;

@@ -24,19 +36,9 @@ /**

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noFileNameArticles(ast, file, preferred, done) {
var match = file.filename && file.filename.match(/^(the|an?)\b/i);
function noFileNameArticles(ast, file) {
var match = file.filename && file.filename.match(/^(the|an?)\b/i);
if (match) {
file.warn('Do not start file names with `' + match[0] + '`');
}
done();
if (match) {
file.warn('Do not start file names with `' + match[0] + '`');
}
}
/*
* Expose.
*/
module.exports = noFileNameArticles;

@@ -8,5 +8,8 @@ /**

* Warn when file names contain consecutive dashes.
* @example
* Invalid: docs/plug--ins.md
* Valid: docs/plug-ins.md
*
* @example {"name": "plug-ins.md"}
*
* @example {"name": "plug--ins.md", "label": "output", "config": {"positionless": true}}
*
* 1:1: Do not use consecutive dashes in a file name
*/

@@ -16,3 +19,4 @@

/* eslint-env commonjs */
/* Expose. */
module.exports = noFileNameConsecutiveDashes;

@@ -24,17 +28,7 @@ /**

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noFileNameConsecutiveDashes(ast, file, preferred, done) {
if (file.filename && /-{2,}/.test(file.filename)) {
file.warn('Do not use consecutive dashes in a file name');
}
done();
function noFileNameConsecutiveDashes(ast, file) {
if (file.filename && /-{2,}/.test(file.filename)) {
file.warn('Do not use consecutive dashes in a file name');
}
}
/*
* Expose.
*/
module.exports = noFileNameConsecutiveDashes;

@@ -17,5 +17,18 @@ /**

* warning.
* @example
* Invalid: plug_ins.md, plug ins.md.
* Valid: plug-ins.md, plugins.md.
*
* @example {"name": "plug-ins.md"}
*
* @example {"name": "plugins.md"}
*
* @example {"name": "plug_ins.md", "label": "output", "config": {"positionless": true}}
*
* 1:1: Do not use `_` in a file name
*
* @example {"name": "README.md", "label": "output", "setting": "\\.a-z0-9", "config": {"positionless": true}}
*
* 1:1: Do not use `R` in a file name
*
* @example {"name": "plug ins.md", "label": "output", "config": {"positionless": true}}
*
* 1:1: Do not use ` ` in a file name
*/

@@ -25,3 +38,4 @@

/* eslint-env commonjs */
/* Expose. */
module.exports = noFileNameIrregularCharacters;

@@ -34,26 +48,20 @@ /**

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
* @param {string|RegExp} preferred - RegExp, or string of
* characters (in which case it’s wrapped in
* `new RegExp('[^' + preferred + ']')`), that matches
* characters which should not be allowed.
*/
function noFileNameIrregularCharacters(ast, file, preferred, done) {
var expression = preferred || /[^\\.a-zA-Z0-9-]/;
var match;
function noFileNameIrregularCharacters(ast, file, preferred) {
var expression = preferred || /[^\\\.a-zA-Z0-9-]/;
var match;
if (typeof expression === 'string') {
expression = new RegExp('[^' + expression + ']');
}
if (typeof expression === 'string') {
expression = new RegExp('[^' + expression + ']');
}
match = file.filename && file.filename.match(expression);
match = file.filename && file.filename.match(expression);
if (match) {
file.warn('Do not use `' + match[0] + '` in a file name');
}
done();
if (match) {
file.warn('Do not use `' + match[0] + '` in a file name');
}
}
/*
* Expose.
*/
module.exports = noFileNameIrregularCharacters;

@@ -9,5 +9,10 @@ /**

* characters.
* @example
* Invalid: Readme.md
* Valid: README.md, readme.md
*
* @example {"name": "README.md"}
*
* @example {"name": "readme.md"}
*
* @example {"name": "Readme.md", "label": "output", "config": {"positionless": true}}
*
* 1:1: Do not mix casing in file names
*/

@@ -17,3 +22,4 @@

/* eslint-env commonjs */
/* Expose. */
module.exports = noFileNameMixedCase;

@@ -26,19 +32,9 @@ /**

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noFileNameMixedCase(ast, file, preferred, done) {
var name = file.filename;
function noFileNameMixedCase(ast, file) {
var name = file.filename;
if (name && !(name === name.toLowerCase() || name === name.toUpperCase())) {
file.warn('Do not mix casing in file names');
}
done();
if (name && !(name === name.toLowerCase() || name === name.toUpperCase())) {
file.warn('Do not mix casing in file names');
}
}
/*
* Expose.
*/
module.exports = noFileNameMixedCase;

@@ -8,5 +8,12 @@ /**

* Warn when file names contain initial or final dashes.
* @example
* Invalid: -readme.md, readme-.md
* Valid: readme.md
*
* @example {"name": "readme.md"}
*
* @example {"name": "-readme.md", "label": "output", "config": {"positionless": true}}
*
* 1:1: Do not use initial or final dashes in a file name
*
* @example {"name": "readme-.md", "label": "output", "config": {"positionless": true}}
*
* 1:1: Do not use initial or final dashes in a file name
*/

@@ -16,3 +23,4 @@

/* eslint-env commonjs */
/* Expose. */
module.exports = noFileNameOuterDashes;

@@ -24,17 +32,7 @@ /**

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noFileNameOuterDashes(ast, file, preferred, done) {
if (file.filename && /^-|-$/.test(file.filename)) {
file.warn('Do not use initial or final dashes in a file name');
}
done();
function noFileNameOuterDashes(ast, file) {
if (file.filename && /^-|-$/.test(file.filename)) {
file.warn('Do not use initial or final dashes in a file name');
}
}
/*
* Expose.
*/
module.exports = noFileNameOuterDashes;

@@ -8,5 +8,17 @@ /**

* Warn when a heading’s content is indented.
* @example
*
* @example {"name": "valid.md"}
*
* <!-- Note: the middle-dots represent spaces -->
* <!-- Invalid: -->
*
* #·Foo
*
* ## Bar·##
*
* ##·Baz
*
* @example {"name": "invalid.md", "label": "input"}
*
* <!-- Note: the middle-dots represent spaces -->
*
* #··Foo

@@ -18,8 +30,22 @@ *

*
* <!-- Valid: -->
* #·Foo
* @example {"name": "invalid.md", "label": "output"}
*
* ## Bar·##
* 3:4: Remove 1 space before this heading’s content
* 5:7: Remove 1 space after this heading’s content
* 7:7: Remove 1 space before this heading’s content
*
* ##·Baz
* @example {"name": "empty-heading.md"}
*
* #··
*
* @example {"name": "tight.md", "config":{"pedantic":true}, "label": "input"}
*
* In pedantic mode, headings without spacing can also be detected:
*
* ##No spacing left, too much right··##
*
* @example {"name": "tight.md", "label": "output"}
*
* 3:3: Add 1 space before this heading’s content
* 3:34: Remove 1 space after this heading’s content
*/

@@ -29,17 +55,12 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var style = require('mdast-util-heading-style');
var plural = require('plur');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Methods.
*/
/* Expose. */
module.exports = noHeadingContentIndent;
/* Methods. */
var start = position.start;

@@ -55,91 +76,81 @@ var end = position.end;

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noHeadingContentIndent(ast, file, preferred, done) {
var contents = file.toString();
function noHeadingContentIndent(ast, file) {
var contents = file.toString();
visit(ast, 'heading', function (node) {
var depth = node.depth;
var children = node.children;
var type = style(node, 'atx');
var head;
var initial;
var final;
var diff;
var word;
var index;
var char;
visit(ast, 'heading', function (node) {
var depth = node.depth;
var children = node.children;
var type = style(node, 'atx');
var head;
var initial;
var final;
var diff;
var word;
var index;
var char;
if (position.generated(node)) {
return;
}
if (position.generated(node)) {
return;
}
if (type === 'atx' || type === 'atx-closed') {
initial = start(node);
index = initial.offset;
char = contents.charAt(index);
if (type === 'atx' || type === 'atx-closed') {
initial = start(node);
index = initial.offset;
char = contents.charAt(index);
while (char && char !== '#') {
index++;
char = contents.charAt(index);
}
while (char && char !== '#') {
index++;
char = contents.charAt(index);
}
/* CR/LF bug: wooorm/remark#195. */
if (!char) {
return;
}
/* istanbul ignore if - CR/LF bug: wooorm/remark#195. */
if (!char) {
return;
}
index = depth + (index - initial.offset);
head = start(children[0]).column;
index = depth + (index - initial.offset);
head = start(children[0]).column;
/*
* Ignore empty headings.
*/
/*
* Ignore empty headings.
*/
if (!head) {
return;
}
if (!head) {
return;
}
diff = head - initial.column - 1 - index;
diff = head - initial.column - 1 - index;
if (diff) {
word = diff > 0 ? 'Remove' : 'Add';
diff = Math.abs(diff);
if (diff) {
word = diff > 0 ? 'Remove' : 'Add';
diff = Math.abs(diff);
file.warn(
word + ' ' + diff + ' ' + plural('space', diff) +
' before this heading’s content',
start(children[0])
);
}
}
file.warn(
word + ' ' + diff + ' ' + plural('space', diff) +
' before this heading’s content',
start(children[0])
);
}
}
/*
* Closed ATX-heading always must have a space
* between their content and the final hashes,
* thus, there is no `add x spaces`.
*/
/*
* Closed ATX-heading always must have a space
* between their content and the final hashes,
* thus, there is no `add x spaces`.
*/
if (type === 'atx-closed') {
final = end(children[children.length - 1]);
diff = end(node).column - final.column - 1 - depth;
if (type === 'atx-closed') {
final = end(children[children.length - 1]);
diff = end(node).column - final.column - 1 - depth;
if (diff) {
file.warn(
'Remove ' + diff + ' ' + plural('space', diff) +
' after this heading’s content',
final
);
}
}
});
done();
if (diff) {
file.warn(
'Remove ' + diff + ' ' + plural('space', diff) +
' after this heading’s content',
final
);
}
}
});
}
/*
* Expose.
*/
module.exports = noHeadingContentIndent;

@@ -8,5 +8,21 @@ /**

* Warn when a heading is indented.
* @example
*
* @example {"name": "valid.md"}
*
* <!-- Note: the middle-dots represent spaces -->
* <!-- Invalid: -->
*
* #·Hello world
*
* Foo
* -----
*
* #·Hello world·#
*
* Bar
* =====
*
* @example {"name": "invalid.md", "label": "input"}
*
* <!-- Note: the middle-dots represent spaces -->
*
* ···# Hello world

@@ -22,12 +38,8 @@ *

*
* <!-- Valid: -->
* # Hello world
* @example {"name": "invalid.md", "label": "output"}
*
* Foo
* -----
*
* # Hello world #
*
* Bar
* =====
* 3:4: Remove 3 spaces before this heading
* 5:2: Remove 1 space before this heading
* 8:2: Remove 1 space before this heading
* 10:4: Remove 3 spaces before this heading
*/

@@ -37,16 +49,11 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var plural = require('plur');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Methods.
*/
/* Expose. */
module.exports = noHeadingIndent;
/* Methods. */
var start = position.start;

@@ -60,49 +67,39 @@

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noHeadingIndent(ast, file, preferred, done) {
var contents = file.toString();
var length = contents.length;
function noHeadingIndent(ast, file) {
var contents = file.toString();
var length = contents.length;
visit(ast, 'heading', function (node) {
var initial = start(node);
var begin = initial.offset;
var index = begin - 1;
var character;
var diff;
visit(ast, 'heading', function (node) {
var initial = start(node);
var begin = initial.offset;
var index = begin - 1;
var character;
var diff;
if (position.generated(node)) {
return;
}
if (position.generated(node)) {
return;
}
while (++index < length) {
character = contents.charAt(index);
while (++index < length) {
character = contents.charAt(index);
if (character !== ' ' && character !== '\t') {
break;
}
}
if (character !== ' ' && character !== '\t') {
break;
}
}
diff = index - begin;
diff = index - begin;
if (diff) {
file.warn(
'Remove ' + diff + ' ' + plural('space', diff) +
' before this heading',
{
'line': initial.line,
'column': initial.column + diff
}
);
if (diff) {
file.warn(
'Remove ' + diff + ' ' + plural('space', diff) +
' before this heading',
{
line: initial.line,
column: initial.column + diff
}
});
done();
);
}
});
}
/*
* Expose.
*/
module.exports = noHeadingIndent;

@@ -14,4 +14,13 @@ /**

* be careful for escapes and dashes.
* @example
* <!-- Invalid: -->
*
* @example {"name": "valid.md"}
*
* # Hello
*
* @example {"name": "valid.md", "setting": ",;:!?"}
*
* # Hello...
*
* @example {"name": "invalid.md", "label": "input"}
*
* # Hello:

@@ -27,4 +36,9 @@ *

*
* <!-- Valid: -->
* # Hello
* @example {"name": "invalid.md", "label": "output"}
*
* 1:1-1:9: Don’t add a trailing `:` to headings
* 3:1-3:9: Don’t add a trailing `?` to headings
* 5:1-5:9: Don’t add a trailing `!` to headings
* 7:1-7:9: Don’t add a trailing `,` to headings
* 9:1-9:9: Don’t add a trailing `;` to headings
*/

@@ -34,12 +48,10 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
var toString = require('mdast-util-to-string');
/* Expose. */
module.exports = noHeadingPunctuation;
/**

@@ -51,28 +63,19 @@ * Warn when headings end in some characters.

* @param {string?} [preferred='\\.,;:!?'] - Group of characters.
* @param {Function} done - Callback.
*/
function noHeadingPunctuation(ast, file, preferred, done) {
preferred = typeof preferred === 'string' ? preferred : '\\.,;:!?';
function noHeadingPunctuation(ast, file, preferred) {
preferred = typeof preferred === 'string' ? preferred : '\\.,;:!?';
visit(ast, 'heading', function (node) {
var value = toString(node);
visit(ast, 'heading', function (node) {
var value = toString(node);
if (position.generated(node)) {
return;
}
if (position.generated(node)) {
return;
}
value = value.charAt(value.length - 1);
value = value.charAt(value.length - 1);
if (new RegExp('[' + preferred + ']').test(value)) {
file.warn('Don’t add a trailing `' + value + '` to headings', node);
}
});
done();
if (new RegExp('[' + preferred + ']').test(value)) {
file.warn('Don’t add a trailing `' + value + '` to headings', node);
}
});
}
/*
* Expose.
*/
module.exports = noHeadingPunctuation;

@@ -11,8 +11,16 @@ /**

* because markdown doesn’t have native comments.
* @example
* <!-- Invalid: -->
*
* @example {"name": "valid.md"}
*
* # Hello
*
* <!--Comments are also OK-->
*
* @example {"name": "invalid.md", "label": "input"}
*
* <h1>Hello</h1>
*
* <!-- Valid: -->
* # Hello
* @example {"name": "invalid.md", "label": "output"}
*
* 1:1-1:15: Do not use HTML in markdown
*/

@@ -22,11 +30,9 @@

/* eslint-env commonjs */
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('unist-util-position');
/*
* Dependencies.
*/
/* Expose. */
module.exports = html;
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
/**

@@ -41,11 +47,9 @@ * Warn when HTML nodes are used.

function html(ast, file, preferred, done) {
visit(ast, 'html', function (node) {
if (!position.generated(node) && !/^\s*<!--/.test(node.value)) {
file.warn('Do not use HTML in markdown', node);
}
});
visit(ast, 'html', function (node) {
if (!position.generated(node) && !/^\s*<!--/.test(node.value)) {
file.warn('Do not use HTML in markdown', node);
}
});
done();
done();
}
module.exports = html;

@@ -11,8 +11,16 @@ /**

* Warns for emphasis, strong, delete, image, and link.
* @example
* <!-- Invalid: -->
* * Hello *, [ world ](http://foo.bar/baz)
*
* <!-- Valid: -->
* *Hello*, [world](http://foo.bar/baz)
* @example {"name": "valid.md"}
*
* Alpha, *bravo*, _charlie_, [delta](http://echo.fox/trot)
*
* @example {"name": "invalid.md", "label": "input"}
*
* Alpha, * bravo *, _ charlie _, [ delta ](http://echo.fox/trot)
*
* @example {"name": "invalid.md", "label": "output"}
*
* 1:8-1:17: Don’t pad `emphasis` with inner spaces
* 1:19-1:30: Don’t pad `emphasis` with inner spaces
* 1:32-1:63: Don’t pad `link` with inner spaces
*/

@@ -22,12 +30,10 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
var toString = require('mdast-util-to-string');
/* Expose. */
module.exports = noInlinePadding;
/**

@@ -39,36 +45,26 @@ * Warn when inline nodes are padded with spaces between

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noInlinePadding(ast, file, preferred, done) {
visit(ast, function (node) {
var type = node.type;
var contents;
function noInlinePadding(ast, file) {
visit(ast, function (node) {
var type = node.type;
var contents;
if (position.generated(node)) {
return;
}
if (position.generated(node)) {
return;
}
if (
type === 'emphasis' ||
type === 'strong' ||
type === 'delete' ||
type === 'image' ||
type === 'link'
) {
contents = toString(node);
if (
type === 'emphasis' ||
type === 'strong' ||
type === 'delete' ||
type === 'image' ||
type === 'link'
) {
contents = toString(node);
if (contents.charAt(0) === ' ' || contents.charAt(contents.length - 1) === ' ') {
file.warn('Don’t pad `' + type + '` with inner spaces', node);
}
}
});
done();
if (contents.charAt(0) === ' ' || contents.charAt(contents.length - 1) === ' ') {
file.warn('Don’t pad `' + type + '` with inner spaces', node);
}
}
});
}
/*
* Expose.
*/
module.exports = noInlinePadding;

@@ -8,8 +8,17 @@ /**

* Warn when URLs without angle-brackets are used.
* @example
* <!-- Invalid: -->
*
* @example {"name": "valid.md"}
*
* <http://foo.bar/baz>
* <mailto:qux@quux.com>
*
* @example {"name": "invalid.md", "label": "input"}
*
* http://foo.bar/baz
*
* <!-- Valid: -->
* <http://foo.bar/baz>
* mailto:qux@quux.com
*
* @example {"name": "invalid.md", "label": "output"}
*
* 1:1-1:19: Don’t use literal URLs without angle brackets
*/

@@ -19,23 +28,15 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var toString = require('mdast-util-to-string');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Methods.
*/
/* Expose. */
module.exports = noLiteralURLs;
/* Methods. */
var start = position.start;
var end = position.end;
/*
* Constants.
*/
/* Constants. */
var MAILTO = 'mailto:';

@@ -48,33 +49,23 @@

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noLiteralURLs(ast, file, preferred, done) {
visit(ast, 'link', function (node) {
var head = start(node.children[0]).column;
var tail = end(node.children[node.children.length - 1]).column;
var initial = start(node).column;
var final = end(node).column;
var value = toString(node);
function noLiteralURLs(ast, file) {
visit(ast, 'link', function (node) {
var head = start(node.children[0]).column;
var tail = end(node.children[node.children.length - 1]).column;
var initial = start(node).column;
var final = end(node).column;
var value = toString(node);
if (position.generated(node)) {
return;
}
if (position.generated(node)) {
return;
}
if (
initial === head &&
final === tail &&
(value === node.url || value == MAILTO + node.url)
) {
file.warn('Don’t use literal URLs without angle brackets', node);
}
});
done();
if (
initial === head &&
final === tail &&
(node.url === MAILTO + value || node.url === value)
) {
file.warn('Don’t use literal URLs without angle brackets', node);
}
});
}
/*
* Expose.
*/
module.exports = noLiteralURLs;

@@ -8,11 +8,17 @@ /**

* Warn for missing blank lines before a block node.
* @example
* <!-- Invalid: -->
*
* @example {"name": "valid.md"}
*
* # Foo
*
* ## Bar
*
* <!-- Valid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* # Foo
* ## Bar
*
* ## Bar
* @example {"name": "invalid.md", "label": "output"}
*
* 2:1-2:7: Missing blank line before block node
*/

@@ -22,30 +28,8 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/**
* Check if `node` is an applicable block-level node.
*
* @param {Node} node - Node to test.
* @return {boolean} - Whether or not `node` is applicable.
*/
function isApplicable(node) {
return [
'paragraph',
'blockquote',
'heading',
'code',
'yaml',
'html',
'list',
'table',
'thematicBreak'
].indexOf(node.type) !== -1;
}
/* Expose. */
module.exports = noMissingBlankLines;

@@ -58,30 +42,40 @@ /**

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noMissingBlankLines(ast, file, preferred, done) {
visit(ast, function (node, index, parent) {
var next = parent && parent.children[index + 1];
function noMissingBlankLines(ast, file) {
visit(ast, function (node, index, parent) {
var next = parent && parent.children[index + 1];
if (position.generated(node)) {
return;
}
if (position.generated(node)) {
return;
}
if (
next &&
isApplicable(node) &&
isApplicable(next) &&
position.start(next).line === position.end(node).line + 1
) {
file.warn('Missing blank line before block node', next);
}
});
done();
if (
next &&
isApplicable(node) &&
isApplicable(next) &&
position.start(next).line === position.end(node).line + 1
) {
file.warn('Missing blank line before block node', next);
}
});
}
/*
* Expose.
/**
* Check if `node` is an applicable block-level node.
*
* @param {Node} node - Node to test.
* @return {boolean} - Whether or not `node` is applicable.
*/
module.exports = noMissingBlankLines;
function isApplicable(node) {
return [
'paragraph',
'blockquote',
'heading',
'code',
'yaml',
'html',
'list',
'table',
'thematicBreak'
].indexOf(node.type) !== -1;
}

@@ -10,12 +10,18 @@ /**

* Options: `number`, default: `1`.
* @example
* <!-- Invalid, when set to `1` -->
*
* @example {"name": "valid.md", "setting": 1}
*
* # Foo
*
* # Bar
* ## Bar
*
* <!-- Valid, when set to `1` -->
* @example {"name": "invalid.md", "setting": 1, "label": "input"}
*
* # Foo
*
* ## Bar
* # Bar
*
* @example {"name": "invalid.md", "setting": 1, "label": "output"}
*
* 3:1-3:6: Don’t use multiple top level headings (3:1)
*/

@@ -25,11 +31,9 @@

/* eslint-env commonjs */
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('unist-util-position');
/*
* Dependencies.
*/
/* Expose. */
module.exports = noMultipleToplevelHeadings;
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
/**

@@ -43,27 +47,23 @@ * Warn when multiple top-level headings are used.

*/
function noMultipleToplevelHeadings(ast, file, preferred, done) {
var style = preferred && preferred !== true ? preferred : 1;
var topLevelheading = false;
function noMultipleToplevelHeadings(ast, file, preferred) {
var style = preferred ? preferred : 1;
var topLevelheading = false;
visit(ast, 'heading', function (node) {
var pos;
visit(ast, 'heading', function (node) {
var pos;
if (position.generated(node)) {
return;
}
if (position.generated(node)) {
return;
}
if (node.depth === style) {
if (topLevelheading) {
pos = position.start(node);
if (node.depth === style) {
if (topLevelheading) {
pos = position.start(node);
file.warn('Don’t use multiple top level headings (' + pos.line + ':' + pos.column + ')', node);
}
file.warn('Don’t use multiple top level headings (' + pos.line + ':' + pos.column + ')', node);
}
topLevelheading = node;
}
});
done();
topLevelheading = node;
}
});
}
module.exports = noMultipleToplevelHeadings;

@@ -11,10 +11,5 @@ /**

* flag.
* @example
* <!-- Invalid: -->
* ```bash
* $ echo a
* $ echo a > file
* ```
*
* <!-- Valid: -->
* @example {"name": "valid.md"}
*
* ```sh

@@ -25,3 +20,2 @@ * echo a

*
* <!-- Also valid: -->
* ```zsh

@@ -32,2 +26,13 @@ * $ echo a

* ```
*
* @example {"name": "invalid.md", "label": "input"}
*
* ```bash
* $ echo a
* $ echo a > file
* ```
*
* @example {"name": "invalid.md", "label": "output"}
*
* 1:1-4:4: Do not use dollar signs before shell-commands
*/

@@ -37,29 +42,24 @@

/* eslint-env commonjs */
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('unist-util-position');
/*
* Dependencies.
*/
/* Expose. */
module.exports = noShellDollars;
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
/**
* List of shell script file extensions (also used as code
/* List of shell script file extensions (also used as code
* flags for syntax highlighting on GitHub):
*
* @see https://github.com/github/linguist/blob/5bf8cf5/lib/linguist/languages.yml#L3002
*/
* https://github.com/github/linguist/blob/5bf8cf5/lib/
* linguist/languages.yml#L3002. */
var flags = [
'sh',
'bash',
'bats',
'cgi',
'command',
'fcgi',
'ksh',
'tmux',
'tool',
'zsh'
'sh',
'bash',
'bats',
'cgi',
'command',
'fcgi',
'ksh',
'tmux',
'tool',
'zsh'
];

@@ -72,37 +72,24 @@

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noShellDollars(ast, file, preferred, done) {
visit(ast, 'code', function (node) {
var language = node.lang;
var value = node.value;
var warn;
function noShellDollars(ast, file) {
visit(ast, 'code', function (node) {
var language = node.lang;
var value = node.value;
var warn;
if (!language || position.generated(node)) {
return;
}
if (!language || position.generated(node)) {
return;
}
/*
* Check both known shell-code and unknown code.
*/
/* Check both known shell-code and unknown code. */
if (flags.indexOf(language) !== -1) {
warn = value.length && value.split('\n').every(function (line) {
return Boolean(!line.trim() || line.match(/^\s*\$\s*/));
});
if (flags.indexOf(language) !== -1) {
warn = value.length && value.split('\n').every(function (line) {
return Boolean(!line.trim() || line.match(/^\s*\$\s*/));
});
if (warn) {
file.warn('Do not use dollar signs before shell-commands', node);
}
}
});
done();
if (warn) {
file.warn('Do not use dollar signs before shell-commands', node);
}
}
});
}
/*
* Expose.
*/
module.exports = noShellDollars;

@@ -8,4 +8,11 @@ /**

* Warn when shortcut reference images are used.
* @example
* <!-- Invalid: -->
*
* @example {"name": "valid.md"}
*
* ![foo][]
*
* [foo]: http://foo.bar/baz.png
*
* @example {"name": "invalid.md", "label": "input"}
*
* ![foo]

@@ -15,6 +22,5 @@ *

*
* <!-- Valid: -->
* ![foo][]
* @example {"name": "invalid.md", "label": "output"}
*
* [foo]: http://foo.bar/baz.png
* 1:1-1:7: Use the trailing [] on reference images
*/

@@ -24,11 +30,9 @@

/* eslint-env commonjs */
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('unist-util-position');
/*
* Dependencies.
*/
/* Expose. */
module.exports = noShortcutReferenceImage;
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
/**

@@ -39,23 +43,13 @@ * Warn when shortcut reference images are used.

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noShortcutReferenceImage(ast, file, preferred, done) {
visit(ast, 'imageReference', function (node) {
if (position.generated(node)) {
return;
}
function noShortcutReferenceImage(ast, file) {
visit(ast, 'imageReference', function (node) {
if (position.generated(node)) {
return;
}
if (node.referenceType === 'shortcut') {
file.warn('Use the trailing [] on reference images', node);
}
});
done();
if (node.referenceType === 'shortcut') {
file.warn('Use the trailing [] on reference images', node);
}
});
}
/*
* Expose.
*/
module.exports = noShortcutReferenceImage;

@@ -8,4 +8,11 @@ /**

* Warn when shortcut reference links are used.
* @example
* <!-- Invalid: -->
*
* @example {"name": "valid.md"}
*
* [foo][]
*
* [foo]: http://foo.bar/baz
*
* @example {"name": "invalid.md", "label": "input"}
*
* [foo]

@@ -15,6 +22,5 @@ *

*
* <!-- Valid: -->
* [foo][]
* @example {"name": "invalid.md", "label": "output"}
*
* [foo]: http://foo.bar/baz
* 1:1-1:6: Use the trailing [] on reference links
*/

@@ -24,11 +30,9 @@

/* eslint-env commonjs */
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('unist-util-position');
/*
* Dependencies.
*/
/* Expose. */
module.exports = noShortcutReferenceLink;
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
/**

@@ -39,23 +43,13 @@ * Warn when shortcut reference links are used.

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noShortcutReferenceLink(ast, file, preferred, done) {
visit(ast, 'linkReference', function (node) {
if (position.generated(node)) {
return;
}
function noShortcutReferenceLink(ast, file) {
visit(ast, 'linkReference', function (node) {
if (position.generated(node)) {
return;
}
if (node.referenceType === 'shortcut') {
file.warn('Use the trailing [] on reference links', node);
}
});
done();
if (node.referenceType === 'shortcut') {
file.warn('Use the trailing [] on reference links', node);
}
});
}
/*
* Expose.
*/
module.exports = noShortcutReferenceLink;

@@ -8,12 +8,23 @@ /**

* Warn when tables are indented.
* @example
* <!-- Invalid: -->
* | A | B |
* | ----- | ----- |
* | Alpha | Bravo |
*
* <!-- Valid: -->
* @example {"name": "valid.md"}
*
* Paragraph.
*
* | A | B |
* | ----- | ----- |
* | Alpha | Bravo |
*
* @example {"name": "invalid.md", "label": "input"}
*
* Paragraph.
*
* | A | B |
* | ----- | ----- |
* | Alpha | Bravo |
*
* @example {"name": "invalid.md", "label": "output"}
*
* 3:1-3:21: Do not indent table rows
* 5:1-5:21: Do not indent table rows
*/

@@ -23,11 +34,9 @@

/* eslint-env commonjs */
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('unist-util-position');
/*
* Dependencies.
*/
/* Expose. */
module.exports = noTableIndentation;
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
/**

@@ -38,29 +47,19 @@ * Warn when a table has a too much indentation.

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noTableIndentation(ast, file, preferred, done) {
visit(ast, 'table', function (node) {
var contents = file.toString();
function noTableIndentation(ast, file) {
visit(ast, 'table', function (node) {
var contents = file.toString();
if (position.generated(node)) {
return;
}
if (position.generated(node)) {
return;
}
node.children.forEach(function (row) {
var fence = contents.slice(position.start(row).offset, position.start(row.children[0]).offset);
node.children.forEach(function (row) {
var fence = contents.slice(position.start(row).offset, position.start(row.children[0]).offset);
if (fence.indexOf('|') > 1) {
file.warn('Do not indent table rows', row);
}
});
if (fence.indexOf('|') > 1) {
file.warn('Do not indent table rows', row);
}
});
done();
});
}
/*
* Expose.
*/
module.exports = noTableIndentation;

@@ -8,13 +8,38 @@ /**

* Warn when hard-tabs instead of spaces
* @example
* <!-- Note: the double guillemet (`»`) and middle-dots represent a tab -->
* <!-- Invalid: -->
* Foo»Bar
*
* »···Foo
* @example {"name": "valid.md"}
*
* <!-- Valid: -->
* Foo Bar
*
* Foo
*
* @example {"name": "invalid.md", "label": "input", "config": {"positionless": true}}
*
* <!-- Note: the guillemets represent tabs -->
*
* »Here's one before a code block.
*
* Here's a tab:», and here is another:».
*
* And this is in `inline»code`.
*
* >»This is in a block quote.
*
* *»And...
*
* »1.»in a list.
*
* And this is a tab as the last character.»
*
* @example {"name": "invalid.md", "label": "output"}
*
* 3:1: Use spaces instead of hard-tabs
* 5:14: Use spaces instead of hard-tabs
* 5:37: Use spaces instead of hard-tabs
* 7:23: Use spaces instead of hard-tabs
* 9:2: Use spaces instead of hard-tabs
* 11:2: Use spaces instead of hard-tabs
* 13:1: Use spaces instead of hard-tabs
* 13:4: Use spaces instead of hard-tabs
* 15:41: Use spaces instead of hard-tabs
*/

@@ -24,10 +49,8 @@

/* eslint-env commonjs */
/* Dependencies. */
var vfileLocation = require('vfile-location');
/*
* Dependencies.
*/
/* Expose. */
module.exports = noTabs;
var vfileLocation = require('vfile-location');
/**

@@ -42,20 +65,14 @@ * Warn when hard-tabs instead of spaces are used.

function noTabs(ast, file, preferred, done) {
var content = file.toString();
var location = vfileLocation(file);
var index = -1;
var length = content.length;
var content = file.toString();
var location = vfileLocation(file);
var index = -1;
var length = content.length;
while (++index < length) {
if (content.charAt(index) === '\t') {
file.warn('Use spaces instead of hard-tabs', location.toPosition(index));
}
while (++index < length) {
if (content.charAt(index) === '\t') {
file.warn('Use spaces instead of hard-tabs', location.toPosition(index));
}
}
done();
done();
}
/*
* Expose.
*/
module.exports = noTabs;

@@ -5,7 +5,8 @@ /**

* @license MIT
* @module no-unused-definitions
* @module no-undefined-references
* @fileoverview
* Warn when references to undefined definitions are found.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* [foo][]

@@ -15,4 +16,9 @@ *

*
* <!-- Invalid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* [bar][]
*
* @example {"name": "invalid.md", "label": "output"}
*
* 1:1-1:8: Found reference to undefined definition
*/

@@ -22,11 +28,9 @@

/* eslint-env commonjs */
/* Dependencies. */
var position = require('unist-util-position');
var visit = require('unist-util-visit');
/*
* Dependencies.
*/
/* Expose. */
module.exports = noUnusedDefinitions;
var position = require('mdast-util-position');
var visit = require('unist-util-visit');
/**

@@ -37,50 +41,42 @@ * Warn when references to undefined definitions are found.

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noUnusedDefinitions(ast, file, preferred, done) {
var map = {};
function noUnusedDefinitions(ast, file) {
var map = {};
/**
* Check `node`.
*
* @param {Node} node - Node.
*/
function mark(node) {
if (position.generated(node)) {
return;
}
visit(ast, 'definition', mark);
visit(ast, 'footnoteDefinition', mark);
map[node.identifier.toUpperCase()] = true;
}
visit(ast, 'imageReference', find);
visit(ast, 'linkReference', find);
visit(ast, 'footnoteReference', find);
/**
* Mark `node`.
*
* @param {Node} node - Node.
*/
function find(node) {
if (position.generated(node)) {
return;
}
return;
if (!map[node.identifier.toUpperCase()]) {
file.warn('Found reference to undefined definition', node);
}
/**
* Check `node`.
*
* @param {Node} node - Node.
*/
function mark(node) {
if (position.generated(node)) {
return;
}
visit(ast, 'definition', mark);
visit(ast, 'footnoteDefinition', mark);
map[node.identifier.toUpperCase()] = true;
}
visit(ast, 'imageReference', find);
visit(ast, 'linkReference', find);
visit(ast, 'footnoteReference', find);
/**
* Mark `node`.
*
* @param {Node} node - Node.
*/
function find(node) {
if (position.generated(node)) {
return;
}
done();
if (!map[node.identifier.toUpperCase()]) {
file.warn('Found reference to undefined definition', node);
}
}
}
/*
* Expose.
*/
module.exports = noUnusedDefinitions;

@@ -8,4 +8,5 @@ /**

* Warn when unused definitions are found.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* [foo][]

@@ -15,4 +16,9 @@ *

*
* <!-- Invalid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* [bar]: https://example.com
*
* @example {"name": "invalid.md", "label": "output"}
*
* 1:1-1:27: Found unused definition
*/

@@ -22,11 +28,9 @@

/* eslint-env commonjs */
/* Dependencies. */
var position = require('unist-util-position');
var visit = require('unist-util-visit');
/*
* Dependencies.
*/
/* Expose. */
module.exports = noUnusedDefinitions;
var position = require('mdast-util-position');
var visit = require('unist-util-visit');
/**

@@ -37,60 +41,52 @@ * Warn when unused definitions are found.

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noUnusedDefinitions(ast, file, preferred, done) {
var map = {};
var identifier;
function noUnusedDefinitions(ast, file) {
var map = {};
var identifier;
/**
* Check `node`.
*
* @param {Node} node - Node.
*/
function find(node) {
if (position.generated(node)) {
return;
}
visit(ast, 'definition', find);
visit(ast, 'footnoteDefinition', find);
map[node.identifier.toUpperCase()] = {
'node': node,
'used': false
};
visit(ast, 'imageReference', mark);
visit(ast, 'linkReference', mark);
visit(ast, 'footnoteReference', mark);
for (identifier in map) {
if (!map[identifier].used) {
file.warn('Found unused definition', map[identifier].node);
}
}
/**
* Mark `node`.
*
* @param {Node} node - Node.
*/
function mark(node) {
var info = map[node.identifier.toUpperCase()];
return;
if (position.generated(node) || !info) {
return;
}
info.used = true;
/**
* Check `node`.
*
* @param {Node} node - Node.
*/
function find(node) {
if (position.generated(node)) {
return;
}
visit(ast, 'definition', find);
visit(ast, 'footnoteDefinition', find);
map[node.identifier.toUpperCase()] = {
node: node,
used: false
};
}
visit(ast, 'imageReference', mark);
visit(ast, 'linkReference', mark);
visit(ast, 'footnoteReference', mark);
/**
* Mark `node`.
*
* @param {Node} node - Node.
*/
function mark(node) {
var info = map[node.identifier.toUpperCase()];
for (identifier in map) {
if (!map[identifier].used) {
file.warn('Found unused definition', map[identifier].node);
}
if (position.generated(node) || !info) {
return;
}
done();
info.used = true;
}
}
/*
* Expose.
*/
module.exports = noUnusedDefinitions;

@@ -18,4 +18,7 @@ /**

* style.
* @example
* <!-- Valid when set to `consistent` or `.` -->
*
* @example {"name": "valid.md", "setting": "."}
*
* <!-- This is also valid when `consistent`. -->
*
* 1. Foo

@@ -25,6 +28,24 @@ *

*
* <!-- Valid when set to `consistent` or `)` -->
* @example {"name": "valid.md", "setting": ")", "config": {"commonmark": true}}
*
* <!-- This is also valid when `consistent`.
* But it does require commonmark. -->
*
* 1) Foo
*
* 2) Bar
*
* @example {"name": "invalid.md", "label": "input", "config": {"commonmark": true}}
*
* 1. Foo
*
* 2) Bar
*
* @example {"name": "invalid.md", "label": "output"}
*
* 3:1-3:8: Marker style should be `.`
*
* @example {"name": "invalid.md", "label": "output", "setting": "!", "config": {"positionless": true}}
*
* 1:1: Invalid ordered list-item marker style `!`: use either `'.'` or `')'`
*/

@@ -34,25 +55,17 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Methods.
*/
/* Expose. */
module.exports = orderedListMarkerStyle;
/* Methods. */
var start = position.start;
/*
* Valid styles.
*/
/* Valid styles. */
var STYLES = {
')': true,
'.': true,
'null': true
')': true,
'.': true,
'null': true
};

@@ -69,56 +82,42 @@

* first found style.
* @param {Function} done - Callback.
*/
function orderedListMarkerStyle(ast, file, preferred, done) {
var contents = file.toString();
function orderedListMarkerStyle(ast, file, preferred) {
var contents = file.toString();
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
if (STYLES[preferred] !== true) {
file.fail('Invalid ordered list-item marker style `' + preferred + '`: use either `\'.\'` or `\')\'`');
done();
if (STYLES[preferred] !== true) {
file.fail('Invalid ordered list-item marker style `' + preferred + '`: use either `\'.\'` or `\')\'`');
return;
}
return;
visit(ast, 'list', function (node) {
var items = node.children;
if (!node.ordered) {
return;
}
visit(ast, 'list', function (node) {
var items = node.children;
items.forEach(function (item) {
var head = item.children[0];
var initial = start(item).offset;
var final = start(head).offset;
var marker;
if (!node.ordered) {
return;
}
if (position.generated(item)) {
return;
}
items.forEach(function (item) {
var head = item.children[0];
var initial = start(item).offset;
var final = start(head).offset;
var marker;
marker = contents.slice(initial, final).replace(/\s|\d/g, '');
if (position.generated(item)) {
return;
}
/* Support checkboxes. */
marker = marker.replace(/\[[x ]?\]\s*$/i, '');
marker = contents.slice(initial, final).replace(/\s|\d/g, '');
/*
* Support checkboxes.
*/
marker = marker.replace(/\[[x ]?\]\s*$/i, '');
if (!preferred) {
preferred = marker;
} else if (marker !== preferred) {
file.warn('Marker style should be `' + preferred + '`', item);
}
});
if (!preferred) {
preferred = marker;
} else if (marker !== preferred) {
file.warn('Marker style should be `' + preferred + '`', item);
}
});
done();
});
}
/*
* Expose.
*/
module.exports = orderedListMarkerStyle;

@@ -17,4 +17,5 @@ /**

* should always be `1`.
* @example
* <!-- Valid when set to `one`: -->
*
* @example {"name": "valid.md", "setting": "one"}
*
* 1. Foo

@@ -24,2 +25,4 @@ * 1. Bar

*
* Paragraph.
*
* 1. Alpha

@@ -29,3 +32,4 @@ * 1. Bravo

*
* <!-- Valid when set to `single`: -->
* @example {"name": "valid.md", "setting": "single"}
*
* 1. Foo

@@ -35,2 +39,4 @@ * 1. Bar

*
* Paragraph.
*
* 3. Alpha

@@ -40,3 +46,4 @@ * 3. Bravo

*
* <!-- Valid when set to `ordered`: -->
* @example {"name": "valid.md", "setting": "ordered"}
*
* 1. Foo

@@ -46,5 +53,29 @@ * 2. Bar

*
* Paragraph.
*
* 3. Alpha
* 4. Bravo
* 5. Charlie
*
* @example {"name": "invalid.md", "setting": "one", "label": "input"}
*
* 1. Foo
* 2. Bar
*
* @example {"name": "invalid.md", "setting": "one", "label": "output"}
*
* 2:1-2:8: Marker should be `1`, was `2`
*
* @example {"name": "invalid.md", "setting": "ordered", "label": "input"}
*
* 1. Foo
* 1. Bar
*
* @example {"name": "invalid.md", "setting": "ordered", "label": "output"}
*
* 2:1-2:8: Marker should be `2`, was `1`
*
* @example {"name": "invalid.md", "setting": "invalid", "label": "output", "config": {"positionless": true}}
*
* 1:1: Invalid ordered list-item marker value `invalid`: use either `'ordered'` or `'one'`
*/

@@ -54,25 +85,17 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Methods.
*/
/* Expose. */
module.exports = orderedListMarkerValue;
/* Methods. */
var start = position.start;
/*
* Valid styles.
*/
/* Valid styles. */
var STYLES = {
'ordered': true,
'single': true,
'one': true
ordered: true,
single: true,
one: true
};

@@ -89,80 +112,54 @@

* defaulting to the latter.
* @param {Function} done - Callback.
*/
function orderedListMarkerValue(ast, file, preferred, done) {
var contents = file.toString();
function orderedListMarkerValue(ast, file, preferred) {
var contents = file.toString();
preferred = typeof preferred !== 'string' ? 'ordered' : preferred;
preferred = typeof preferred === 'string' ? preferred : 'ordered';
if (STYLES[preferred] !== true) {
file.fail('Invalid ordered list-item marker value `' + preferred + '`: use either `\'ordered\'` or `\'one\'`');
done();
if (STYLES[preferred] !== true) {
file.fail('Invalid ordered list-item marker value `' + preferred + '`: use either `\'ordered\'` or `\'one\'`');
return;
}
return;
visit(ast, 'list', function (node) {
var items = node.children;
var shouldBe = (preferred === 'one' ? 1 : node.start) || 1;
/* Ignore unordered lists. */
if (!node.ordered) {
return;
}
visit(ast, 'list', function (node) {
var items = node.children;
var shouldBe = (preferred === 'one' ? 1 : node.start) || 1;
items.forEach(function (item, index) {
var head = item.children[0];
var initial = start(item).offset;
var final = start(head).offset;
var marker;
/*
* Ignore unordered lists.
*/
/* Ignore first list item. */
if (index === 0) {
return;
}
if (!node.ordered) {
return;
}
/* Increase the expected line number when in
* `ordered` mode. */
if (preferred === 'ordered') {
shouldBe++;
}
items.forEach(function (item, index) {
var head = item.children[0];
var initial = start(item).offset;
var final = start(head).offset;
var marker;
/* Ignore generated nodes. */
if (position.generated(item)) {
return;
}
/*
* Ignore first list item.
*/
marker = contents.slice(initial, final).replace(/[\s\.\)]/g, '');
if (index === 0) {
return;
}
/* Support checkboxes. */
marker = Number(marker.replace(/\[[x ]?\]\s*$/i, ''));
/*
* Increase the expected line number when in
* `ordered` mode.
*/
if (preferred === 'ordered') {
shouldBe++;
}
/*
* Ignore generated nodes.
*/
if (position.generated(item)) {
return;
}
marker = contents.slice(initial, final).replace(/[\s\.\)]/g, '');
/*
* Support checkboxes.
*/
marker = Number(marker.replace(/\[[x ]?\]\s*$/i, ''));
if (marker !== shouldBe) {
file.warn('Marker should be `' + shouldBe + '`, was `' + marker + '`', item);
}
});
if (marker !== shouldBe) {
file.warn('Marker should be `' + shouldBe + '`, was `' + marker + '`', item);
}
});
done();
});
}
/*
* Expose.
*/
module.exports = orderedListMarkerValue;

@@ -13,4 +13,7 @@ /**

* default: `'consistent'`.
* @example
* <!-- Valid when set to `consistent` or `* * *`: -->
*
* @example {"name": "valid.md", "setting": "* * *"}
*
* <!-- This is also valid when `consistent`. -->
*
* * * *

@@ -20,6 +23,25 @@ *

*
* <!-- Valid when set to `consistent` or `_______`: -->
* @example {"name": "valid.md", "setting": "_______"}
*
* <!-- This is also valid when `consistent`. -->
*
* _______
*
* _______
*
* @example {"name": "invalid.md", "label": "input"}
*
* <!-- Always invalid. -->
*
* ***
*
* * * *
*
* @example {"name": "invalid.md", "label": "output"}
*
* 5:1-5:6: Rules should use `***`
*
* @example {"name": "invalid.md", "label": "output", "setting": "!!!", "config": {"positionless": true}}
*
* 1:1: Invalid preferred rule-style: provide a valid markdown rule, or `'consistent'`
*/

@@ -29,15 +51,10 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Methods.
*/
/* Expose. */
module.exports = ruleStyle;
/* Methods. */
var start = position.start;

@@ -47,13 +64,2 @@ var end = position.end;

/**
* Warn when a given style is invalid.
*
* @param {*} style - `*`, `_`, ` ` (space), or `-`.
* @return {boolean} - Whether or not `style` is a
* valid rule style.
*/
function validateRuleStyle(style) {
return style === null || !/[^-_* ]/.test(style);
}
/**
* Warn when the horizontal rules violate a given or

@@ -66,43 +72,43 @@ * detected style.

* horizontal rule, defaulting to the first found style.
* @param {Function} done - Callback.
*/
function ruleStyle(ast, file, preferred, done) {
var contents = file.toString();
function ruleStyle(ast, file, preferred) {
var contents = file.toString();
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
if (validateRuleStyle(preferred) !== true) {
file.fail('Invalid preferred rule-style: provide a valid markdown rule, or `\'consistent\'`');
done();
if (validateRuleStyle(preferred) !== true) {
file.fail('Invalid preferred rule-style: provide a valid markdown rule, or `\'consistent\'`');
return;
}
return;
visit(ast, 'thematicBreak', function (node) {
var initial = start(node).offset;
var final = end(node).offset;
var hr;
if (position.generated(node)) {
return;
}
visit(ast, 'thematicBreak', function (node) {
var initial = start(node).offset;
var final = end(node).offset;
var hr;
hr = contents.slice(initial, final);
if (position.generated(node)) {
return;
}
hr = contents.slice(initial, final);
if (preferred) {
if (hr !== preferred) {
file.warn('Rules should use `' + preferred + '`', node);
}
} else {
preferred = hr;
}
});
done();
if (preferred) {
if (hr !== preferred) {
file.warn('Rules should use `' + preferred + '`', node);
}
} else {
preferred = hr;
}
});
}
/*
* Expose.
/**
* Warn when a given style is invalid.
*
* @param {*} style - `*`, `_`, ` ` (space), or `-`.
* @return {boolean} - Whether or not `style` is a
* valid rule style.
*/
module.exports = ruleStyle;
function validateRuleStyle(style) {
return style === null || !/[^-_* ]/.test(style);
}

@@ -5,3 +5,3 @@ /**

* @license MIT
* @module blockquote-indentation
* @module strong-marker
* @fileoverview

@@ -16,10 +16,30 @@ * Warn for violating strong markers.

* style.
* @example
* <!-- Valid when set to `consistent` or `*` -->
* **foo**
* **bar**
*
* <!-- Valid when set to `consistent` or `_` -->
* __foo__
* __bar__
* @example {"name": "valid.md"}
*
* **foo** and **bar**.
*
* @example {"name": "also-valid.md"}
*
* __foo__ and __bar__.
*
* @example {"name": "valid.md", "setting": "*"}
*
* **foo**.
*
* @example {"name": "valid.md", "setting": "_"}
*
* __foo__.
*
* @example {"name": "invalid.md", "label": "input"}
*
* **foo** and __bar__.
*
* @example {"name": "invalid.md", "label": "output"}
*
* 1:13-1:20: Strong should use `*` as a marker
*
* @example {"name": "invalid.md", "label": "output", "setting": "!", "config": {"positionless": true}}
*
* 1:1: Invalid strong marker `!`: use either `'consistent'`, `'*'`, or `'_'`
*/

@@ -29,19 +49,14 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Map of valid markers.
*/
/* Expose. */
module.exports = strongMarker;
/* Map of valid markers. */
var MARKERS = {
'*': true,
'_': true,
'null': true
'*': true,
'_': true,
'null': true
};

@@ -56,34 +71,26 @@

* marker, either `"*"` or `"_"`, or `"consistent"`.
* @param {Function} done - Callback.
*/
function strongMarker(ast, file, preferred, done) {
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
function strongMarker(ast, file, preferred) {
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
if (MARKERS[preferred] !== true) {
file.fail('Invalid strong marker `' + preferred + '`: use either `\'consistent\'`, `\'*\'`, or `\'_\'`');
} else {
visit(ast, 'strong', function (node) {
var marker = file.toString().charAt(position.start(node).offset);
if (MARKERS[preferred] !== true) {
file.fail('Invalid strong marker `' + preferred + '`: use either `\'consistent\'`, `\'*\'`, or `\'_\'`');
return;
}
if (position.generated(node)) {
return;
}
visit(ast, 'strong', function (node) {
var marker = file.toString().charAt(position.start(node).offset);
if (preferred) {
if (marker !== preferred) {
file.warn('Strong should use `' + preferred + '` as a marker', node);
}
} else {
preferred = marker;
}
});
if (position.generated(node)) {
return;
}
done();
if (preferred) {
if (marker !== preferred) {
file.warn('Strong should use `' + preferred + '` as a marker', node);
}
} else {
preferred = marker;
}
});
}
/*
* Expose.
*/
module.exports = strongMarker;

@@ -15,4 +15,7 @@ /**

* style.
* @example
* <!-- Valid when set to `consistent` or `padded` -->
*
* @example {"name": "valid.md", "setting": "padded"}
*
* <!--Also valid when `consistent`-->
*
* | A | B |

@@ -22,3 +25,6 @@ * | ----- | ----- |

*
* <!-- Valid when set to `consistent` or `compact` -->
* @example {"name": "valid.md", "setting": "compact"}
*
* <!--Also valid when `consistent`-->
*
* |A |B |

@@ -28,6 +34,19 @@ * |-----|-----|

*
* <!-- Invalid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* <!--Always invalid-->
*
* | A | B |
* | -----| -----|
* | Alpha| Bravo|
*
* @example {"name": "invalid.md", "label": "output"}
*
* 3:5: Cell should be padded, isn’t
* 3:9: Cell should be padded, isn’t
* 3:16: Cell should be padded, isn’t
*
* @example {"name": "invalid.md", "label": "output", "setting": "invalid", "config": {"positionless": true}}
*
* 1:1: Invalid table-cell-padding style `invalid`
*/

@@ -37,26 +56,20 @@

/* eslint-env commonjs */
/* eslint-disable max-params */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Methods.
*/
/* Expose. */
module.exports = tableCellPadding;
/* Methods. */
var start = position.start;
var end = position.end;
/*
* Valid styles.
*/
/* Valid styles. */
var STYLES = {
'null': true,
'padded': true,
'compact': true
null: true,
padded: true,
compact: true
};

@@ -73,119 +86,111 @@

* first found style.
* @param {Function} done - Callback.
*/
function tableCellPadding(ast, file, preferred, done) {
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
function tableCellPadding(ast, file, preferred) {
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
if (STYLES[preferred] !== true) {
file.fail('Invalid table-cell-padding style `' + preferred + '`');
}
if (STYLES[preferred] !== true) {
file.fail('Invalid table-cell-padding style `' + preferred + '`');
return;
}
visit(ast, 'table', function (node) {
var children = node.children;
var contents = file.toString();
var starts = [];
var ends = [];
var locations;
var positions;
var style;
var type;
var warning;
visit(ast, 'table', function (node) {
var children = node.children;
var contents = file.toString();
var starts = [];
var ends = [];
var locations;
var positions;
var style;
var type;
var warning;
if (position.generated(node)) {
return;
}
if (position.generated(node)) {
return;
}
/**
* Check a fence. Checks both its initial spacing
* (between a cell and the fence), and its final
* spacing (between the fence and the next cell).
*
* @param {number} initial - Starting index.
* @param {number} final - Closing index.
* @param {Node} cell - Table cell.
* @param {Node?} next - Following cell.
* @param {number} index - Position of `cell` in
* its parent.
*/
function check(initial, final, cell, next, index) {
var fence = contents.slice(initial, final);
var pos = fence.indexOf('|');
/**
* Check a fence. Checks both its initial spacing
* (between a cell and the fence), and its final
* spacing (between the fence and the next cell).
*
* @param {number} initial - Starting index.
* @param {number} final - Closing index.
* @param {Node} cell - Table cell.
* @param {Node?} next - Following cell.
* @param {number} index - Position of `cell` in
* its parent.
*/
function check(initial, final, cell, next, index) {
var fence = contents.slice(initial, final);
var pos = fence.indexOf('|');
if (
cell &&
pos !== -1 &&
(
ends[index] === undefined ||
pos < ends[index]
)
) {
ends[index] = pos;
}
if (
cell &&
pos !== -1 &&
(ends[index] === undefined || pos < ends[index])
) {
ends[index] = pos;
}
if (next && pos !== -1) {
pos = fence.length - pos - 1;
if (next && pos !== -1) {
pos = fence.length - pos - 1;
if (starts[index + 1] === undefined || pos < starts[index + 1]) {
starts[index + 1] = pos;
}
}
if (starts[index + 1] === undefined || pos < starts[index + 1]) {
starts[index + 1] = pos;
}
}
}
children.forEach(function (row) {
var cells = row.children;
children.forEach(function (row) {
var cells = row.children;
check(start(row).offset, start(cells[0]).offset, null, cells[0], -1);
check(start(row).offset, start(cells[0]).offset, null, cells[0], -1);
cells.forEach(function (cell, index) {
var next = cells[index + 1] || null;
var final = start(next).offset || end(row).offset;
cells.forEach(function (cell, index) {
var next = cells[index + 1] || null;
var final = start(next).offset || end(row).offset;
check(end(cell).offset, final, cell, next, index);
});
});
check(end(cell).offset, final, cell, next, index);
});
});
positions = starts.concat(ends);
positions = starts.concat(ends);
style = preferred === 'padded' ? 1 : preferred === 'compact' ? 0 : null;
if (preferred === 'padded') {
style = 1;
} else if (preferred === 'compact') {
style = 0;
} else {
style = null;
}
if (preferred === 'padded') {
style = 1;
} else if (preferred === 'compact') {
style = 0;
} else {
positions.some(function (pos) {
/*
* `some` skips non-existant indices, so
* there's no need to check for `!isNaN`.
*/
if (preferred === 'padded') {
style = 1;
} else if (preferred === 'compact') {
style = 0;
} else {
positions.some(function (pos) {
/* `some` skips non-existant indices, so
* there's no need to check for `!isNaN`. */
style = Math.min(pos, 1);
style = Math.min(pos, 1);
return true;
});
}
return true;
});
}
locations = children[0].children.map(function (cell) {
return start(cell);
}).concat(children[0].children.map(function (cell) {
return end(cell);
}));
locations = children[0].children.map(function (cell) {
return start(cell);
}).concat(children[0].children.map(function (cell) {
return end(cell);
}));
type = style === 1 ? 'padded' : 'compact';
warning = 'Cell should be ' + type + ', isn’t';
type = style === 1 ? 'padded' : 'compact';
warning = 'Cell should be ' + type + ', isn’t';
positions.forEach(function (diff, index) {
if (diff !== style && diff !== undefined && diff !== null) {
file.warn(warning, locations[index]);
}
});
positions.forEach(function (diff, index) {
if (diff !== style && diff !== undefined && diff !== null) {
file.warn(warning, locations[index]);
}
});
done();
});
}
/*
* Expose.
*/
module.exports = tableCellPadding;

@@ -8,4 +8,5 @@ /**

* Warn when table pipes are not aligned.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* | A | B |

@@ -15,6 +16,12 @@ * | ----- | ----- |

*
* <!-- Invalid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* | A | B |
* | -- | -- |
* | Alpha | Bravo |
*
* @example {"name": "invalid.md", "label": "output"}
*
* 3:9-3:10: Misaligned table fence
* 3:17-3:18: Misaligned table fence
*/

@@ -24,15 +31,10 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Methods.
*/
/* Expose. */
module.exports = tablePipeAlignment;
/* Methods. */
var start = position.start;

@@ -46,67 +48,57 @@ var end = position.end;

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function tablePipeAlignment(ast, file, preferred, done) {
visit(ast, 'table', function (node) {
var contents = file.toString();
var indices = [];
var offset;
var line;
function tablePipeAlignment(ast, file) {
visit(ast, 'table', function (node) {
var contents = file.toString();
var indices = [];
var offset;
var line;
if (position.generated(node)) {
return;
}
if (position.generated(node)) {
return;
}
/**
* Check all pipes after each column are at
* aligned.
*
* @param {number} initial - Starting index.
* @param {number} final - Closing index.
* @param {number} index - Position of cell in
* its parent.
*/
function check(initial, final, index) {
var pos = initial + contents.slice(initial, final).indexOf('|') - offset + 1;
/**
* Check all pipes after each column are at
* aligned.
*
* @param {number} initial - Starting index.
* @param {number} final - Closing index.
* @param {number} index - Position of cell in
* its parent.
*/
function check(initial, final, index) {
var pos = initial + contents.slice(initial, final).indexOf('|') - offset + 1;
if (indices[index] === undefined || indices[index] === null) {
indices[index] = pos;
} else if (pos !== indices[index]) {
file.warn('Misaligned table fence', {
'start': {
'line': line,
'column': pos
},
'end': {
'line': line,
'column': pos + 1
}
});
}
}
if (indices[index] === undefined || indices[index] === null) {
indices[index] = pos;
} else if (pos !== indices[index]) {
file.warn('Misaligned table fence', {
start: {
line: line,
column: pos
},
end: {
line: line,
column: pos + 1
}
});
}
}
node.children.forEach(function (row) {
var cells = row.children;
node.children.forEach(function (row) {
var cells = row.children;
line = start(row).line;
offset = start(row).offset;
line = start(row).line;
offset = start(row).offset;
check(start(row).offset, start(cells[0]).offset, 0);
check(start(row).offset, start(cells[0]).offset, 0);
row.children.forEach(function (cell, index) {
var next = start(cells[index + 1]).offset || end(row).offset;
row.children.forEach(function (cell, index) {
var next = start(cells[index + 1]).offset || end(row).offset;
check(end(cell).offset, next, index + 1);
});
});
check(end(cell).offset, next, index + 1);
});
});
done();
});
}
/*
* Expose.
*/
module.exports = tablePipeAlignment;

@@ -8,4 +8,5 @@ /**

* Warn when table rows are not fenced with pipes.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* | A | B |

@@ -15,6 +16,14 @@ * | ----- | ----- |

*
* <!-- Invalid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* A | B
* ----- | -----
* Alpha | Bravo
*
* @example {"name": "invalid.md", "label": "output"}
*
* 1:1: Missing initial pipe in table fence
* 1:10: Missing final pipe in table fence
* 3:1: Missing initial pipe in table fence
* 3:14: Missing final pipe in table fence
*/

@@ -24,15 +33,10 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Methods.
*/
/* Expose. */
module.exports = tablePipes;
/* Methods. */
var start = position.start;

@@ -46,37 +50,27 @@ var end = position.end;

* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function tablePipes(ast, file, preferred, done) {
visit(ast, 'table', function (node) {
var contents = file.toString();
function tablePipes(ast, file) {
visit(ast, 'table', function (node) {
var contents = file.toString();
node.children.forEach(function (row) {
var cells = row.children;
var head = cells[0];
var tail = cells[cells.length - 1];
var initial = contents.slice(start(row).offset, start(head).offset);
var final = contents.slice(end(tail).offset, end(row).offset);
node.children.forEach(function (row) {
var cells = row.children;
var head = cells[0];
var tail = cells[cells.length - 1];
var initial = contents.slice(start(row).offset, start(head).offset);
var final = contents.slice(end(tail).offset, end(row).offset);
if (position.generated(row)) {
return;
}
if (position.generated(row)) {
return;
}
if (initial.indexOf('|') === -1) {
file.warn('Missing initial pipe in table fence', start(row));
}
if (initial.indexOf('|') === -1) {
file.warn('Missing initial pipe in table fence', start(row));
}
if (final.indexOf('|') === -1) {
file.warn('Missing final pipe in table fence', end(row));
}
});
if (final.indexOf('|') === -1) {
file.warn('Missing final pipe in table fence', end(row));
}
});
done();
});
}
/*
* Expose.
*/
module.exports = tablePipes;

@@ -16,18 +16,29 @@ /**

* style.
* @example
* <!-- Valid when set to `consistent` or `-` -->
* - Foo
* - Bar
*
* <!-- Valid when set to `consistent` or `*` -->
* * Foo
* * Bar
* @example {"name": "valid.md", "setting": "*"}
*
* <!-- Valid when set to `consistent` or `+` -->
* + Foo
* + Bar
* * Foo
*
* <!-- Never valid: -->
* + Foo
* - Bar
* @example {"name": "valid.md", "setting": "-"}
*
* - Foo
*
* @example {"name": "valid.md", "setting": "+"}
*
* + Foo
*
* @example {"name": "invalid.md", "label": "input"}
*
* * Foo
* - Bar
* + Baz
*
* @example {"name": "invalid.md", "label": "output"}
*
* 2:1-2:6: Marker style should be `*`
* 3:1-3:6: Marker style should be `*`
*
* @example {"name": "invalid.md", "label": "output", "setting": "!", "config": {"positionless": true}}
*
* 1:1: Invalid unordered list-item marker style `!`: use either `'-'`, `'*'`, or `'+'`
*/

@@ -37,26 +48,18 @@

/* eslint-env commonjs */
/*
* Dependencies.
*/
/* Dependencies. */
var visit = require('unist-util-visit');
var position = require('mdast-util-position');
var position = require('unist-util-position');
/*
* Methods.
*/
/* Expose. */
module.exports = unorderedListMarkerStyle;
/* Methods. */
var start = position.start;
/*
* Valid styles.
*/
/* Valid styles. */
var STYLES = {
'-': true,
'*': true,
'+': true,
'null': true
'-': true,
'*': true,
'+': true,
'null': true
};

@@ -73,56 +76,42 @@

* defaulting to the first found style.
* @param {Function} done - Callback.
*/
function unorderedListMarkerStyle(ast, file, preferred, done) {
var contents = file.toString();
function unorderedListMarkerStyle(ast, file, preferred) {
var contents = file.toString();
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
if (STYLES[preferred] !== true) {
file.fail('Invalid unordered list-item marker style `' + preferred + '`: use either `\'-\'`, `\'*\'`, or `\'+\'`');
done();
if (STYLES[preferred] !== true) {
file.fail('Invalid unordered list-item marker style `' + preferred + '`: use either `\'-\'`, `\'*\'`, or `\'+\'`');
return;
}
return;
visit(ast, 'list', function (node) {
var items = node.children;
if (node.ordered) {
return;
}
visit(ast, 'list', function (node) {
var items = node.children;
items.forEach(function (item) {
var head = item.children[0];
var initial = start(item).offset;
var final = start(head).offset;
var marker;
if (node.ordered) {
return;
}
if (position.generated(item)) {
return;
}
items.forEach(function (item) {
var head = item.children[0];
var initial = start(item).offset;
var final = start(head).offset;
var marker;
marker = contents.slice(initial, final).replace(/\s/g, '');
if (position.generated(item)) {
return;
}
/* Support checkboxes. */
marker = marker.replace(/\[[x ]?\]\s*$/i, '');
marker = contents.slice(initial, final).replace(/\s/g, '');
/*
* Support checkboxes.
*/
marker = marker.replace(/\[[x ]?\]\s*$/i, '');
if (!preferred) {
preferred = marker;
} else if (marker !== preferred) {
file.warn('Marker style should be `' + preferred + '`', item);
}
});
if (!preferred) {
preferred = marker;
} else if (marker !== preferred) {
file.warn('Marker style should be `' + preferred + '`', item);
}
});
done();
});
}
/*
* Expose.
*/
module.exports = unorderedListMarkerStyle;
{
"name": "remark-lint",
"version": "4.0.2",
"version": "4.1.0",
"description": "Lint markdown with remark",

@@ -12,22 +12,30 @@ "license": "MIT",

],
"repository": {
"type": "git",
"url": "https://github.com/wooorm/remark-lint.git"
},
"repository": "https://github.com/wooorm/remark-lint",
"bugs": "https://github.com/wooorm/remark-lint/issues",
"author": "Titus Wormer <tituswormer@gmail.com> (http://wooorm.com)",
"contributors": [
"Titus Wormer <tituswormer@gmail.com> (http://wooorm.com)"
"Titus Wormer <tituswormer@gmail.com> (http://wooorm.com)",
"Stephan Schneider <stephanschndr@gmail.com>",
"Ben Balter <ben.balter@github.com>",
"Danny Arnold <despair.blue@gmail.com>",
"Tony Brix <tony@brix.ninja>",
"Michael Mior <michael.mior@gmail.com>",
"Patrick Gilday <pcgilday@gmail.com>",
"Yoshua Wuyts <yoshuawuyts@gmail.com>",
"YJ Yang <chcokr@gmail.com>",
"Burak Yiğit Kaya <ben@byk.im>"
],
"dependencies": {
"decamelize": "^1.0.0",
"load-plugin": "^1.1.1",
"mdast-util-heading-style": "^1.0.0",
"mdast-util-position": "^1.0.0",
"mdast-util-to-string": "^1.0.0",
"npm-prefix": "^1.1.1",
"plur": "^2.0.0",
"remark-message-control": "^2.0.0",
"trough": "^1.0.0",
"unist-util-position": "^2.0.1",
"unist-util-visit": "^1.0.0",
"vfile-location": "^2.0.0",
"vfile-sort": "^1.0.0"
"vfile-sort": "^1.0.0",
"wrapped": "^1.0.1"
},

@@ -40,8 +48,6 @@ "files": [

"browserify": "^13.0.0",
"chalk": "^1.1.3",
"dox": "^0.8.0",
"eslint": "^2.0.0",
"esmangle": "^1.0.0",
"istanbul": "^0.4.0",
"jscs": "^3.0.0",
"jscs-jsdoc": "^2.0.0",
"nyc": "^7.1.0",
"remark": "^5.0.0",

@@ -51,20 +57,62 @@ "remark-cli": "^1.0.0",

"remark-github": "^5.0.0",
"remark-lint-no-url-trailing-slash": "^2.0.0",
"remark-toc": "^3.0.0",
"remark-validate-links": "^4.0.0",
"mocha": "^2.0.0",
"vfile": "^1.0.0"
"strip-indent": "^1.0.0",
"tape": "^4.6.0",
"to-vfile": "^1.0.0",
"trim": "0.0.1",
"unist-builder": "^1.0.2",
"unist-util-remove-position": "^1.1.0",
"xo": "^0.16.0"
},
"scripts": {
"build-rules": "node script/build-rule-documentation.js",
"build-index": "node script/build-index.js",
"build-rules": "node script/build-docs.js",
"build-md": "remark . --quiet --frail",
"build-bundle": "browserify index.js --bare -s remarkLint > remark-lint.js",
"build-mangle": "esmangle remark-lint.js > remark-lint.min.js",
"build": "npm run build-rules && npm run build-md && npm run build-bundle && npm run build-mangle",
"lint-api": "eslint .",
"lint-style": "jscs . --reporter inline",
"lint": "npm run lint-api && npm run lint-style",
"test-api": "mocha --check-leaks test/index.js",
"test-coverage": "istanbul cover _mocha -- test/index.js",
"build": "npm run build-md && npm run build-index && npm run build-rules && npm run build-bundle && npm run build-mangle",
"lint": "xo",
"test-api": "tape test/index.js",
"test-coverage": "nyc --reporter lcov tape test/index.js",
"test": "npm run build && npm run lint && npm run test-coverage"
},
"nyc": {
"check-coverage": true,
"lines": 100,
"functions": 100,
"branches": 100
},
"xo": {
"space": true,
"rules": {
"guard-for-in": "off",
"no-eq-null": "off",
"eqeqeq": "off"
},
"ignores": [
"remark-lint.js",
"remark-lint.min.js"
]
},
"remarkConfig": {
"output": true,
"plugins": {
"comment-config": null,
"github": null,
"toc": {
"tight": true,
"maxDepth": 2
},
"./": {
"no-missing-blank-lines": false,
"list-item-spacing": false
},
"validate-links": null
},
"settings": {
"bullet": "*"
}
}
}

@@ -7,4 +7,2 @@ # ![remark-lint][logo]

<!--lint disable list-item-spacing-->
**remark-lint** is a markdown code style linter. Another linter? Yes.

@@ -24,3 +22,2 @@ Ensuring the markdown you (and contributors) write is of great quality will

* [Programmatic](#programmatic)
* [remark.use(lint\[, options\])](#remarkuselint-options)
* [Rules](#rules)

@@ -98,3 +95,2 @@ * [Configuring remark-lint](#configuring-remark-lint)

```txt
<stdin>
1:1 warning Missing newline character at end of file final-newline

@@ -115,10 +111,14 @@ 1:1-1:16 warning First heading level should be `1` first-heading-level

```js
{
file: '~/example.md',
{ [1:1-1:16: First heading level should be `1`]
message: 'First heading level should be `1`',
name: '1:1-1:16',
file: '',
reason: 'First heading level should be `1`',
line: 1,
column: 1,
location: Position { start: [Object], end: [Object] },
location: {
start: { line: 1, column: 1, offset: 0 },
end: { line: 1, column: 16, offset: 15 } },
fatal: false,
ruleId: 'first-heading-level',
fatal: false,
source: 'remark-lint' }

@@ -220,2 +220,8 @@ ```

If you want to run all of **remark** from **Atom**, use
[**linter-remark**][linter-remark].
To run **remark**, optionally with **remark-lint** from **Gulp**, use
[**gulp-remark**][gulp-remark].
I’m very interested in more integrations. Let me know if I can help.

@@ -225,2 +231,6 @@

External rules can be loaded through the [`external` setting][external].
Learn how to create and use external rules in [`doc/external.md`][doc-external].
<!--

@@ -258,9 +268,9 @@ This list is ordered based on the name without prefix, so

[build-badge]: https://img.shields.io/travis/wooorm/remark-inline-links.svg
[build-badge]: https://img.shields.io/travis/wooorm/remark-lint.svg
[build-status]: https://travis-ci.org/wooorm/remark-inline-links
[build-status]: https://travis-ci.org/wooorm/remark-lint
[coverage-badge]: https://img.shields.io/codecov/c/github/wooorm/remark-inline-links.svg
[coverage-badge]: https://img.shields.io/codecov/c/github/wooorm/remark-lint.svg
[coverage-status]: https://codecov.io/github/wooorm/remark-inline-links
[coverage-status]: https://codecov.io/github/wooorm/remark-lint

@@ -271,3 +281,3 @@ [chat-badge]: https://img.shields.io/gitter/room/wooorm/remark.svg

[releases]: https://github.com/wooorm/remark-inline-links/releases
[releases]: https://github.com/wooorm/remark-lint/releases

@@ -301,1 +311,9 @@ [license]: LICENSE

[vfile-message]: https://github.com/wooorm/vfile#vfilemessage
[linter-remark]: https://github.com/wooorm/linter-remark
[gulp-remark]: https://github.com/denysdovhan/gulp-remark
[external]: doc/rules.md#external
[doc-external]: doc/external.md
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc