In addition to its built-in rules, markdownlint
lets you enhance the linting
experience by passing an array 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). When defined by a file or
package, the export can be a single rule object (see below) or an array of them.
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 without needing to write 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.
Custom rules can (should) operate on a structured set of tokens based on the
micromark
parser
(this is preferred). Alternatively, custom
rules can operate on a structured set of tokens based on the
markdown-it
parser
(legacy support). Finally, custom rules
can operate directly on text with the none
parser
.
A simple rule implementation using the micromark
parser to report a violation
for any use of blockquotes might look like:
/** @type import("markdownlint").Rule */
module.exports = {
"names": [ "any-blockquote-micromark" ],
"description": "Rule that reports an error for any blockquote",
"information": new URL("https://example.com/rules/any-blockquote"),
"tags": [ "test" ],
"parser": "micromark",
"function": (params, onError) => {
const blockquotes = params.parsers.micromark.tokens
.filter((token) => token.type === "blockQuote");
for (const blockquote of blockquotes) {
const lines = blockquote.endLine - blockquote.startLine + 1;
onError({
"lineNumber": blockquote.startLine,
"detail": "Blockquote spans " + lines + " line(s).",
"context": params.lines[blockquote.startLine - 1]
});
}
}
}
That same rule implemented using the markdown-it
parser might look like:
/** @type import("markdownlint").Rule */
module.exports = {
"names": [ "any-blockquote-markdown-it" ],
"description": "Rule that reports an error for any blockquote",
"information": new URL("https://example.com/rules/any-blockquote"),
"tags": [ "test" ],
"parser": "markdownit",
"function": (params, onError) => {
const blockquotes = params.parsers.markdownit.tokens
.filter((token) => token.type === "blockquote_open");
for (const blockquote of blockquotes) {
const [ startIndex, endIndex ] = blockquote.map;
const lines = endIndex - startIndex;
onError({
"lineNumber": blockquote.lineNumber,
"detail": "Blockquote spans " + lines + " line(s).",
"context": blockquote.line
});
}
}
}
A rule is implemented as an Object
:
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.parser
is a requiredString
value"markdownit" | "micromark" | "none"
that specifies the parser data used viaparams.parsers
(see below).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.parsers
is anObject
with properties corresponding to the value ofparser
in the rule definition (see above).markdownit
is anObject
that provides access to output from themarkdown-it
parser.tokens
is anArray
ofmarkdown-it
Token
s with addedline
andlineNumber
properties. (This property was previously on theparams
object.)
micromark
is anObject
that provides access to output from themicromark
parser.tokens
is anArray
ofMicromarkToken
objects.
- Samples for both
tokens
are available via test snapshots.
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).version
is aString
that corresponds to the version ofmarkdownlint
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.