Skip to content

Latest commit

 

History

History
376 lines (351 loc) · 11.2 KB

CustomRules.md

File metadata and controls

376 lines (351 loc) · 11.2 KB

Custom Rules

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.

Implementing Simple 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.

Authoring

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 required Array of String values that identify the rule in output messages and config.
  • description is a required String 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 required Array of String values that groups related rules for easier customization.
  • asynchronous is an optional Boolean value that indicates whether the rule returns a Promise and runs asynchronously.
  • function is a required Function that implements the rule and is passed two parameters:
    • params is an Object with properties that describe the content being analyzed:
      • name is a String that identifies the input file/string.
      • tokens is an Array of markdown-it Tokens with added line and lineNumber properties.
      • lines is an Array of String values corresponding to the lines of the input file/string.
      • frontMatterLines is an Array of String values corresponding to any front matter (not present in lines).
      • config is an Object corresponding to the rule's entry in options.config (if present).
    • onError is a function that takes a single Object parameter with one required and four optional properties:
      • lineNumber is a required Number specifying the 1-based line number of the error.
      • detail is an optional String with information about what caused the error.
      • context is an optional String 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 optional Array with two Number values identifying the 1-based column and length of the error.
      • fixInfo is an optional Object with information about how to fix the error (all properties are optional, but at least one of deleteCount and insertText should be present; when applying a fix, the delete should be performed before the insert):
        • lineNumber is an optional Number specifying the 1-based line number of the edit.
        • editColumn is an optional Number specifying the 1-based column number of the edit.
        • deleteCount is an optional Number specifying the number of characters to delete (the value -1 is used to delete the line).
        • insertText is an optional String 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.

Asynchronous Rules

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.

Examples

References

Params

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
  }
}