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.
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.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.function
is a required synchronousFunction
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
objects 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.details
is an optionalString
with information about what caused the error.context
is an optionalString
with relevant text surrounding the error location.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.
- 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 Microsoft/vscode-docs-authoring repository
- 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
}
}