Add better external rule support, and refactor module

* Remove requirement for synchronous rules to accept, and
  invoke, a `done` handler;
* Add support for returning promises, generators, from
  rules.

* Add `doc/external.md`, with a description on how to use
  and create external rule packages;
* Add structural fixtures per-rule inline, which are now
  shown in (improved) `doc/rules.md`, and used in testing
  each rule;
* Add contributors to `package.json`;
* Remove history before stable from changelog;
* Refactor tests to use `tape` instead of `mocha`;
* Add automated generation of `rules.js` index;
* Fix incorrect badges.
This commit is contained in:
Titus Wormer 2016-08-02 22:47:01 +02:00
parent 60907ad17e
commit 8dedd190c1
236 changed files with 4320 additions and 5095 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
.DS_Store
*.log
coverage/
.nyc_output/
node_modules/
remark-lint.js
remark-lint.min.js

View File

@ -1 +0,0 @@
test

View File

@ -5,7 +5,6 @@ node_js:
- '4.0'
- '5.0'
- '6.0'
sudo: false
after_success:
- bash <(curl -s https://codecov.io/bash)
deploy:

207
doc/external.md Normal file
View File

@ -0,0 +1,207 @@
# External rules
External rules make it easy to develop and publish small linting rules
for markdown.
## Table of Contents
* [Using external rules](#using-external-rules)
* [Creating external rules](#creating-external-rules)
## Using external rules
External rules can be used by passing their file-path or their name,
in which case `remark-lint-` can be omitted, in an `external` array
to **remark-lint**. This only works in Node.js.
Alternatively, load modules yourself and pass them in the `external` array too.
### CLI
Say we are in the following directory: `~/projects/things`.
Create a `.remarkrc` file and add the following JSON:
```json
{
"plugins": {
"lint": {
"external": [
"no-empty-sections"
]
}
}
}
```
Add a markdown file, such as `readme.md` file, which looks as follows:
```md
# A
## B (this section is empty!)
## C
```
Then, install the required dependencies:
```sh
npm install -g remark-cli remark-lint remark-lint-no-empty-sections
```
Now, run the following in your shell, from the same directory:
```sh
# This will process all markdown files in the current
# directory, and pass the through the specified plugins.
remark .
```
That should show a report like:
```sh
readme.md
5:1-5:5 warning Remove empty section: "B (this section is empty!)" empty-sections
⚠ 1 warning
```
### Programmatic
Say we have a file, `example.md`, which looks as follows:
```md
[link](http://example.com/);
```
And `example.js` next to it:
```js
var fs = require('fs');
var remark = require('remark');
var lint = require('remark-lint');
var report = require('vfile-reporter');
var doc = fs.readFileSync('example.md', 'utf8');
remark()
.use(lint, {external: ['no-url-trailing-slash']})
.process(doc, function (err, file) {
console.log(report(err || file));
});
```
Then, install the required dependencies:
```sh
npm install remark remark-lint remark-lint-no-url-trailing-slash
```
Finally, run `example.js` with Node:
```sh
node example.js
```
Yields:
```txt
1:1-1:28 warning Remove trailing slash (http://example.com) trailing-slash
⚠ 1 warning
```
## Creating external rules
External rule packages expose an object of dash-cased rule ids to
functions.
`index.js`:
```js
module.exports = {
'code-js-flag': require('./rules/code-js-flag.js')
};
```
### Synchronous
Each rule is a function which gets passed a [`root`][root] node,
a [virtual file][vfile], and a setting.
The setting is never `true` or `false`, those are used later to filter
messages. Rules always run, even when theyre turned off, as they can
be turned on from within markdown code through [comments][]
An example, `./rules/code-js-flag.js`, is as follows:
```js
var visit = require('unist-util-visit');
module.exports = rule;
var valid = ['js', 'javascript', 'es', 'es6', 'javascript'];
var def = valid[0];
function rule(ast, file, setting) {
pref = setting == null ? def : setting;
if (valid.indexOf(pref) === -1) {
/* `file.fail` is for invalid, unrecoverable things, **not** for
* linting messages. */
file.fail(pref + ' is not a valid JavaScript extension');
return
}
visit(ast, 'code', function (node) {
/* Emit a linting message, only for JS code though. */
if (valid.indexOf(node.lang) !== -1 && node.lang !== pref) {
file.warn(
'JavaScript code blocks should use `' + pref + '` as a ' +
'language flag, not `' + node.lang + '`',
node
);
}
});
}
```
### Promises
A rule can return a promise, this will automatically switch everything
to asynchronous and wait for the rule to resolve or reject before
continuing on. Note: Do not resolve a value. Rejecting an error is OK.
```js
function rule(ast, file, setting) {
return new Promise(function (resolve, reject) {
// ...do async things.
setImmediate(function () {
resolve();
});
});
}
```
### Callbacks
If a rule has a fourth parameters, `next`, it must be invoked, and it
may be invoked asynchronously. An error may be given to `next` to stop
all processing.
```js
function rule(ast, file, setting, next) {
/* ...do async things. */
setImmediate(function () {
next();
});
}
```
<!--Definitions:-->
[root]: https://github.com/wooorm/mdast#root
[vfile]: https://github.com/wooorm/vfile
[comments]: https://github.com/wooorm/remark-lint#configuring-remark-lint

File diff suppressed because it is too large Load Diff

View File

@ -124,68 +124,3 @@
1.0.0 / 2015-08-17
==================
* 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
==================

View File

@ -165,9 +165,7 @@ function ruleFactory(id, rule, options) {
var scope = file.namespace('remark-lint');
/* Track new messages per file. */
if (scope.index === undefined || scope.index === null) {
scope.index = file.messages.length;
}
scope.index = file.messages.length;
fn(ast, file, options, function (err) {
var messages = file.messages;

61
lib/rules.js Normal file
View File

@ -0,0 +1,61 @@
/* This file is generated. */
'use strict';
module.exports = {
'blockquote-indentation': require('./rules/blockquote-indentation.js'),
'checkbox-character-style': require('./rules/checkbox-character-style.js'),
'checkbox-content-indent': require('./rules/checkbox-content-indent.js'),
'code-block-style': require('./rules/code-block-style.js'),
'definition-case': require('./rules/definition-case.js'),
'definition-spacing': require('./rules/definition-spacing.js'),
'emphasis-marker': require('./rules/emphasis-marker.js'),
'fenced-code-flag': require('./rules/fenced-code-flag.js'),
'fenced-code-marker': require('./rules/fenced-code-marker.js'),
'file-extension': require('./rules/file-extension.js'),
'final-definition': require('./rules/final-definition.js'),
'final-newline': require('./rules/final-newline.js'),
'first-heading-level': require('./rules/first-heading-level.js'),
'hard-break-spaces': require('./rules/hard-break-spaces.js'),
'heading-increment': require('./rules/heading-increment.js'),
'heading-style': require('./rules/heading-style.js'),
'link-title-style': require('./rules/link-title-style.js'),
'list-item-bullet-indent': require('./rules/list-item-bullet-indent.js'),
'list-item-content-indent': require('./rules/list-item-content-indent.js'),
'list-item-indent': require('./rules/list-item-indent.js'),
'list-item-spacing': require('./rules/list-item-spacing.js'),
'maximum-heading-length': require('./rules/maximum-heading-length.js'),
'maximum-line-length': require('./rules/maximum-line-length.js'),
'no-auto-link-without-protocol': require('./rules/no-auto-link-without-protocol.js'),
'no-blockquote-without-caret': require('./rules/no-blockquote-without-caret.js'),
'no-consecutive-blank-lines': require('./rules/no-consecutive-blank-lines.js'),
'no-duplicate-definitions': require('./rules/no-duplicate-definitions.js'),
'no-duplicate-headings': require('./rules/no-duplicate-headings.js'),
'no-emphasis-as-heading': require('./rules/no-emphasis-as-heading.js'),
'no-file-name-articles': require('./rules/no-file-name-articles.js'),
'no-file-name-consecutive-dashes': require('./rules/no-file-name-consecutive-dashes.js'),
'no-file-name-irregular-characters': require('./rules/no-file-name-irregular-characters.js'),
'no-file-name-mixed-case': require('./rules/no-file-name-mixed-case.js'),
'no-file-name-outer-dashes': require('./rules/no-file-name-outer-dashes.js'),
'no-heading-content-indent': require('./rules/no-heading-content-indent.js'),
'no-heading-indent': require('./rules/no-heading-indent.js'),
'no-heading-punctuation': require('./rules/no-heading-punctuation.js'),
'no-html': require('./rules/no-html.js'),
'no-inline-padding': require('./rules/no-inline-padding.js'),
'no-literal-urls': require('./rules/no-literal-urls.js'),
'no-missing-blank-lines': require('./rules/no-missing-blank-lines.js'),
'no-multiple-toplevel-headings': require('./rules/no-multiple-toplevel-headings.js'),
'no-shell-dollars': require('./rules/no-shell-dollars.js'),
'no-shortcut-reference-image': require('./rules/no-shortcut-reference-image.js'),
'no-shortcut-reference-link': require('./rules/no-shortcut-reference-link.js'),
'no-table-indentation': require('./rules/no-table-indentation.js'),
'no-tabs': require('./rules/no-tabs.js'),
'no-undefined-references': require('./rules/no-undefined-references.js'),
'no-unused-definitions': require('./rules/no-unused-definitions.js'),
'ordered-list-marker-style': require('./rules/ordered-list-marker-style.js'),
'ordered-list-marker-value': require('./rules/ordered-list-marker-value.js'),
'rule-style': require('./rules/rule-style.js'),
'strong-marker': require('./rules/strong-marker.js'),
'table-cell-padding': require('./rules/table-cell-padding.js'),
'table-pipe-alignment': require('./rules/table-pipe-alignment.js'),
'table-pipes': require('./rules/table-pipes.js'),
'unordered-list-marker-style': require('./rules/unordered-list-marker-style.js')
};

View File

@ -10,21 +10,43 @@
*
* The default value, `consistent`, detects the first used indentation
* 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
*/
'use strict';

View File

@ -12,36 +12,61 @@
*
* These values can also be passed in as an object, such as:
*
* ```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"}}
*
* <!--Also valid by default (note: `»` represents `\t`)-->
*
* - [»] List item
* - [»] List item
*
* @example {"name": "invalid.md", "label": "input"}
*
* <!--Note: `»` represents `\t`-->
*
* <!-- Always invalid -->
* - [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'`
*/
'use strict';

View File

@ -5,18 +5,26 @@
* @module checkbox-content-indent
* @fileoverview
* Warn when list item checkboxes are followed by too much white-space.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* - [ ] List item
* + [x] List item
* * [X] List item
* - [ ] List item
*
* <!-- Invalid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* - [ ] List item
* + [x] List item
* * [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
*/
'use strict';

View File

@ -12,29 +12,80 @@
* The default value, `consistent`, detects the first used code-block
* style, and will warn when a subsequent code-block uses a different
* 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'` -->
*
* alpha();
*
* Paragraph.
*
* bravo();
*
* @example {"setting": "indented", "name": "invalid.md", "label": "input"}
*
* <!-- Valid, when set to `fenced` or `consistent`, invalid when set to `indented` -->
* ```
* Hello
* ```
* ...
* ```bar
* World
* alpha();
* ```
*
* <!-- Always invalid -->
* Hello
* ...
* Paragraph.
*
* ```
* World
* ```
* bravo();
* ```
*
* @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'` -->
*
* ```
* 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'`
*/
'use strict';

View File

@ -5,12 +5,18 @@
* @module definition-case
* @fileoverview
* 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
*/
'use strict';

View File

@ -5,12 +5,18 @@
* @module definition-spacing
* @fileoverview
* 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
*/
'use strict';

View File

@ -12,14 +12,45 @@
* The default value, `consistent`, detects the first used emphasis
* style, and will warn when a subsequent emphasis uses a different
* style.
* @example
* <!-- Valid when set to `consistent` or `*` -->
* *foo*
* *bar*
*
* <!-- Valid when set to `consistent` or `_` -->
* @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": "_", "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 `'_'`
*/
'use strict';

View File

@ -15,29 +15,54 @@
* In addition it can have the property `allowEmpty` (`boolean`)
* which signifies whether or not to warn for fenced code-blocks without
* 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
*/
'use strict';

View File

@ -11,33 +11,50 @@
* The default value, `consistent`, detects the first used fenced code
* marker style, and will warn when a subsequent fenced code uses a
* 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 `'~'`
*/
'use strict';

View File

@ -11,9 +11,16 @@
* `AUTHORS` or `LICENSE`).
*
* 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"}
*/
'use strict';

View File

@ -5,18 +5,24 @@
* @module final-definition
* @fileoverview
* 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`)
*/
'use strict';

View File

@ -26,7 +26,7 @@ function finalNewline(ast, file) {
var contents = file.toString();
var last = contents.length - 1;
if (last > 0 && contents.charAt(last) !== '\n') {
if (last > -1 && contents.charAt(last) !== '\n') {
file.warn('Missing newline character at end of file');
}
}

View File

@ -7,16 +7,42 @@
* Warn when the first heading has a level other than a specified value.
*
* 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`
*/
'use strict';

View File

@ -5,16 +5,24 @@
* @module hard-break-spaces
* @fileoverview
* 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
*/
'use strict';

View File

@ -5,16 +5,22 @@
* @module heading-increment
* @fileoverview
* 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
*/
'use strict';

View File

@ -12,37 +12,54 @@
* The default value, `consistent`, detects the first used heading
* style, and will warn when a subsequent heading uses a different
* 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
*/
'use strict';

View File

@ -1,70 +0,0 @@
/**
* @author Titus Wormer
* @copyright 2015 Titus Wormer
* @license MIT
* @module Rules
* @fileoverview Map of rule ids to rules.
*/
'use strict';
/* Expose. */
module.exports = {
'no-auto-link-without-protocol': require('./no-auto-link-without-protocol'),
'no-literal-urls': require('./no-literal-urls'),
'no-consecutive-blank-lines': require('./no-consecutive-blank-lines'),
'no-missing-blank-lines': require('./no-missing-blank-lines'),
'blockquote-indentation': require('./blockquote-indentation'),
'no-blockquote-without-caret': require('./no-blockquote-without-caret'),
'code-block-style': require('./code-block-style'),
'checkbox-content-indent': require('./checkbox-content-indent'),
'checkbox-character-style': require('./checkbox-character-style'),
'definition-case': require('./definition-case'),
'definition-spacing': require('./definition-spacing'),
'no-emphasis-as-heading': require('./no-emphasis-as-heading'),
'emphasis-marker': require('./emphasis-marker'),
'fenced-code-flag': require('./fenced-code-flag'),
'fenced-code-marker': require('./fenced-code-marker'),
'file-extension': require('./file-extension'),
'final-newline': require('./final-newline'),
'no-file-name-articles': require('./no-file-name-articles'),
'no-file-name-consecutive-dashes': require('./no-file-name-consecutive-dashes'),
'no-file-name-irregular-characters': require('./no-file-name-irregular-characters'),
'no-file-name-mixed-case': require('./no-file-name-mixed-case'),
'no-file-name-outer-dashes': require('./no-file-name-outer-dashes'),
'final-definition': require('./final-definition'),
'hard-break-spaces': require('./hard-break-spaces'),
'heading-increment': require('./heading-increment'),
'no-heading-content-indent': require('./no-heading-content-indent'),
'no-heading-indent': require('./no-heading-indent'),
'first-heading-level': require('./first-heading-level'),
'maximum-heading-length': require('./maximum-heading-length'),
'no-heading-punctuation': require('./no-heading-punctuation'),
'heading-style': require('./heading-style'),
'no-multiple-toplevel-headings': require('./no-multiple-toplevel-headings'),
'no-duplicate-headings': require('./no-duplicate-headings'),
'no-duplicate-definitions': require('./no-duplicate-definitions'),
'no-html': require('./no-html'),
'no-inline-padding': require('./no-inline-padding'),
'maximum-line-length': require('./maximum-line-length'),
'link-title-style': require('./link-title-style'),
'list-item-bullet-indent': require('./list-item-bullet-indent'),
'list-item-content-indent': require('./list-item-content-indent'),
'list-item-indent': require('./list-item-indent'),
'list-item-spacing': require('./list-item-spacing'),
'ordered-list-marker-style': require('./ordered-list-marker-style'),
'ordered-list-marker-value': require('./ordered-list-marker-value'),
'no-shortcut-reference-image': require('./no-shortcut-reference-image'),
'no-shortcut-reference-link': require('./no-shortcut-reference-link'),
'rule-style': require('./rule-style'),
'no-shell-dollars': require('./no-shell-dollars'),
'strong-marker': require('./strong-marker'),
'no-table-indentation': require('./no-table-indentation'),
'table-pipe-alignment': require('./table-pipe-alignment'),
'table-cell-padding': require('./table-cell-padding'),
'table-pipes': require('./table-pipes'),
'no-tabs': require('./no-tabs'),
'unordered-list-marker-style': require('./unordered-list-marker-style'),
'no-undefined-references': require('./no-undefined-references.js'),
'no-unused-definitions': require('./no-unused-definitions.js')
};

View File

@ -12,23 +12,54 @@
* The default value, `consistent`, detects the first used quote
* style, and will warn when a subsequent titles use a different
* 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": "()"}
*
* <!--Also valid when `consistent`-->
*
* [Example](http://example.com (Example Domain) )
* [Example](http://example.com (Example Domain) )
*
* @example {"name": "invalid.md", "label": "input"}
*
* <!--Always invalid-->
*
* <!-- 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 `'()'`
*/
'use strict';

View File

@ -5,14 +5,25 @@
* @module list-item-bullet-indent
* @fileoverview
* 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
*/
'use strict';

View File

@ -5,16 +5,20 @@
* @module list-item-content-indent
* @fileoverview
* 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: Dont use mixed indentation for children, remove 1 space
*/
'use strict';

View File

@ -9,35 +9,60 @@
*
* Options: `string`, either `'tab-size'`, `'mixed'`, or `'space'`,
* default: `'tab-size'`.
* @example
* <!-- Valid when `tab-size` -->
* * List
* item.
*
* 11. List
* item.
*
* <!-- Valid when `mixed` -->
* * List item.
*
* 11. List item
* @example {"name": "valid.md", "setting": "tab-size"}
*
* * List
* item.
*
* Paragraph.
*
* 11. List
* item.
*
* <!-- Valid when `space` -->
* Paragraph.
*
* * List
* item.
*
* * List
* item.
*
* @example {"name": "valid.md", "setting": "mixed"}
*
* * List item.
*
* Paragraph.
*
* 11. List item
*
* Paragraph.
*
* * List
* item.
*
* * List
* item.
*
* @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'`
*/
'use strict';

View File

@ -6,8 +6,17 @@
* @fileoverview
* Warn when list looseness is incorrect, such as being tight
* 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
* item
*
@ -15,23 +24,29 @@
*
* - item 3
*
* <!-- Valid: -->
* - item 1
* - item 2
* - item 3
* @example {"name": "invalid.md", "label": "input"}
*
* A tight list:
*
* <!-- Invalid: -->
* - Wrapped
* item
* - item 2
* - item 3
*
* <!-- Invalid: -->
* A loose list:
*
* - item 1
*
* - item 2
*
* - 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
*/
'use strict';

View File

@ -9,13 +9,20 @@
* Options: `number`, default: `60`.
*
* 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`
*/
'use strict';

View File

@ -10,24 +10,50 @@
*
* Ignores nodes which cannot be wrapped, such as heasings, tables,
* 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 | cant | 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
*/
'use strict';

View File

@ -5,14 +5,20 @@
* @module no-auto-link-without-protocol
* @fileoverview
* 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
*/
'use strict';

View File

@ -5,16 +5,22 @@
* @module no-blockquote-without-caret
* @fileoverview
* Warn when blank lines without carets are found in a blockquote.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* > Foo...
* >
* > ...Bar.
*
* <!-- Invalid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* > Foo...
*
* > ...Bar.
*
* @example {"name": "invalid.md", "label": "output"}
*
* 2:1: Missing caret in blockquote
*/
'use strict';

View File

@ -6,17 +6,32 @@
* @fileoverview
* Warn for too many consecutive blank lines. Knows about the extra line
* needed between a list and indented code, and two lists.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* Foo...
*
* ...Bar.
*
* <!-- Invalid: -->
* @example {"name": "valid-for-code.md"}
*
* Paragraph.
*
* * List
*
*
* bravo();
*
* @example {"name": "invalid.md", "label": "input"}
*
* Foo...
*
*
* ...Bar.
*
* @example {"name": "invalid.md", "label": "output"}
*
* 4:1: Remove 1 line before node
*/
'use strict';

View File

@ -5,14 +5,20 @@
* @module no-duplicate-definitions
* @fileoverview
* 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)
*/
'use strict';

View File

@ -5,18 +5,25 @@
* @module no-duplicate-headings
* @fileoverview
* Warn when duplicate headings are found.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* # Foo
*
* ## Bar
*
* <!-- Invalid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* # Foo
*
* ## Foo
*
* ## [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)
*/
'use strict';

View File

@ -6,16 +6,22 @@
* @fileoverview
* Warn when emphasis (including strong), instead of a heading, introduces
* a paragraph.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* # Foo
*
* Bar.
*
* <!-- Invalid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* *Foo*
*
* Bar.
*
* @example {"name": "invalid.md", "label": "output"}
*
* 1:1-1:6: Dont use emphasis to introduce a section, use a heading
*/
'use strict';

View File

@ -5,9 +5,20 @@
* @module no-file-name-articles
* @fileoverview
* 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`
*/
'use strict';

View File

@ -5,9 +5,12 @@
* @module no-file-name-consecutive-dashes
* @fileoverview
* 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
*/
'use strict';

View File

@ -14,9 +14,22 @@
*
* Any match by the wrapped or given expressions triggers a
* 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
*/
'use strict';
@ -36,7 +49,7 @@ module.exports = noFileNameIrregularCharacters;
* characters which should not be allowed.
*/
function noFileNameIrregularCharacters(ast, file, preferred) {
var expression = preferred || /[^\\.a-zA-Z0-9-]/;
var expression = preferred || /[^\\\.a-zA-Z0-9-]/;
var match;
if (typeof expression === 'string') {

View File

@ -6,9 +6,14 @@
* @fileoverview
* Warn when a file name uses mixed case: both upper- and lower case
* 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
*/
'use strict';

View File

@ -5,9 +5,16 @@
* @module no-file-name-outer-dashes
* @fileoverview
* 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
*/
'use strict';

View File

@ -5,21 +5,47 @@
* @module no-heading-content-indent
* @fileoverview
* Warn when a headings 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
*
* ## Bar··##
*
* ##··Baz
*
* <!-- Valid: -->
* #·Foo
* @example {"name": "invalid.md", "label": "output"}
*
* ## Bar·##
* 3:4: Remove 1 space before this headings content
* 5:7: Remove 1 space after this headings content
* 7:7: Remove 1 space before this headings 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 headings content
* 3:34: Remove 1 space after this headings content
*/
'use strict';
@ -74,7 +100,7 @@ function noHeadingContentIndent(ast, file) {
char = contents.charAt(index);
}
/* CR/LF bug: wooorm/remark#195. */
/* istanbul ignore if - CR/LF bug: wooorm/remark#195. */
if (!char) {
return;
}

View File

@ -5,9 +5,25 @@
* @module no-heading-indent
* @fileoverview
* 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
*
* ·Foo
@ -18,16 +34,12 @@
* ···Bar
* =====
*
* <!-- 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
*/
'use strict';

View File

@ -11,8 +11,17 @@
*
* Note that these are added to a regex, in a group (`'[' + char + ']'`),
* 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:
*
* # Hello?
@ -23,8 +32,13 @@
*
* # Hello;
*
* <!-- Valid: -->
* # Hello
* @example {"name": "invalid.md", "label": "output"}
*
* 1:1-1:9: Dont add a trailing `:` to headings
* 3:1-3:9: Dont add a trailing `?` to headings
* 5:1-5:9: Dont add a trailing `!` to headings
* 7:1-7:9: Dont add a trailing `,` to headings
* 9:1-9:9: Dont add a trailing `;` to headings
*/
'use strict';

View File

@ -8,12 +8,20 @@
*
* Ignores comments, because they are used by this tool, remark, and
* because markdown doesnt 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
*/
'use strict';

View File

@ -8,12 +8,20 @@
* content.
*
* 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: Dont pad `emphasis` with inner spaces
* 1:19-1:30: Dont pad `emphasis` with inner spaces
* 1:32-1:63: Dont pad `link` with inner spaces
*/
'use strict';

View File

@ -5,12 +5,21 @@
* @module no-literal-urls
* @fileoverview
* 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: Dont use literal URLs without angle brackets
*/
'use strict';
@ -51,7 +60,7 @@ function noLiteralURLs(ast, file) {
if (
initial === head &&
final === tail &&
(value === node.url || value == MAILTO + node.url)
(node.url === MAILTO + value || node.url === value)
) {
file.warn('Dont use literal URLs without angle brackets', node);
}

View File

@ -5,15 +5,21 @@
* @module no-missing-blank-lines
* @fileoverview
* Warn for missing blank lines before a block node.
* @example
* <!-- Invalid: -->
* # Foo
* ## Bar
*
* <!-- Valid: -->
* @example {"name": "valid.md"}
*
* # Foo
*
* ## Bar
*
* @example {"name": "invalid.md", "label": "input"}
*
* # Foo
* ## Bar
*
* @example {"name": "invalid.md", "label": "output"}
*
* 2:1-2:7: Missing blank line before block node
*/
'use strict';

View File

@ -7,16 +7,22 @@
* Warn when multiple top-level headings are used.
*
* Options: `number`, default: `1`.
* @example
* <!-- Invalid, when set to `1` -->
*
* @example {"name": "valid.md", "setting": 1}
*
* # Foo
*
* ## Bar
*
* @example {"name": "invalid.md", "setting": 1, "label": "input"}
*
* # Foo
*
* # Bar
*
* <!-- Valid, when set to `1` -->
* # Foo
* @example {"name": "invalid.md", "setting": 1, "label": "output"}
*
* ## Bar
* 3:1-3:6: Dont use multiple top level headings (3:1)
*/
'use strict';

View File

@ -8,25 +8,30 @@
*
* Ignored indented code blocks and fenced code blocks without language
* flag.
* @example
* <!-- Invalid: -->
* ```bash
* $ echo a
* $ echo a > file
* ```
*
* <!-- Valid: -->
* @example {"name": "valid.md"}
*
* ```sh
* echo a
* echo a > file
* ```
*
* <!-- Also valid: -->
* ```zsh
* $ echo a
* a
* $ echo a > file
* ```
*
* @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
*/
'use strict';

View File

@ -5,16 +5,22 @@
* @module no-shortcut-reference-image
* @fileoverview
* 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]
*
* [foo]: http://foo.bar/baz.png
*
* <!-- Valid: -->
* ![foo][]
* @example {"name": "invalid.md", "label": "output"}
*
* [foo]: http://foo.bar/baz.png
* 1:1-1:7: Use the trailing [] on reference images
*/
'use strict';

View File

@ -5,16 +5,22 @@
* @module no-shortcut-reference-link
* @fileoverview
* 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]
*
* [foo]: http://foo.bar/baz
*
* <!-- Valid: -->
* [foo][]
* @example {"name": "invalid.md", "label": "output"}
*
* [foo]: http://foo.bar/baz
* 1:1-1:6: Use the trailing [] on reference links
*/
'use strict';

View File

@ -5,16 +5,27 @@
* @module no-table-indentation
* @fileoverview
* 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
*/
'use strict';

View File

@ -5,17 +5,42 @@
* @module no-tabs
* @fileoverview
* 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
*/
'use strict';

View File

@ -2,17 +2,23 @@
* @author Titus Wormer
* @copyright 2016 Titus Wormer
* @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][]
*
* [foo]: https://example.com
*
* <!-- Invalid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* [bar][]
*
* @example {"name": "invalid.md", "label": "output"}
*
* 1:1-1:8: Found reference to undefined definition
*/
'use strict';

View File

@ -5,14 +5,20 @@
* @module no-unused-definitions
* @fileoverview
* Warn when unused definitions are found.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* [foo][]
*
* [foo]: https://example.com
*
* <!-- Invalid: -->
* @example {"name": "invalid.md", "label": "input"}
*
* [bar]: https://example.com
*
* @example {"name": "invalid.md", "label": "output"}
*
* 1:1-1:27: Found unused definition
*/
'use strict';

View File

@ -15,16 +15,37 @@
* The default value, `consistent`, detects the first used list
* style, and will warn when a subsequent list uses a different
* style.
* @example
* <!-- Valid when set to `consistent` or `.` -->
*
* @example {"name": "valid.md", "setting": "."}
*
* <!-- This is also valid when `consistent`. -->
*
* 1. Foo
*
* 2. Bar
*
* <!-- 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 `')'`
*/
'use strict';

View File

@ -14,33 +14,64 @@
* relative to the starting point. When set to `'single'`, bullets should
* be the same as the relative starting point. When set to `'one'`, bullets
* should always be `1`.
* @example
* <!-- Valid when set to `one`: -->
*
* @example {"name": "valid.md", "setting": "one"}
*
* 1. Foo
* 1. Bar
* 1. Baz
*
* Paragraph.
*
* 1. Alpha
* 1. Bravo
* 1. Charlie
*
* <!-- Valid when set to `single`: -->
* @example {"name": "valid.md", "setting": "single"}
*
* 1. Foo
* 1. Bar
* 1. Baz
*
* Paragraph.
*
* 3. Alpha
* 3. Bravo
* 3. Charlie
*
* <!-- Valid when set to `ordered`: -->
* @example {"name": "valid.md", "setting": "ordered"}
*
* 1. Foo
* 2. Bar
* 3. Baz
*
* 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'`
*/
'use strict';

View File

@ -10,16 +10,38 @@
*
* Options: `string`, either a valid markdown rule, or `consistent`,
* default: `'consistent'`.
* @example
* <!-- Valid when set to `consistent` or `* * *`: -->
*
* @example {"name": "valid.md", "setting": "* * *"}
*
* <!-- This is also valid when `consistent`. -->
*
* * * *
*
* * * *
*
* <!-- 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'`
*/
'use strict';

View File

@ -2,7 +2,7 @@
* @author Titus Wormer
* @copyright 2015 Titus Wormer
* @license MIT
* @module blockquote-indentation
* @module strong-marker
* @fileoverview
* Warn for violating strong markers.
*
@ -12,14 +12,34 @@
* The default value, `consistent`, detects the first used strong
* style, and will warn when a subsequent strong uses a different
* 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 `'_'`
*/
'use strict';

View File

@ -12,21 +12,40 @@
* The default value, `consistent`, detects the first used cell padding
* style, and will warn when a subsequent cells uses a different
* style.
* @example
* <!-- Valid when set to `consistent` or `padded` -->
*
* @example {"name": "valid.md", "setting": "padded"}
*
* <!--Also valid when `consistent`-->
*
* | A | B |
* | ----- | ----- |
* | Alpha | Bravo |
*
* <!-- Valid when set to `consistent` or `compact` -->
* @example {"name": "valid.md", "setting": "compact"}
*
* <!--Also valid when `consistent`-->
*
* |A |B |
* |-----|-----|
* |Alpha|Bravo|
*
* <!-- 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, isnt
* 3:9: Cell should be padded, isnt
* 3:16: Cell should be padded, isnt
*
* @example {"name": "invalid.md", "label": "output", "setting": "invalid", "config": {"positionless": true}}
*
* 1:1: Invalid table-cell-padding style `invalid`
*/
'use strict';

View File

@ -5,16 +5,23 @@
* @module table-pipe-alignment
* @fileoverview
* Warn when table pipes are not aligned.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* | A | B |
* | ----- | ----- |
* | Alpha | Bravo |
*
* <!-- 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
*/
'use strict';

View File

@ -5,16 +5,25 @@
* @module table-pipes
* @fileoverview
* Warn when table rows are not fenced with pipes.
* @example
* <!-- Valid: -->
*
* @example {"name": "valid.md"}
*
* | A | B |
* | ----- | ----- |
* | Alpha | Bravo |
*
* <!-- 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
*/
'use strict';

View File

@ -13,22 +13,33 @@
* The default value, `consistent`, detects the first used list
* style, and will warn when a subsequent list uses a different
* 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 `'+'`
*/
'use strict';

View File

@ -9,21 +9,26 @@
"validate",
"remark"
],
"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-to-string": "^1.0.0",
"npm-prefix": "^1.1.1",
"plur": "^2.0.0",
"remark-message-control": "^2.0.0",
"trough": "^1.0.0",
@ -39,28 +44,35 @@
],
"devDependencies": {
"browserify": "^13.0.0",
"chalk": "^1.1.3",
"dox": "^0.8.0",
"esmangle": "^1.0.0",
"istanbul": "^0.4.0",
"mocha": "^2.0.0",
"nyc": "^7.1.0",
"remark": "^5.0.0",
"remark-cli": "^1.0.0",
"remark-comment-config": "^4.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",
"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": "mocha --check-leaks test/index.js",
"test-coverage": "istanbul cover _mocha -- test/index.js",
"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": {
@ -87,10 +99,12 @@
"comment-config": null,
"github": null,
"toc": {
"tight": true
"tight": true,
"maxDepth": 2
},
"./": {
"no-missing-blank-lines": false
"no-missing-blank-lines": false,
"list-item-spacing": false
},
"validate-links": null
},

View File

@ -4,8 +4,6 @@
[![Coverage Status][coverage-badge]][coverage-status]
[![Chat][chat-badge]][chat]
<!--lint disable list-item-spacing-->
**remark-lint** is a markdown code style linter. Another linter? Yes.
Ensuring the markdown you (and contributors) write is of great quality will
provide better rendering in all the different markdown parsers, and makes
@ -20,7 +18,6 @@ processor powered by [plugins][remark-plugins] (such as this one).
* [Installation](#installation)
* [Command line](#command-line)
* [Programmatic](#programmatic)
* [remark.use(lint\[, options\])](#remarkuselint-options)
* [Rules](#rules)
* [Configuring remark-lint](#configuring-remark-lint)
* [Using remark to fix your markdown](#using-remark-to-fix-your-markdown)
@ -93,7 +90,6 @@ console.log(report(file));
Now, running `node example.js` yields:
```txt
<stdin>
1:1 warning Missing newline character at end of file final-newline
1:1-1:16 warning First heading level should be `1` first-heading-level
1:1-1:16 warning Dont add a trailing `!` to headings no-heading-punctuation
@ -109,14 +105,18 @@ When processing a file, these warnings are available at `file.messages`, and
look as follows:
```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] },
ruleId: 'first-heading-level',
location: {
start: { line: 1, column: 1, offset: 0 },
end: { line: 1, column: 16, offset: 15 } },
fatal: false,
ruleId: 'first-heading-level',
source: 'remark-lint' }
```
@ -213,10 +213,20 @@ and I strongly suggest checking out how it can make your life easier :+1:
Currently, **remark-lint** is integrated with Atom through
[**linter-markdown**][linter-markdown].
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].
Im very interested in more integrations. Let me know if I can help.
## List of External Rules
External rules can be loaded through the [`external` setting][external].
Learn how to create and use external rules in [`doc/external.md`][doc-external].
<!--
This list is ordered based on the name without prefix, so
excluding `remark-lint-no-` or `remark-lint-`
@ -249,19 +259,19 @@ excluding `remark-lint-no-` or `remark-lint-`
<!-- Definitions -->
[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
[chat-badge]: https://img.shields.io/gitter/room/wooorm/remark.svg
[chat]: https://gitter.im/wooorm/remark
[releases]: https://github.com/wooorm/remark-inline-links/releases
[releases]: https://github.com/wooorm/remark-lint/releases
[license]: LICENSE
@ -290,3 +300,11 @@ excluding `remark-lint-no-` or `remark-lint-`
[vfile]: https://github.com/wooorm/vfile
[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

View File

@ -1,10 +0,0 @@
{
"reset": {
"description": "By default, all rules are turned on unless explicitly set to `false`.\nWhen `reset: true`, the opposite is true: all rules are turned off,\nunless when given a non-nully and non-false value.\n\nOptions: `boolean`, default: `false`.",
"example": "<!-- Explicitly activate rules: -->\n```json\n{\n \"reset\": true,\n \"final-newline\": true\n}\n```\n"
},
"external": {
"description": "External contains a list of extra rules to load.\nThese are, or refer to, an object mapping `ruleId`s to rules.\n\nNote that in node.js, a string can be given (a module\nname or a file), but in the browser an object must be passed in.\n\nWhen using a globally installed remark-lint, globally installed external\nrules are also loaded.\n\nThe prefix `remark-lint-` can be omitted.",
"example": "<!-- Load more rules -->\n```json\n{\n \"external\": [\"foo\", \"bar\", \"baz\"]\n}\n```\n"
}
}

184
script/build-docs.js Normal file
View File

@ -0,0 +1,184 @@
/**
* @author Titus Wormer
* @copyright 2016 Titus Wormer
* @license MIT
* @module remark:lint:script:build-docs
* @fileoverview Creates `rules.md` files.
*/
'use strict';
/* Dependencies. */
var fs = require('fs');
var path = require('path');
var inspect = require('util').inspect;
var chalk = require('chalk');
var remark = require('remark');
var toc = require('remark-toc');
var u = require('unist-builder');
var rules = require('./util/rules');
var rule = require('./util/rule');
/* Processor. */
var markdown = remark().use(toc);
/* Generate. */
[path.join(process.cwd())].forEach(function (filePath) {
var children = [];
var all = rules(filePath);
var root;
rules(filePath).forEach(function (rulePath) {
var info = rule(rulePath);
var tests = info.tests;
children = children.concat(
u('heading', {depth: 2}, [
u('inlineCode', info.ruleId)
]),
markdown.parse(info.description).children
);
Object.keys(tests).forEach(function (setting) {
var fixtures = tests[setting];
Object.keys(fixtures).forEach(function (fileName) {
var fixture = fixtures[fileName];
var label = inspect(JSON.parse(setting));
var sentence;
if (label === 'true') {
label = u('text', 'turned on');
} else {
label = u('inlineCode', label);
}
sentence = [
u('text', 'When this rule is '),
label,
u('text', ', the following file\n'),
u('inlineCode', fileName),
u('text', ' is ')
];
if (fixture.output.length) {
sentence.push(
u('strong', [u('text', 'not')]),
u('text', ' ')
);
}
sentence.push(u('text', 'ok:'));
if (fixture.input == null) {
children.push(
u('paragraph', [
u('text', 'When '),
label,
u('text', ' is passed in, the following error is given:')
])
);
} else {
children.push(
u('paragraph', sentence),
u('code', {lang: 'markdown'}, fixture.input)
);
}
if (fixture.output.length) {
children.push(
u('code', {lang: 'text'}, fixture.output.join('\n'))
);
}
});
});
});
root = u('root', [].concat(
markdown.parse([
'<!-- This file is generated -->',
'',
'# List of Rules',
'',
'This document describes all (' + all.length + ')',
'available rules, what they check for, examples of',
'what they warn for, and how to fix their warnings.',
'',
'Note: both camel-cased and dash-cases versions of rule ids',
'are supported in configuration objects:',
'',
'```json',
'{',
' "final-newline": false',
'}',
'```',
'',
'...is treated the same as:',
'',
'```json',
'{',
' "finalNewline": false',
'}',
'```',
'',
'## Table of Contents',
'',
'## `reset`',
'',
'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.',
'',
'Options: `boolean`, default: `false`.',
'',
'Explicitly activate rules:',
'',
'```json',
'{',
' "reset": true,',
' "final-newline": true',
'}',
'```',
'',
'## `external`',
'',
'External contains a list of extra rules to load. These are,',
'or refer to, an object mapping `ruleId`s to rules.',
'',
'Note that in Node.js, a `string` can be given (a module name',
'or a file path), but in the browser an object must be passed',
'in.',
'',
'When using a globally installed remark-lint, globally installed',
'external rules are also loaded.',
'',
'The prefix `remark-lint-` can be omitted.',
'',
'```js',
'{',
' external: [\'no-empty-sections\', \'./a-local-file.js\']',
'}',
'```',
'',
'Read more about external rules in',
'[`doc/external.md`](./external.md).',
'',
''
].join('\n')).children,
children
));
markdown.run(root);
fs.writeFileSync(
path.join(filePath, 'doc', 'rules.md'),
markdown.stringify(root)
);
console.log(
chalk.green('✓') +
' wrote docs for ' + all.length + ' rules in ' +
path.basename(filePath)
);
});

52
script/build-index.js Normal file
View File

@ -0,0 +1,52 @@
/**
* @author Titus Wormer
* @copyright 2016 Titus Wormer
* @license MIT
* @module remark:lint:script:build-indices
* @fileoverview Creates `index.js` files for rules.
*/
'use strict';
/* Dependencies. */
var fs = require('fs');
var path = require('path');
var chalk = require('chalk');
var rules = require('./util/rules');
/* Generate. */
[path.join(process.cwd())].forEach(function (filePath) {
var base = path.resolve(filePath, 'lib', 'rules.js');
var doc = [];
rules(filePath).forEach(function (rulePath) {
var name = path.basename(rulePath);
var relative = './' + path.relative(path.dirname(base), rulePath);
name = name.slice(0, name.indexOf('.'));
doc.push(
' \'' + name + '\': require(\'' + relative + '\')'
);
});
doc = [].concat(
[
'/* This file is generated. */',
'\'use strict\';',
'module.exports = {'
],
doc.join(',\n'),
[
'};',
''
]
).join('\n');
fs.writeFileSync(path.join(base), doc);
console.log(
chalk.green('✓') +
' wrote `index.js` in ' + path.basename(filePath)
);
});

View File

@ -1,181 +0,0 @@
#!/usr/bin/env node
/**
* @author Titus Wormer
* @copyright 2015 Titus Wormer
* @license MIT
* @module remark:lint:script:build-rule-documentation
* @fileoverview Creates documentation for all exposed
* rules.
*/
'use strict';
/* Dependencies. */
var fs = require('fs');
var path = require('path');
var dox = require('dox');
var remark = require('remark');
var toc = require('remark-toc');
var rules = require('../lib/rules');
var additional = require('./additional.json');
/* Methods. */
var exists = fs.existsSync;
var children = [];
/* Add main heading. */
children.push({
type: 'heading',
depth: 1,
children: [{
type: 'text',
value: 'List of Rules'
}]
});
/* Add main description. */
children.push(
{
type: 'paragraph',
children: [{
type: 'text',
value: 'This document describes all available rules, what they\n' +
'check for, examples of what they warn for, and how to\n' +
'fix their warnings.'
}]
},
{
type: 'paragraph',
children: [
{
type: 'text',
value: 'See the readme for a '
},
{
type: 'link',
url: 'https://github.com/wooorm/remark-lint#list-of-external-rules',
children: [{
type: 'text',
value: 'list of external rules'
}]
},
{
type: 'text',
value: '.'
}
]
}
);
/* Add the rules heading. */
children.push({
type: 'heading',
depth: 2,
children: [{
type: 'text',
value: 'Rules'
}]
});
/* Add a section on how to turn of rules. */
children.push({
type: 'paragraph',
children: [{
type: 'text',
value: 'Remember that rules can always be turned off by\n' +
'passing false. In addition, when reset is given, values can\n' +
'be null or undefined in order to be ignored.'
}]
});
/* Add the table-of-contents heading. */
children.push({
type: 'heading',
depth: 3,
children: [{
type: 'text',
value: 'Table of Contents'
}]
});
/* Add rules. */
Object.keys(additional).sort()
.concat(Object.keys(rules).sort())
.forEach(function (ruleId) {
var description;
var filePath;
var example;
var code;
var tags;
filePath = path.join('lib', 'rules', ruleId + '.js');
if (exists(filePath)) {
code = fs.readFileSync(filePath, 'utf-8');
tags = dox.parseComments(code)[0].tags;
description = find(tags, 'fileoverview');
example = find(tags, 'example');
if (!description) {
throw new Error(ruleId + ' is missing a `@fileoverview`');
}
description = description.string;
example = example && example.string;
} else {
description = additional[ruleId].description;
example = additional[ruleId].example;
}
children.push({
type: 'heading',
depth: 3,
children: [{
type: 'text',
value: ruleId
}]
});
if (example) {
children.push({
type: 'code',
lang: 'md',
value: example
});
}
children = children.concat(remark().parse(description).children);
});
/* Node. */
var node = {type: 'root', children: children};
/* Add toc. */
remark().use(toc).run(node);
/* Write. */
fs.writeFileSync('doc/rules.md', remark().stringify(node));
/**
* Find the first tag in `tags` with a type set to `key`.
*
* @param {Array.<Object>} tags - List of tags.
* @param {string} key - Type of tag.
* @return {Object?} - Tag, when found.
*/
function find(tags, key) {
var value = null;
tags.some(function (tag) {
if (tag && tag.type === key) {
value = tag;
return true;
}
return false;
});
return value;
}

159
script/util/rule.js Executable file
View File

@ -0,0 +1,159 @@
/**
* @author Titus Wormer
* @copyright 2016 Titus Wormer
* @license MIT
* @module remark:lint:script:rule
* @fileoverview Get information for a rule.
*/
'use strict';
/* Dependencies. */
var fs = require('fs');
var path = require('path');
var dox = require('dox');
var strip = require('strip-indent');
var trim = require('trim');
/* Expose. */
module.exports = ruleSync;
/**
* Get information for a rule at `filePath`.
*
* @param {string} filePath - Path to rule.
*/
function ruleSync(filePath) {
var ruleId = path.basename(filePath);
var result = {};
var tests = {};
var description;
var code;
var tags;
var name;
ruleId = ruleId.slice(0, ruleId.indexOf('.'));
code = fs.readFileSync(filePath, 'utf-8');
tags = dox.parseComments(code)[0].tags;
description = find(tags, 'fileoverview');
name = find(tags, 'module');
/* istanbul ignore if */
if (name !== ruleId) {
throw new Error(
ruleId + ' has an invalid `@module`: ' + name
);
}
/* istanbul ignore if */
if (!description) {
throw new Error(ruleId + ' is missing a `@fileoverview`');
}
description = strip(description);
result.ruleId = ruleId;
result.description = trim(description);
result.tests = tests;
result.filePath = filePath;
findAll(tags, 'example').map(strip).forEach(function (example) {
var lines = example.split('\n');
var value = strip(lines.slice(1).join('\n'));
var info;
var setting;
var context;
var name;
try {
info = JSON.parse(lines[0]);
} catch (err) {
/* istanbul ignore next */
throw new Error(
'Could not parse example in ' + ruleId + ':\n' + err.stack
);
}
setting = JSON.stringify(info.setting || true);
context = tests[setting];
name = info.name;
if (!context) {
context = tests[setting] = [];
}
if (!info.label) {
context[name] = {
config: info.config || {},
setting: setting,
input: value,
output: []
};
return;
}
/* istanbul ignore if */
if (info.label !== 'input' && info.label !== 'output') {
throw new Error(
'Expected `input` or `ouput` for `label` in ' +
ruleId + ', not `' + info.label + '`'
);
}
if (!context[name]) {
context[name] = {config: info.config || {}};
}
context[name].setting = setting;
if (info.label === 'output') {
value = value.split('\n');
}
context[name][info.label] = value;
});
return result;
}
/**
* Find the first tag in `tags` with a type set to `key`.
*
* @param {Array.<Object>} tags - List of tags.
* @param {string} key - Type of tag.
* @return {Object?} - Tag, when found.
*/
function find(tags, key) {
var value = null;
tags.some(function (tag) {
if (tag && tag.type === key) {
value = tag;
return true;
}
return false;
});
return value && value.string;
}
/**
* Find the first tag in `tags` with a type set to `key`.
*
* @param {Array.<Object>} tags - List of tags.
* @param {string} key - Type of tag.
* @return {Object?} - Tag, when found.
*/
function findAll(tags, key) {
return tags
.filter(function (tag) {
return tag && tag.type === key;
})
.map(function (tag) {
return tag.string;
});
}

33
script/util/rules.js Normal file
View File

@ -0,0 +1,33 @@
/**
* @author Titus Wormer
* @copyright 2016 Titus Wormer
* @license MIT
* @module remark:lint:script:util:rules
* @fileoverview Get a list of rules.
*/
'use strict';
/* Dependencies. */
var fs = require('fs');
var path = require('path');
/* Expose. */
module.exports = rulesSync;
/**
* Get a list of rules in a package.
*
* @param {string} filePath - Package to look into.
*/
function rulesSync(filePath) {
var basePath = path.join(filePath, 'lib', 'rules');
return fs.readdirSync(basePath)
.filter(function (basename) {
return path.extname(basename) === '.js';
})
.map(function (basename) {
return path.join(basePath, basename);
});
}

View File

@ -1,40 +0,0 @@
/**
* @author Titus Wormer
* @copyright 2015 Titus Wormer
* @license MIT
* @module remark:lint:test:clean
* @fileoverview remark plug-in used to remove positional
* information from remarks syntax tree.
* @todo Externalise into its own repository.
*/
'use strict';
/* Dependencies. */
var visit = require('unist-util-visit');
/**
* Delete the `position` key for each node.
*
* @param {Node} ast - Root node.
*/
function transformer(ast) {
visit(ast, function (node) {
node.position = undefined;
});
}
/**
* Return `transformer`.
*
* @return {Function} - See `transformer`.
*/
function attacher() {
return transformer;
}
/*
* Expose.
*/
module.exports = attacher;

View File

@ -1,19 +0,0 @@
/**
* @author Titus Wormer
* @copyright 2015 Titus Wormer
* @license MIT
* @module remark:lint:test:external
* @fileoverview Map of example external rules.
*/
'use strict';
/* eslint-env commonjs */
/*
* Expose.
*/
module.exports = {
'no-lorem': require('./no-lorem')
};

View File

@ -1,50 +0,0 @@
/**
* @author Titus Wormer
* @copyright 2015 Titus Wormer
* @license MIT
* @module remark:lint:test:no-lorem
* @fileoverview
* Warn when `lorem` is used in a document.
* @example
* <!-- Invalid: -->
* lorem
*
* <!-- Valid: -->
* ipsum
*/
'use strict';
/* eslint-env commonjs */
/*
* Dependencies.
*/
var vfileLocation = require('vfile-location');
/**
* Warn when `lorem` is used in a document.
*
* @param {Node} ast - Root node.
* @param {File} file - Virtual file.
* @param {*} preferred - Ignored.
* @param {Function} done - Callback.
*/
function noLorem(ast, file, preferred, done) {
var content = file.toString();
var expression = /\blorem\b/gi;
var location = vfileLocation(file);
while (expression.exec(content)) {
file.warn('Do not use lorem', location.toPosition(expression.lastIndex));
}
done();
}
/*
* Expose.
*/
module.exports = noLorem;

View File

@ -1,13 +0,0 @@
> Foo
<!-- -->
> Bar
<!-- -->
> Baz
<!-- -->
>

View File

@ -1,13 +0,0 @@
> Foo
<!-- -->
> Bar
<!-- -->
> Baz
<!-- -->
>

View File

@ -1,6 +0,0 @@
---
title: "Example # "
---
# Establishing an example #
Description.

View File

@ -1,7 +0,0 @@
* [x] Foo;
- [x] Bar;
+ [x] Baz;
* [x]

View File

@ -1,7 +0,0 @@
* [ ] Foo;
- [ ] Bar;
+ [ ] Baz;
* [ ]

View File

@ -1,7 +0,0 @@
* [ ] Foo;
- [ ] Bar;
+ [ ] Baz;
* [ ]

View File

@ -1,7 +0,0 @@
* [X] Foo;
- [X] Bar;
+ [X] Baz;
* [X]

View File

@ -1,11 +0,0 @@
* [x] This starts with two spaces;
- [ ] This with three;
- [ ] This with a space and a tab;
+ [X] Below is trailing white space;
* [x]
Foo.

View File

@ -1,15 +0,0 @@
- [x] One;
+ [X] Two;
* [ ] Three;
- [ ] Four;
- [x] Five (code);
+ [X] Below is a single white space;
* [x]
Foo.

View File

@ -1,11 +0,0 @@
Some fenced code block:
```
foo
```
And one with language flag:
```barscript
bar
```

View File

@ -1,7 +0,0 @@
Some indented code block:
foo
And another:
bar

View File

@ -1,13 +0,0 @@
# Hello
<!--lint disable no-duplicate-headings maximum-line-length-->
## Hello
Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello.
<!--lint enable no-duplicate-headings-->
### Hello
Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello.

View File

@ -1,7 +0,0 @@
<!--lint disable maximum-line-length-->
alpha bravo charlie delta echo foxtrot golf hotel india julliet kilo lima mike november.
<!--lint enable maximum-line-length-->
alpha bravo charlie delta echo foxtrot golf hotel india julliet kilo lima mike november.

View File

@ -1,7 +0,0 @@
<!--lint enable maximum-line-length-->
alpha bravo charlie delta echo foxtrot golf hotel india julliet kilo lima mike november.
<!--lint enable maximum-line-length-->
alpha bravo charlie delta echo foxtrot golf hotel india julliet kilo lima mike november.

View File

@ -1,7 +0,0 @@
<!--lint enable maximum-line-length-->
alpha bravo charlie delta echo foxtrot golf hotel india julliet kilo lima mike november.
<!--lint disable maximum-line-length-->
alpha bravo charlie delta echo foxtrot golf hotel india julliet kilo lima mike november.

View File

@ -1 +0,0 @@
Foo <!--lint enable no-html--> <span>This is HTML</span>.

View File

@ -1,5 +0,0 @@
Intro.
<!--lint foo bar-->
Outro.

View File

@ -1,5 +0,0 @@
Intro.
<!--lint enable bar-->
Outro.

View File

@ -1 +0,0 @@
Things should not fail without warnings, nor comments. Alpha bravo charlie delta echo foxtrot.

View File

@ -1,3 +0,0 @@
This document has definitions with improper spacing and casing.
[Invalid]: http://example.com/favicon.ico "Example Domain"

View File

@ -1,3 +0,0 @@
This document has definitions with proper spacing and casing.
[valid]: http://example.com/favicon.ico "Example Domain"

View File

@ -1,3 +0,0 @@
This document has definitions with improper spacing and casing.
[another invalid]: http://example.org/favicon.ico "Example Domain"

Some files were not shown because too many files have changed in this diff Show More