mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-09-22 05:40:48 +02:00
Enable custom rules to use the micromark parser, export micromark helpers for reuse.
This commit is contained in:
parent
264da24dae
commit
5cc40c54b7
16 changed files with 4109 additions and 113 deletions
57
README.md
57
README.md
|
@ -13,22 +13,31 @@ npm install markdownlint --save-dev
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The [Markdown](https://en.wikipedia.org/wiki/Markdown) markup language
|
The [Markdown][markdown] markup language is designed to be easy to read, write,
|
||||||
is designed to be easy to read, write, and understand. It succeeds -
|
and understand. It succeeds - and its flexibility is both a benefit and a
|
||||||
and its flexibility is both a benefit and a drawback. Many styles are
|
drawback. Many styles are possible, so formatting can be inconsistent; some
|
||||||
possible, so formatting can be inconsistent. Some constructs don't
|
constructs don't work well in all parsers and should be avoided.
|
||||||
work well in all parsers and should be avoided. The
|
|
||||||
[CommonMark](https://commonmark.org/) specification standardizes
|
|
||||||
parsers - but not authors.
|
|
||||||
|
|
||||||
`markdownlint` is a
|
`markdownlint` is a [static analysis][static-analysis] tool for
|
||||||
[static analysis](https://en.wikipedia.org/wiki/Static_program_analysis)
|
[Node.js][nodejs] with a library of rules to enforce standards and consistency
|
||||||
tool for [Node.js](https://nodejs.org/) with a library of rules
|
for Markdown files. It was inspired by - and heavily influenced by - Mark
|
||||||
to enforce standards and consistency for Markdown files. It was
|
Harrison's [markdownlint][markdownlint-ruby] for Ruby. The initial rules, rule
|
||||||
inspired by - and heavily influenced by - Mark Harrison's
|
documentation, and test cases came from that project.
|
||||||
[markdownlint](https://github.com/markdownlint/markdownlint) for
|
|
||||||
[Ruby](https://www.ruby-lang.org/). The initial rules, rule documentation,
|
`markdownlint` uses the [`micromark`][micromark] parser and honors the
|
||||||
and test cases came directly from that project.
|
[CommonMark][commonmark] specification for Markdown. It additionally supports
|
||||||
|
popular [GitHub Flavored Markdown (GFM)][gfm] syntax like autolinks and tables
|
||||||
|
as well as directives, footnotes, and math syntax - all implemented by
|
||||||
|
[`micromark` extensions][micromark-extensions].
|
||||||
|
|
||||||
|
[commonmark]: https://commonmark.org/
|
||||||
|
[gfm]: https://github.github.com/gfm/
|
||||||
|
[markdown]: https://en.wikipedia.org/wiki/Markdown
|
||||||
|
[markdownlint-ruby]: https://github.com/markdownlint/markdownlint
|
||||||
|
[micromark]: https://github.com/micromark/micromark
|
||||||
|
[micromark-extensions]: https://github.com/micromark/micromark?tab=readme-ov-file#list-of-extensions
|
||||||
|
[nodejs]: https://nodejs.org/
|
||||||
|
[static-analysis]: https://en.wikipedia.org/wiki/Static_program_analysis
|
||||||
|
|
||||||
### Related
|
### Related
|
||||||
|
|
||||||
|
@ -565,7 +574,7 @@ Type: `Array` of `Array` of `Function` and plugin parameters
|
||||||
|
|
||||||
Specifies additional [`markdown-it` plugins][markdown-it-plugin] to use when
|
Specifies additional [`markdown-it` plugins][markdown-it-plugin] to use when
|
||||||
parsing input. Plugins can be used to support additional syntax and features for
|
parsing input. Plugins can be used to support additional syntax and features for
|
||||||
advanced scenarios.
|
advanced scenarios. *Deprecated.*
|
||||||
|
|
||||||
[markdown-it-plugin]: https://www.npmjs.com/search?q=keywords:markdown-it-plugin
|
[markdown-it-plugin]: https://www.npmjs.com/search?q=keywords:markdown-it-plugin
|
||||||
|
|
||||||
|
@ -601,23 +610,20 @@ Specifies which version of the `result` object to return (see the "Usage"
|
||||||
section below for examples).
|
section below for examples).
|
||||||
|
|
||||||
Passing a `resultVersion` of `0` corresponds to the original, simple format
|
Passing a `resultVersion` of `0` corresponds to the original, simple format
|
||||||
where each error is identified by rule name and line number. *This is
|
where each error is identified by rule name and line number. *Deprecated*
|
||||||
deprecated.*
|
|
||||||
|
|
||||||
Passing a `resultVersion` of `1` corresponds to a detailed format where each
|
Passing a `resultVersion` of `1` corresponds to a detailed format where each
|
||||||
error includes information about the line number, rule name, alias, description,
|
error includes information about the line number, rule name, alias, description,
|
||||||
as well as any additional detail or context that is available. *This is
|
as well as any additional detail or context that is available. *Deprecated*
|
||||||
deprecated.*
|
|
||||||
|
|
||||||
Passing a `resultVersion` of `2` corresponds to a detailed format where each
|
Passing a `resultVersion` of `2` corresponds to a detailed format where each
|
||||||
error includes information about the line number, rule names, description, as
|
error includes information about the line number, rule names, description, as
|
||||||
well as any additional detail or context that is available. *This is
|
well as any additional detail or context that is available. *Deprecated*
|
||||||
deprecated.*
|
|
||||||
|
|
||||||
Passing a `resultVersion` of `3` corresponds to the detailed version `2` format
|
Passing a `resultVersion` of `3` corresponds to the detailed version `2` format
|
||||||
with additional information about how to fix automatically-fixable errors. In
|
with additional information about how to fix automatically-fixable errors. In
|
||||||
this mode, all errors that occur on each line are reported (other versions
|
this mode, all errors that occur on each line are reported (other versions
|
||||||
report only the first error for each rule). *This is the default.*
|
report only the first error for each rule). This is the default behavior.
|
||||||
|
|
||||||
##### options.strings
|
##### options.strings
|
||||||
|
|
||||||
|
@ -946,10 +952,11 @@ Generate normal and minified scripts with:
|
||||||
npm run build-demo
|
npm run build-demo
|
||||||
```
|
```
|
||||||
|
|
||||||
Then reference `markdown-it` and `markdownlint`:
|
Then reference `markdownlint` and `micromark` scripts:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script src="demo/markdown-it.min.js"></script>
|
<script src="demo/micromark-browser.js"></script>
|
||||||
|
<script src="demo/micromark-html-browser.js"></script>
|
||||||
<script src="demo/markdownlint-browser.min.js"></script>
|
<script src="demo/markdownlint-browser.min.js"></script>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1710,7 +1710,7 @@ function validateRuleList(ruleList, synchronous) {
|
||||||
!result &&
|
!result &&
|
||||||
(rule.parser !== undefined) &&
|
(rule.parser !== undefined) &&
|
||||||
(rule.parser !== "markdownit") &&
|
(rule.parser !== "markdownit") &&
|
||||||
!((customIndex < 0) && (rule.parser === "micromark")) &&
|
(rule.parser !== "micromark") &&
|
||||||
(rule.parser !== "none")
|
(rule.parser !== "none")
|
||||||
) {
|
) {
|
||||||
result = newError("parser", rule.parser);
|
result = newError("parser", rule.parser);
|
||||||
|
|
|
@ -14,9 +14,8 @@ built-in rules.
|
||||||
For simple requirements like disallowing certain characters or patterns,
|
For simple requirements like disallowing certain characters or patterns,
|
||||||
the community-developed
|
the community-developed
|
||||||
[markdownlint-rule-search-replace][markdownlint-rule-search-replace]
|
[markdownlint-rule-search-replace][markdownlint-rule-search-replace]
|
||||||
plug-in can be used.
|
plug-in can be used. This plug-in allows anyone to create a set of simple
|
||||||
This plug-in allows anyone to create a set of simple text-replacement rules in
|
text-replacement rules without needing to write code.
|
||||||
JSON without needing to write any code.
|
|
||||||
|
|
||||||
[markdownlint-rule-search-replace]: https://www.npmjs.com/package/markdownlint-rule-search-replace
|
[markdownlint-rule-search-replace]: https://www.npmjs.com/package/markdownlint-rule-search-replace
|
||||||
|
|
||||||
|
@ -27,29 +26,62 @@ 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
|
behavior. That function is called once for each file/string input and is passed
|
||||||
the parsed input and a function to log any violations.
|
the parsed input and a function to log any violations.
|
||||||
|
|
||||||
A simple rule implementation looks like:
|
Custom rules can (should) operate on a structured set of tokens based on the
|
||||||
|
[`micromark`][micromark] `parser` (this is preferred). Alternatively, custom
|
||||||
|
rules can operate on a structured set of tokens based on the
|
||||||
|
[`markdown-it`][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:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
/** @type import("markdownlint").Rule */
|
/** @type import("markdownlint").Rule */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"names": [ "any-blockquote" ],
|
"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:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
/** @type import("markdownlint").Rule */
|
||||||
|
module.exports = {
|
||||||
|
"names": [ "any-blockquote-markdown-it" ],
|
||||||
"description": "Rule that reports an error for any blockquote",
|
"description": "Rule that reports an error for any blockquote",
|
||||||
"information": new URL("https://example.com/rules/any-blockquote"),
|
"information": new URL("https://example.com/rules/any-blockquote"),
|
||||||
"tags": [ "test" ],
|
"tags": [ "test" ],
|
||||||
"parser": "markdownit",
|
"parser": "markdownit",
|
||||||
"function": function rule(params, onError) {
|
"function": (params, onError) => {
|
||||||
params.parsers.markdownit.tokens.filter(function filterToken(token) {
|
const blockquotes = params.parsers.markdownit.tokens
|
||||||
return token.type === "blockquote_open";
|
.filter(((token) => token.type === "blockquote_open"));
|
||||||
}).forEach(function forToken(blockquote) {
|
for (const blockquote of blockquotes) {
|
||||||
var lines = blockquote.map[1] - blockquote.map[0];
|
const [ startIndex, endIndex ] = blockquote.map;
|
||||||
|
const lines = endIndex - startIndex;
|
||||||
onError({
|
onError({
|
||||||
"lineNumber": blockquote.lineNumber,
|
"lineNumber": blockquote.lineNumber,
|
||||||
"detail": "Blockquote spans " + lines + " line(s).",
|
"detail": "Blockquote spans " + lines + " line(s).",
|
||||||
"context": blockquote.line.substr(0, 7)
|
"context": blockquote.line
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
A rule is implemented as an `Object`:
|
A rule is implemented as an `Object`:
|
||||||
|
@ -62,9 +94,8 @@ A rule is implemented as an `Object`:
|
||||||
about the rule.
|
about the rule.
|
||||||
- `tags` is a required `Array` of `String` values that groups related rules for
|
- `tags` is a required `Array` of `String` values that groups related rules for
|
||||||
easier customization.
|
easier customization.
|
||||||
- `parser` is a required `String` value `"markdownit" | "none"` that specifies
|
- `parser` is a required `String` value `"markdownit" | "micromark" | "none"`
|
||||||
the parser data used via `params.parsers` (see below).
|
that specifies the parser data used via `params.parsers` (see below).
|
||||||
- Note: The value `"micromark"` is valid but is NOT currently supported.
|
|
||||||
- `asynchronous` is an optional `Boolean` value that indicates whether the rule
|
- `asynchronous` is an optional `Boolean` value that indicates whether the rule
|
||||||
returns a `Promise` and runs asynchronously.
|
returns a `Promise` and runs asynchronously.
|
||||||
- `function` is a required `Function` that implements the rule and is passed two
|
- `function` is a required `Function` that implements the rule and is passed two
|
||||||
|
@ -79,7 +110,10 @@ A rule is implemented as an `Object`:
|
||||||
- `tokens` is an `Array` of [`markdown-it` `Token`s][markdown-it-token]
|
- `tokens` is an `Array` of [`markdown-it` `Token`s][markdown-it-token]
|
||||||
with added `line` and `lineNumber` properties. (This property was
|
with added `line` and `lineNumber` properties. (This property was
|
||||||
previously on the `params` object.)
|
previously on the `params` object.)
|
||||||
- Samples for `tokens` are available via [test snapshots][tokens].
|
- `micromark` is an `Object` that provides access to output from the
|
||||||
|
[`micromark`][micromark] parser.
|
||||||
|
- `tokens` is an `Array` of [`MicromarkToken`][micromark-token] objects.
|
||||||
|
- Samples for both `tokens` are available via [test snapshots][tokens].
|
||||||
- `lines` is an `Array` of `String` values corresponding to the lines of the
|
- `lines` is an `Array` of `String` values corresponding to the lines of the
|
||||||
input file/string.
|
input file/string.
|
||||||
- `frontMatterLines` is an `Array` of `String` values corresponding to any
|
- `frontMatterLines` is an `Array` of `String` values corresponding to any
|
||||||
|
@ -152,6 +186,8 @@ exception.
|
||||||
[markdown-it]: https://github.com/markdown-it/markdown-it
|
[markdown-it]: https://github.com/markdown-it/markdown-it
|
||||||
[markdown-it-token]: https://markdown-it.github.io/markdown-it/#Token
|
[markdown-it-token]: https://markdown-it.github.io/markdown-it/#Token
|
||||||
[markdownlint-rule]: https://www.npmjs.com/search?q=keywords:markdownlint-rule
|
[markdownlint-rule]: https://www.npmjs.com/search?q=keywords:markdownlint-rule
|
||||||
|
[micromark]: https://github.com/micromark/micromark
|
||||||
|
[micromark-token]: ../lib/markdownlint.d.ts
|
||||||
[rule-helpers]: https://www.npmjs.com/package/markdownlint-rule-helpers
|
[rule-helpers]: https://www.npmjs.com/package/markdownlint-rule-helpers
|
||||||
[options-custom-rules]: ../README.md#optionscustomrules
|
[options-custom-rules]: ../README.md#optionscustomrules
|
||||||
[test-rules]: ../test/rules
|
[test-rules]: ../test/rules
|
||||||
|
|
1
helpers/.npmignore
Normal file
1
helpers/.npmignore
Normal file
|
@ -0,0 +1 @@
|
||||||
|
test.js
|
|
@ -3,7 +3,10 @@
|
||||||
"version": "0.26.0",
|
"version": "0.26.0",
|
||||||
"description": "A collection of markdownlint helper functions for custom rules",
|
"description": "A collection of markdownlint helper functions for custom rules",
|
||||||
"main": "./helpers.js",
|
"main": "./helpers.js",
|
||||||
"exports": "./helpers.js",
|
"exports": {
|
||||||
|
".": "./helpers.js",
|
||||||
|
"./micromark": "./micromark-helpers.cjs"
|
||||||
|
},
|
||||||
"author": "David Anson (https://dlaa.me/)",
|
"author": "David Anson (https://dlaa.me/)",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/DavidAnson/markdownlint",
|
"homepage": "https://github.com/DavidAnson/markdownlint",
|
||||||
|
|
28
helpers/test.cjs
Normal file
28
helpers/test.cjs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// eslint-disable-next-line n/no-extraneous-require
|
||||||
|
const test = require("ava").default;
|
||||||
|
const { "exports": packageExports, name } = require("../helpers/package.json");
|
||||||
|
|
||||||
|
const exportMappings = new Map([
|
||||||
|
[ ".", "../helpers/helpers.js" ],
|
||||||
|
[ "./micromark", "../helpers/micromark-helpers.cjs" ]
|
||||||
|
]);
|
||||||
|
|
||||||
|
test("exportMappings", (t) => {
|
||||||
|
t.deepEqual(
|
||||||
|
Object.keys(packageExports),
|
||||||
|
[ ...exportMappings.keys() ]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const [ exportName, exportPath ] of exportMappings) {
|
||||||
|
test(exportName, (t) => {
|
||||||
|
t.is(
|
||||||
|
require(exportName.replace(/^\./u, name)),
|
||||||
|
require(exportPath)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
|
@ -58,7 +58,7 @@ function validateRuleList(ruleList, synchronous) {
|
||||||
!result &&
|
!result &&
|
||||||
(rule.parser !== undefined) &&
|
(rule.parser !== undefined) &&
|
||||||
(rule.parser !== "markdownit") &&
|
(rule.parser !== "markdownit") &&
|
||||||
!((customIndex < 0) && (rule.parser === "micromark")) &&
|
(rule.parser !== "micromark") &&
|
||||||
(rule.parser !== "none")
|
(rule.parser !== "none")
|
||||||
) {
|
) {
|
||||||
result = newError("parser", rule.parser);
|
result = newError("parser", rule.parser);
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
"lint-test-repos": "ava --timeout=10m test/markdownlint-test-repos-*.js",
|
"lint-test-repos": "ava --timeout=10m test/markdownlint-test-repos-*.js",
|
||||||
"serial-config-docs": "npm run build-config && npm run build-docs",
|
"serial-config-docs": "npm run build-config && npm run build-docs",
|
||||||
"serial-declaration-demo": "npm run build-declaration && npm-run-all --continue-on-error --parallel build-demo test-declaration",
|
"serial-declaration-demo": "npm run build-declaration && npm-run-all --continue-on-error --parallel build-demo test-declaration",
|
||||||
"test": "ava --timeout=30s test/markdownlint-test.js test/markdownlint-test-config.js test/markdownlint-test-custom-rules.js test/markdownlint-test-helpers.js test/markdownlint-test-micromark.mjs test/markdownlint-test-result-object.js test/markdownlint-test-scenarios.js",
|
"test": "ava --timeout=30s test/markdownlint-test.js test/markdownlint-test-config.js test/markdownlint-test-custom-rules.js test/markdownlint-test-helpers.js test/markdownlint-test-micromark.mjs test/markdownlint-test-result-object.js test/markdownlint-test-scenarios.js helpers/test.cjs",
|
||||||
"test-cover": "c8 --100 npm test",
|
"test-cover": "c8 --100 npm test",
|
||||||
"test-declaration": "cd example/typescript && tsc --module nodenext && tsc --module commonjs && node type-check.js",
|
"test-declaration": "cd example/typescript && tsc --module nodenext && tsc --module commonjs && node type-check.js",
|
||||||
"test-extra": "ava --timeout=10m test/markdownlint-test-extra-parse.js test/markdownlint-test-extra-type.js",
|
"test-extra": "ava --timeout=10m test/markdownlint-test-extra-parse.js test/markdownlint-test-extra-type.js",
|
||||||
|
|
|
@ -7,6 +7,7 @@ const test = require("ava").default;
|
||||||
const markdownlint = require("../lib/markdownlint");
|
const markdownlint = require("../lib/markdownlint");
|
||||||
const customRules = require("./rules/rules.js");
|
const customRules = require("./rules/rules.js");
|
||||||
const { homepage, version } = require("../package.json");
|
const { homepage, version } = require("../package.json");
|
||||||
|
const { newLineRe } = require("../helpers/helpers.js");
|
||||||
|
|
||||||
test("customRulesV0", (t) => new Promise((resolve) => {
|
test("customRulesV0", (t) => new Promise((resolve) => {
|
||||||
t.plan(4);
|
t.plan(4);
|
||||||
|
@ -22,7 +23,8 @@ test("customRulesV0", (t) => new Promise((resolve) => {
|
||||||
t.falsy(err);
|
t.falsy(err);
|
||||||
const expectedResult = {};
|
const expectedResult = {};
|
||||||
expectedResult[customRulesMd] = {
|
expectedResult[customRulesMd] = {
|
||||||
"any-blockquote": [ 12 ],
|
"any-blockquote-markdown-it": [ 12 ],
|
||||||
|
"any-blockquote-micromark": [ 12 ],
|
||||||
"every-n-lines": [ 2, 4, 6, 10, 12 ],
|
"every-n-lines": [ 2, 4, 6, 10, 12 ],
|
||||||
"first-line": [ 1 ],
|
"first-line": [ 1 ],
|
||||||
"letters-E-X": [ 3, 7 ]
|
"letters-E-X": [ 3, 7 ]
|
||||||
|
@ -31,7 +33,9 @@ test("customRulesV0", (t) => new Promise((resolve) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let actualMessage = actualResult.toString();
|
let actualMessage = actualResult.toString();
|
||||||
let expectedMessage =
|
let expectedMessage =
|
||||||
"./test/custom-rules.md: 12: any-blockquote" +
|
"./test/custom-rules.md: 12: any-blockquote-markdown-it" +
|
||||||
|
" Rule that reports an error for any blockquote\n" +
|
||||||
|
"./test/custom-rules.md: 12: any-blockquote-micromark" +
|
||||||
" Rule that reports an error for any blockquote\n" +
|
" Rule that reports an error for any blockquote\n" +
|
||||||
"./test/custom-rules.md: 2: every-n-lines" +
|
"./test/custom-rules.md: 2: every-n-lines" +
|
||||||
" Rule that reports an error every N lines\n" +
|
" Rule that reports an error every N lines\n" +
|
||||||
|
@ -53,7 +57,9 @@ test("customRulesV0", (t) => new Promise((resolve) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
actualMessage = actualResult.toString(true);
|
actualMessage = actualResult.toString(true);
|
||||||
expectedMessage =
|
expectedMessage =
|
||||||
"./test/custom-rules.md: 12: any-blockquote" +
|
"./test/custom-rules.md: 12: any-blockquote-markdown-it" +
|
||||||
|
" Rule that reports an error for any blockquote\n" +
|
||||||
|
"./test/custom-rules.md: 12: any-blockquote-micromark" +
|
||||||
" Rule that reports an error for any blockquote\n" +
|
" Rule that reports an error for any blockquote\n" +
|
||||||
"./test/custom-rules.md: 2: every-n-lines" +
|
"./test/custom-rules.md: 2: every-n-lines" +
|
||||||
" Rule that reports an error every N lines\n" +
|
" Rule that reports an error every N lines\n" +
|
||||||
|
@ -91,13 +97,22 @@ test("customRulesV1", (t) => new Promise((resolve) => {
|
||||||
const expectedResult = {};
|
const expectedResult = {};
|
||||||
expectedResult[customRulesMd] = [
|
expectedResult[customRulesMd] = [
|
||||||
{ "lineNumber": 12,
|
{ "lineNumber": 12,
|
||||||
"ruleName": "any-blockquote",
|
"ruleName": "any-blockquote-markdown-it",
|
||||||
"ruleAlias": "any-blockquote",
|
"ruleAlias": "any-blockquote-markdown-it",
|
||||||
"ruleDescription": "Rule that reports an error for any blockquote",
|
"ruleDescription": "Rule that reports an error for any blockquote",
|
||||||
"ruleInformation":
|
"ruleInformation":
|
||||||
`${homepage}/blob/main/test/rules/any-blockquote.js`,
|
`${homepage}/blob/main/test/rules/any-blockquote.js`,
|
||||||
"errorDetail": "Blockquote spans 1 line(s).",
|
"errorDetail": "Blockquote spans 1 line(s).",
|
||||||
"errorContext": "> Block",
|
"errorContext": "> Blockquote",
|
||||||
|
"errorRange": null },
|
||||||
|
{ "lineNumber": 12,
|
||||||
|
"ruleName": "any-blockquote-micromark",
|
||||||
|
"ruleAlias": "any-blockquote-micromark",
|
||||||
|
"ruleDescription": "Rule that reports an error for any blockquote",
|
||||||
|
"ruleInformation":
|
||||||
|
`${homepage}/blob/main/test/rules/any-blockquote.js`,
|
||||||
|
"errorDetail": "Blockquote spans 1 line(s).",
|
||||||
|
"errorContext": "> Blockquote",
|
||||||
"errorRange": null },
|
"errorRange": null },
|
||||||
{ "lineNumber": 2,
|
{ "lineNumber": 2,
|
||||||
"ruleName": "every-n-lines",
|
"ruleName": "every-n-lines",
|
||||||
|
@ -170,9 +185,12 @@ test("customRulesV1", (t) => new Promise((resolve) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const actualMessage = actualResult.toString();
|
const actualMessage = actualResult.toString();
|
||||||
const expectedMessage =
|
const expectedMessage =
|
||||||
"./test/custom-rules.md: 12: any-blockquote/any-blockquote" +
|
"./test/custom-rules.md: 12: any-blockquote-markdown-it/any-blockquote-markdown-it" +
|
||||||
" Rule that reports an error for any blockquote" +
|
" Rule that reports an error for any blockquote" +
|
||||||
" [Blockquote spans 1 line(s).] [Context: \"> Block\"]\n" +
|
" [Blockquote spans 1 line(s).] [Context: \"> Blockquote\"]\n" +
|
||||||
|
"./test/custom-rules.md: 12: any-blockquote-micromark/any-blockquote-micromark" +
|
||||||
|
" Rule that reports an error for any blockquote" +
|
||||||
|
" [Blockquote spans 1 line(s).] [Context: \"> Blockquote\"]\n" +
|
||||||
"./test/custom-rules.md: 2: every-n-lines/every-n-lines" +
|
"./test/custom-rules.md: 2: every-n-lines/every-n-lines" +
|
||||||
" Rule that reports an error every N lines [Line number 2]\n" +
|
" Rule that reports an error every N lines [Line number 2]\n" +
|
||||||
"./test/custom-rules.md: 4: every-n-lines/every-n-lines" +
|
"./test/custom-rules.md: 4: every-n-lines/every-n-lines" +
|
||||||
|
@ -211,12 +229,20 @@ test("customRulesV2", (t) => new Promise((resolve) => {
|
||||||
const expectedResult = {};
|
const expectedResult = {};
|
||||||
expectedResult[customRulesMd] = [
|
expectedResult[customRulesMd] = [
|
||||||
{ "lineNumber": 12,
|
{ "lineNumber": 12,
|
||||||
"ruleNames": [ "any-blockquote" ],
|
"ruleNames": [ "any-blockquote-markdown-it" ],
|
||||||
"ruleDescription": "Rule that reports an error for any blockquote",
|
"ruleDescription": "Rule that reports an error for any blockquote",
|
||||||
"ruleInformation":
|
"ruleInformation":
|
||||||
`${homepage}/blob/main/test/rules/any-blockquote.js`,
|
`${homepage}/blob/main/test/rules/any-blockquote.js`,
|
||||||
"errorDetail": "Blockquote spans 1 line(s).",
|
"errorDetail": "Blockquote spans 1 line(s).",
|
||||||
"errorContext": "> Block",
|
"errorContext": "> Blockquote",
|
||||||
|
"errorRange": null },
|
||||||
|
{ "lineNumber": 12,
|
||||||
|
"ruleNames": [ "any-blockquote-micromark" ],
|
||||||
|
"ruleDescription": "Rule that reports an error for any blockquote",
|
||||||
|
"ruleInformation":
|
||||||
|
`${homepage}/blob/main/test/rules/any-blockquote.js`,
|
||||||
|
"errorDetail": "Blockquote spans 1 line(s).",
|
||||||
|
"errorContext": "> Blockquote",
|
||||||
"errorRange": null },
|
"errorRange": null },
|
||||||
{ "lineNumber": 2,
|
{ "lineNumber": 2,
|
||||||
"ruleNames": [ "every-n-lines" ],
|
"ruleNames": [ "every-n-lines" ],
|
||||||
|
@ -281,9 +307,12 @@ test("customRulesV2", (t) => new Promise((resolve) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const actualMessage = actualResult.toString();
|
const actualMessage = actualResult.toString();
|
||||||
const expectedMessage =
|
const expectedMessage =
|
||||||
"./test/custom-rules.md: 12: any-blockquote" +
|
"./test/custom-rules.md: 12: any-blockquote-markdown-it" +
|
||||||
" Rule that reports an error for any blockquote" +
|
" Rule that reports an error for any blockquote" +
|
||||||
" [Blockquote spans 1 line(s).] [Context: \"> Block\"]\n" +
|
" [Blockquote spans 1 line(s).] [Context: \"> Blockquote\"]\n" +
|
||||||
|
"./test/custom-rules.md: 12: any-blockquote-micromark" +
|
||||||
|
" Rule that reports an error for any blockquote" +
|
||||||
|
" [Blockquote spans 1 line(s).] [Context: \"> Blockquote\"]\n" +
|
||||||
"./test/custom-rules.md: 2: every-n-lines" +
|
"./test/custom-rules.md: 2: every-n-lines" +
|
||||||
" Rule that reports an error every N lines [Line number 2]\n" +
|
" Rule that reports an error every N lines [Line number 2]\n" +
|
||||||
"./test/custom-rules.md: 4: every-n-lines" +
|
"./test/custom-rules.md: 4: every-n-lines" +
|
||||||
|
@ -328,7 +357,8 @@ test("customRulesConfig", (t) => new Promise((resolve) => {
|
||||||
t.falsy(err);
|
t.falsy(err);
|
||||||
const expectedResult = {};
|
const expectedResult = {};
|
||||||
expectedResult[customRulesMd] = {
|
expectedResult[customRulesMd] = {
|
||||||
"any-blockquote": [ 12 ],
|
"any-blockquote-markdown-it": [ 12 ],
|
||||||
|
"any-blockquote-micromark": [ 12 ],
|
||||||
"every-n-lines": [ 3, 6, 12 ],
|
"every-n-lines": [ 3, 6, 12 ],
|
||||||
"first-line": [ 1 ],
|
"first-line": [ 1 ],
|
||||||
"letters-E-X": [ 7 ]
|
"letters-E-X": [ 7 ]
|
||||||
|
@ -402,7 +432,7 @@ test("customRulesBadProperty", (t) => {
|
||||||
]) {
|
]) {
|
||||||
const { propertyName, propertyValues } = testCase;
|
const { propertyName, propertyValues } = testCase;
|
||||||
for (const propertyValue of propertyValues) {
|
for (const propertyValue of propertyValues) {
|
||||||
const badRule = { ...customRules.anyBlockquote };
|
const badRule = { ...customRules.firstLine };
|
||||||
badRule[propertyName] = propertyValue;
|
badRule[propertyName] = propertyValue;
|
||||||
// eslint-disable-next-line jsdoc/valid-types
|
// eslint-disable-next-line jsdoc/valid-types
|
||||||
/** @type import("../lib/markdownlint").Options */
|
/** @type import("../lib/markdownlint").Options */
|
||||||
|
@ -592,7 +622,7 @@ test("customRulesParserMarkdownIt", (t) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("customRulesParserMicromark", (t) => {
|
test("customRulesParserMicromark", (t) => {
|
||||||
t.plan(1);
|
t.plan(5);
|
||||||
// eslint-disable-next-line jsdoc/valid-types
|
// eslint-disable-next-line jsdoc/valid-types
|
||||||
/** @type import("../lib/markdownlint").Options */
|
/** @type import("../lib/markdownlint").Options */
|
||||||
const options = {
|
const options = {
|
||||||
|
@ -603,12 +633,12 @@ test("customRulesParserMicromark", (t) => {
|
||||||
"tags": [ "tag" ],
|
"tags": [ "tag" ],
|
||||||
"parser": "micromark",
|
"parser": "micromark",
|
||||||
"function":
|
"function":
|
||||||
() => {
|
(params) => {
|
||||||
// t.false(Object.keys(params).includes("tokens"));
|
t.false(Object.keys(params).includes("tokens"));
|
||||||
// t.is(Object.keys(params.parsers).length, 1);
|
t.is(Object.keys(params.parsers).length, 1);
|
||||||
// t.truthy(params.parsers.micromark);
|
t.truthy(params.parsers.micromark);
|
||||||
// t.is(Object.keys(params.parsers.micromark).length, 1);
|
t.is(Object.keys(params.parsers.micromark).length, 1);
|
||||||
// t.truthy(params.parsers.micromark.tokens);
|
t.truthy(params.parsers.micromark.tokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -616,10 +646,7 @@ test("customRulesParserMicromark", (t) => {
|
||||||
"string": "# Heading\n"
|
"string": "# Heading\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return markdownlint.promises.markdownlint(options).catch((error) => {
|
return markdownlint.promises.markdownlint(options).then(() => null);
|
||||||
// parser "micromark" currently unsupported for custom rules
|
|
||||||
t.is(error.message, "Property 'parser' of custom rule at index 0 is incorrect: 'micromark'.");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("customRulesMarkdownItParamsTokensSameObject", (t) => {
|
test("customRulesMarkdownItParamsTokensSameObject", (t) => {
|
||||||
|
@ -664,10 +691,41 @@ test("customRulesMarkdownItTokensSnapshot", (t) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"files": "./test/every-markdown-syntax.md",
|
|
||||||
"noInlineConfig": true
|
"noInlineConfig": true
|
||||||
};
|
};
|
||||||
return markdownlint.promises.markdownlint(options).then(() => null);
|
return fs
|
||||||
|
.readFile("./test/every-markdown-syntax.md", "utf8")
|
||||||
|
.then((content) => {
|
||||||
|
options.strings = { "content": content.split(newLineRe).join("\n") };
|
||||||
|
return markdownlint.promises.markdownlint(options).then(() => null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("customRulesMicromarkTokensSnapshot", (t) => {
|
||||||
|
t.plan(1);
|
||||||
|
// eslint-disable-next-line jsdoc/valid-types
|
||||||
|
/** @type import("../lib/markdownlint").Options */
|
||||||
|
const options = {
|
||||||
|
"customRules": [
|
||||||
|
{
|
||||||
|
"names": [ "name" ],
|
||||||
|
"description": "description",
|
||||||
|
"tags": [ "tag" ],
|
||||||
|
"parser": "micromark",
|
||||||
|
"function":
|
||||||
|
(params) => {
|
||||||
|
t.snapshot(params.parsers.micromark.tokens, "Unexpected tokens");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"noInlineConfig": true
|
||||||
|
};
|
||||||
|
return fs
|
||||||
|
.readFile("./test/every-markdown-syntax.md", "utf8")
|
||||||
|
.then((content) => {
|
||||||
|
options.strings = { "content": content.split(newLineRe).join("\n") };
|
||||||
|
return markdownlint.promises.markdownlint(options).then(() => null);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("customRulesDefinitionStatic", (t) => new Promise((resolve) => {
|
test("customRulesDefinitionStatic", (t) => new Promise((resolve) => {
|
||||||
|
|
|
@ -17,26 +17,9 @@ const testTokens = new Promise((resolve, reject) => {
|
||||||
testContent.then(parse).then(resolve, reject);
|
testContent.then(parse).then(resolve, reject);
|
||||||
});
|
});
|
||||||
|
|
||||||
const cloneToken = (token) => {
|
|
||||||
for (const child of token.children) {
|
|
||||||
const expectedParent = (token.type ? token : null);
|
|
||||||
if (child.parent !== expectedParent) {
|
|
||||||
throw new Error("Unexpected parent.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const clone = { ...token };
|
|
||||||
delete clone.parent;
|
|
||||||
clone.children = clone.children.map(cloneToken);
|
|
||||||
return clone;
|
|
||||||
};
|
|
||||||
|
|
||||||
const cloneTokens = (tokens) => (
|
|
||||||
cloneToken({ "children": tokens }).children
|
|
||||||
);
|
|
||||||
|
|
||||||
test("parse", async(t) => {
|
test("parse", async(t) => {
|
||||||
t.plan(1);
|
t.plan(1);
|
||||||
t.snapshot(cloneTokens(await testTokens), "Unexpected tokens");
|
t.snapshot(await testTokens, "Unexpected tokens");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("getEvents/filterByPredicate", async(t) => {
|
test("getEvents/filterByPredicate", async(t) => {
|
||||||
|
|
|
@ -2,26 +2,54 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
/** @type import("../../lib/markdownlint").Rule */
|
/** @type import("../../lib/markdownlint").Rule[] */
|
||||||
module.exports = {
|
module.exports = [
|
||||||
"names": [ "any-blockquote" ],
|
|
||||||
"description": "Rule that reports an error for any blockquote",
|
// micromark parser (preferred)
|
||||||
"information": new URL(
|
{
|
||||||
"https://github.com/DavidAnson/markdownlint" +
|
"names": [ "any-blockquote-micromark" ],
|
||||||
"/blob/main/test/rules/any-blockquote.js"
|
"description": "Rule that reports an error for any blockquote",
|
||||||
),
|
"information": new URL(
|
||||||
"tags": [ "test" ],
|
"https://github.com/DavidAnson/markdownlint/blob/main/test/rules/any-blockquote.js"
|
||||||
"parser": "markdownit",
|
),
|
||||||
"function": (params, onError) => {
|
"tags": [ "test" ],
|
||||||
const blockquotes = params.parsers.markdownit.tokens
|
"parser": "micromark",
|
||||||
.filter(((token) => token.type === "blockquote_open"));
|
"function": (params, onError) => {
|
||||||
for (const blockquote of blockquotes) {
|
const blockquotes = params.parsers.micromark.tokens
|
||||||
const lines = blockquote.map[1] - blockquote.map[0];
|
.filter(((token) => token.type === "blockQuote"));
|
||||||
onError({
|
for (const blockquote of blockquotes) {
|
||||||
"lineNumber": blockquote.lineNumber,
|
const lines = blockquote.endLine - blockquote.startLine + 1;
|
||||||
"detail": "Blockquote spans " + lines + " line(s).",
|
onError({
|
||||||
"context": blockquote.line.substr(0, 7)
|
"lineNumber": blockquote.startLine,
|
||||||
});
|
"detail": "Blockquote spans " + lines + " line(s).",
|
||||||
|
"context": params.lines[blockquote.startLine - 1]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// markdown-it parser (legacy)
|
||||||
|
{
|
||||||
|
"names": [ "any-blockquote-markdown-it" ],
|
||||||
|
"description": "Rule that reports an error for any blockquote",
|
||||||
|
"information": new URL(
|
||||||
|
"https://github.com/DavidAnson/markdownlint/blob/main/test/rules/any-blockquote.js"
|
||||||
|
),
|
||||||
|
"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
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
];
|
||||||
|
|
|
@ -21,7 +21,7 @@ const validateJson = require("./validate-json");
|
||||||
module.exports.validateJson = validateJson;
|
module.exports.validateJson = validateJson;
|
||||||
|
|
||||||
module.exports.all = [
|
module.exports.all = [
|
||||||
anyBlockquote,
|
...anyBlockquote,
|
||||||
everyNLines,
|
everyNLines,
|
||||||
firstLine,
|
firstLine,
|
||||||
lettersEX,
|
lettersEX,
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue