Skip to content

Commit

Permalink
Add better external rule support, and refactor module
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
wooorm committed Aug 2, 2016
1 parent 60907ad commit 8dedd19
Show file tree
Hide file tree
Showing 236 changed files with 4,115 additions and 4,890 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.DS_Store
*.log
coverage/
.nyc_output/
node_modules/
remark-lint.js
remark-lint.min.js
1 change: 0 additions & 1 deletion .remarkignore

This file was deleted.

1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ node_js:
- '4.0'
- '5.0'
- '6.0'
sudo: false
after_success:
- bash <(curl -s https://codecov.io/bash)
deploy:
Expand Down
207 changes: 207 additions & 0 deletions doc/external.md
Original file line number Diff line number Diff line change
@@ -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 id’s 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 they’re 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
Loading

0 comments on commit 8dedd19

Please sign in to comment.