In addition to its built-in rules, markdownlint
lets you enhance the linting
experience by passing a list of custom rules using the options.customRules
property. Custom rules can do everything the built-in
rules can and are defined inline or imported from another package (keyword
markdownlint-rule
on npm). Custom rules can be disabled,
enabled, and customized using the same syntax as built-in rules.
For simple requirements like disallowing certain characters or patterns, the community-developed markdownlint-rule-search-replace plug-in can be used. This plug-in allows anyone to create a set of simple text-replacement rules in JSON without needing to write any code.
Rules are defined by a name (or multiple names), a description, an optional link to more information, one or more tags, and a function that implements the rule's behavior. That function is called once for each file/string input and is passed the parsed input and a function to log any violations.
A simple rule implementation looks like:
module.exports = {
"names": [ "any-blockquote" ],
"description": "Rule that reports an error for any blockquote",
"information": new URL("https://example.com/rules/any-blockquote"),
"tags": [ "test" ],
"function": function rule(params, onError) {
params.parsers.markdownit.tokens.filter(function filterToken(token) {
return token.type === "blockquote_open";
}).forEach(function forToken(blockquote) {
var lines = blockquote.map[1] - blockquote.map[0];
onError({
"lineNumber": blockquote.lineNumber,
"detail": "Blockquote spans " + lines + " line(s).",
"context": blockquote.line.substr(0, 7)
});
});
}
};
A rule is implemented as an Object
with one optional and four required
properties:
names
is a requiredArray
ofString
values that identify the rule in output messages and config.description
is a requiredString
value that describes the rule in output messages.information
is an optional (absolute)URL
of a link to more information about the rule.tags
is a requiredArray
ofString
values that groups related rules for easier customization.asynchronous
is an optionalBoolean
value that indicates whether the rule returns aPromise
and runs asynchronously.function
is a requiredFunction
that implements the rule and is passed two parameters:params
is anObject
with properties that describe the content being analyzed:name
is aString
that identifies the input file/string.tokens
is anArray
ofmarkdown-it
Token
s with addedline
andlineNumber
properties.lines
is anArray
ofString
values corresponding to the lines of the input file/string.frontMatterLines
is anArray
ofString
values corresponding to any front matter (not present inlines
).config
is anObject
corresponding to the rule's entry inoptions.config
(if present).
onError
is a function that takes a singleObject
parameter with one required and four optional properties:lineNumber
is a requiredNumber
specifying the 1-based line number of the error.detail
is an optionalString
with information about what caused the error.context
is an optionalString
with relevant text surrounding the error location.information
is an optional (absolute)URL
of a link to override the same-named value provided by the rule definition. (Uncommon)range
is an optionalArray
with twoNumber
values identifying the 1-based column and length of the error.fixInfo
is an optionalObject
with information about how to fix the error (all properties are optional, but at least one ofdeleteCount
andinsertText
should be present; when applying a fix, the delete should be performed before the insert):lineNumber
is an optionalNumber
specifying the 1-based line number of the edit.editColumn
is an optionalNumber
specifying the 1-based column number of the edit.deleteCount
is an optionalNumber
specifying the number of characters to delete (the value-1
is used to delete the line).insertText
is an optionalString
specifying the text to insert.\n
is the platform-independent way to add a line break; line breaks should be added at the beginning of a line instead of at the end.
The collection of helper functions shared by the built-in rules is available for use by custom rules in the markdownlint-rule-helpers package.
If a rule needs to perform asynchronous operations (such as fetching a network
resource), it can specify the value true
for its asynchronous
property.
Asynchronous rules should return a Promise
from their function
implementation that is resolved when the rule completes. (The value passed to
resolve(...)
is ignored.) Linting violations from asynchronous rules are
reported via the onError
function just like for synchronous rules.
Note: Asynchronous rules cannot be referenced in a synchronous calling
context (i.e., markdownlint.sync(...)
). Attempting to do so throws an
exception.
- Simple rules used by the project's test cases
- Code for all
markdownlint
built-in rules - Package configuration for publishing to npm
- Packages should export a single rule object or an
Array
of rule objects
- Packages should export a single rule object or an
- Custom rules from the axibase/docs-util repository
- Custom rules from the webhintio/hint repository
The Markdown document:
# Title
Text *text* text.
Yields the params
object:
{
"name": "doc/example.md",
"tokens": [
{
"type": "heading_open",
"tag": "h1",
"attrs": null,
"map": [ 0, 1 ],
"nesting": 1,
"level": 0,
"children": null,
"content": "",
"markup": "#",
"info": "",
"meta": null,
"block": true,
"hidden": false,
"line": "# Title",
"lineNumber": 1
},
{
"type": "inline",
"tag": "",
"attrs": null,
"map": [ 0, 1 ],
"nesting": 0,
"level": 1,
"children": [
{
"type": "text",
"tag": "",
"attrs": null,
"map": null,
"nesting": 0,
"level": 0,
"children": null,
"content": "Title",
"markup": "",
"info": "",
"meta": null,
"block": false,
"hidden": false,
"lineNumber": 1,
"line": "# Title"
}
],
"content": "Title",
"markup": "",
"info": "",
"meta": null,
"block": true,
"hidden": false,
"line": "# Title",
"lineNumber": 1
},
{
"type": "heading_close",
"tag": "h1",
"attrs": null,
"map": null,
"nesting": -1,
"level": 0,
"children": null,
"content": "",
"markup": "#",
"info": "",
"meta": null,
"block": true,
"hidden": false
},
{
"type": "paragraph_open",
"tag": "p",
"attrs": null,
"map": [ 2, 3 ],
"nesting": 1,
"level": 0,
"children": null,
"content": "",
"markup": "",
"info": "",
"meta": null,
"block": true,
"hidden": false,
"line": "Text *text* text.",
"lineNumber": 3
},
{
"type": "inline",
"tag": "",
"attrs": null,
"map": [ 2, 3 ],
"nesting": 0,
"level": 1,
"children": [
{
"type": "text",
"tag": "",
"attrs": null,
"map": null,
"nesting": 0,
"level": 0,
"children": null,
"content": "Text ",
"markup": "",
"info": "",
"meta": null,
"block": false,
"hidden": false,
"lineNumber": 3,
"line": "Text *text* text."
},
{
"type": "em_open",
"tag": "em",
"attrs": null,
"map": null,
"nesting": 1,
"level": 1,
"children": null,
"content": "",
"markup": "*",
"info": "",
"meta": null,
"block": false,
"hidden": false,
"lineNumber": 3,
"line": "Text *text* text."
},
{
"type": "text",
"tag": "",
"attrs": null,
"map": null,
"nesting": 0,
"level": 1,
"children": null,
"content": "text",
"markup": "",
"info": "",
"meta": null,
"block": false,
"hidden": false,
"lineNumber": 3,
"line": "Text *text* text."
},
{
"type": "em_close",
"tag": "em",
"attrs": null,
"map": null,
"nesting": -1,
"level": 0,
"children": null,
"content": "",
"markup": "*",
"info": "",
"meta": null,
"block": false,
"hidden": false,
"lineNumber": 3,
"line": "Text *text* text."
},
{
"type": "text",
"tag": "",
"attrs": null,
"map": null,
"nesting": 0,
"level": 0,
"children": null,
"content": " text.",
"markup": "",
"info": "",
"meta": null,
"block": false,
"hidden": false,
"lineNumber": 3,
"line": "Text *text* text."
}
],
"content": "Text *text* text.",
"markup": "",
"info": "",
"meta": null,
"block": true,
"hidden": false,
"line": "Text *text* text.",
"lineNumber": 3
},
{
"type": "paragraph_close",
"tag": "p",
"attrs": null,
"map": null,
"nesting": -1,
"level": 0,
"children": null,
"content": "",
"markup": "",
"info": "",
"meta": null,
"block": true,
"hidden": false
}
],
"lines": [
"# Title",
"",
"Text *text* text.",
""
],
"frontMatterLines": [],
"config": {
"customValue1": "abc",
"customValue2": 123
}
}