markdownlint 0.33.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/CONTRIBUTING.md +4 -3
- package/README.md +25 -10
- package/demo/markdownlint-browser.js +3286 -2119
- package/doc/CustomRules.md +25 -13
- package/doc/Rules.md +106 -7
- package/doc/md013.md +8 -7
- package/doc/md034.md +5 -0
- package/doc/md044.md +10 -0
- package/doc/md049.md +3 -0
- package/doc/md050.md +3 -0
- package/doc/md051.md +26 -0
- package/doc/md058.md +48 -0
- package/helpers/README.md +3 -27
- package/helpers/helpers.js +60 -292
- package/helpers/micromark.cjs +153 -36
- package/helpers/package.json +2 -2
- package/lib/cache.js +62 -13
- package/lib/configuration-strict.d.ts +1185 -0
- package/lib/configuration.d.ts +3 -1173
- package/lib/constants.js +3 -2
- package/lib/markdownlint.d.ts +143 -61
- package/lib/markdownlint.js +151 -54
- package/lib/md001.js +17 -8
- package/lib/md003.js +16 -8
- package/lib/md004.js +57 -47
- package/lib/md005.js +7 -8
- package/lib/md007.js +13 -12
- package/lib/md009.js +52 -43
- package/lib/md010.js +50 -32
- package/lib/md011.js +18 -7
- package/lib/md012.js +19 -8
- package/lib/md013.js +46 -37
- package/lib/md014.js +37 -36
- package/lib/md018.js +20 -8
- package/lib/md019-md021.js +80 -0
- package/lib/md020.js +15 -6
- package/lib/md022.js +10 -12
- package/lib/md023.js +24 -22
- package/lib/md024.js +14 -8
- package/lib/md025.js +17 -9
- package/lib/md026.js +5 -6
- package/lib/md027.js +24 -43
- package/lib/md028.js +26 -14
- package/lib/md029.js +42 -33
- package/lib/md030.js +7 -8
- package/lib/md031.js +45 -22
- package/lib/md032.js +18 -30
- package/lib/md033.js +6 -4
- package/lib/md034.js +30 -9
- package/lib/md035.js +5 -3
- package/lib/md036.js +34 -39
- package/lib/md037.js +3 -0
- package/lib/md038.js +6 -5
- package/lib/md039.js +68 -52
- package/lib/md040.js +21 -21
- package/lib/md041.js +21 -26
- package/lib/md042.js +50 -36
- package/lib/md043.js +22 -13
- package/lib/md044.js +5 -2
- package/lib/md045.js +6 -4
- package/lib/md046.js +9 -8
- package/lib/md047.js +5 -2
- package/lib/md048.js +22 -13
- package/lib/md049-md050.js +19 -5
- package/lib/md051.js +18 -20
- package/lib/md052.js +5 -2
- package/lib/md053.js +5 -2
- package/lib/md054.js +12 -12
- package/lib/md055.js +10 -3
- package/lib/md056.js +18 -6
- package/lib/md058.js +43 -0
- package/lib/rules.js +11 -4
- package/package.json +22 -25
- package/schema/.markdownlint.jsonc +54 -51
- package/schema/.markdownlint.yaml +53 -50
- package/schema/ValidatingConfiguration.md +6 -8
- package/schema/markdownlint-config-schema-strict.json +1819 -0
- package/schema/markdownlint-config-schema.json +115 -105
- package/style/prettier.json +1 -0
- package/lib/md019.js +0 -39
- package/lib/md021.js +0 -62
package/lib/md001.js
CHANGED
|
@@ -2,21 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorDetailIf
|
|
5
|
+
const { addErrorDetailIf } = require("../helpers");
|
|
6
|
+
const { getHeadingLevel } = require("../helpers/micromark.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
6
8
|
|
|
9
|
+
// eslint-disable-next-line jsdoc/valid-types
|
|
10
|
+
/** @type import("./markdownlint").Rule */
|
|
7
11
|
module.exports = {
|
|
8
12
|
"names": [ "MD001", "heading-increment" ],
|
|
9
13
|
"description": "Heading levels should only increment by one level at a time",
|
|
10
14
|
"tags": [ "headings" ],
|
|
15
|
+
"parser": "micromark",
|
|
11
16
|
"function": function MD001(params, onError) {
|
|
12
|
-
let prevLevel =
|
|
13
|
-
|
|
14
|
-
const level =
|
|
15
|
-
if (
|
|
16
|
-
addErrorDetailIf(
|
|
17
|
-
|
|
17
|
+
let prevLevel = Number.MAX_SAFE_INTEGER;
|
|
18
|
+
for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
|
|
19
|
+
const level = getHeadingLevel(heading);
|
|
20
|
+
if (level > prevLevel) {
|
|
21
|
+
addErrorDetailIf(
|
|
22
|
+
onError,
|
|
23
|
+
heading.startLine,
|
|
24
|
+
`h${prevLevel + 1}`,
|
|
25
|
+
`h${level}`
|
|
26
|
+
);
|
|
18
27
|
}
|
|
19
28
|
prevLevel = level;
|
|
20
|
-
}
|
|
29
|
+
}
|
|
21
30
|
}
|
|
22
31
|
};
|
package/lib/md003.js
CHANGED
|
@@ -2,22 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorDetailIf
|
|
6
|
-
|
|
5
|
+
const { addErrorDetailIf } = require("../helpers");
|
|
6
|
+
const { getHeadingLevel, getHeadingStyle } = require("../helpers/micromark.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
7
8
|
|
|
9
|
+
// eslint-disable-next-line jsdoc/valid-types
|
|
10
|
+
/** @type import("./markdownlint").Rule */
|
|
8
11
|
module.exports = {
|
|
9
12
|
"names": [ "MD003", "heading-style" ],
|
|
10
13
|
"description": "Heading style",
|
|
11
14
|
"tags": [ "headings" ],
|
|
15
|
+
"parser": "micromark",
|
|
12
16
|
"function": function MD003(params, onError) {
|
|
13
17
|
let style = String(params.config.style || "consistent");
|
|
14
|
-
|
|
15
|
-
const styleForToken =
|
|
18
|
+
for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
|
|
19
|
+
const styleForToken = getHeadingStyle(heading);
|
|
16
20
|
if (style === "consistent") {
|
|
17
21
|
style = styleForToken;
|
|
18
22
|
}
|
|
19
23
|
if (styleForToken !== style) {
|
|
20
|
-
const h12 =
|
|
24
|
+
const h12 = getHeadingLevel(heading) <= 2;
|
|
21
25
|
const setextWithAtx =
|
|
22
26
|
(style === "setext_with_atx") &&
|
|
23
27
|
((h12 && (styleForToken === "setext")) ||
|
|
@@ -33,10 +37,14 @@ module.exports = {
|
|
|
33
37
|
} else if (style === "setext_with_atx_closed") {
|
|
34
38
|
expected = h12 ? "setext" : "atx_closed";
|
|
35
39
|
}
|
|
36
|
-
addErrorDetailIf(
|
|
37
|
-
|
|
40
|
+
addErrorDetailIf(
|
|
41
|
+
onError,
|
|
42
|
+
heading.startLine,
|
|
43
|
+
expected,
|
|
44
|
+
styleForToken
|
|
45
|
+
);
|
|
38
46
|
}
|
|
39
47
|
}
|
|
40
|
-
}
|
|
48
|
+
}
|
|
41
49
|
}
|
|
42
50
|
};
|
package/lib/md004.js
CHANGED
|
@@ -2,11 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorDetailIf
|
|
6
|
-
|
|
7
|
-
const {
|
|
5
|
+
const { addErrorDetailIf } = require("../helpers");
|
|
6
|
+
const { getDescendantsByType, getTokenParentOfType } = require("../helpers/micromark.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const markerToStyle = {
|
|
10
|
+
"-": "dash",
|
|
11
|
+
"+": "plus",
|
|
12
|
+
"*": "asterisk"
|
|
13
|
+
};
|
|
14
|
+
const styleToMarker = {
|
|
10
15
|
"dash": "-",
|
|
11
16
|
"plus": "+",
|
|
12
17
|
"asterisk": "*"
|
|
@@ -16,60 +21,65 @@ const differentItemStyle = {
|
|
|
16
21
|
"plus": "asterisk",
|
|
17
22
|
"asterisk": "dash"
|
|
18
23
|
};
|
|
19
|
-
const validStyles =
|
|
24
|
+
const validStyles = new Set([
|
|
25
|
+
"asterisk",
|
|
26
|
+
"consistent",
|
|
27
|
+
"dash",
|
|
28
|
+
"plus",
|
|
29
|
+
"sublist"
|
|
30
|
+
]);
|
|
20
31
|
|
|
32
|
+
// eslint-disable-next-line jsdoc/valid-types
|
|
33
|
+
/** @type import("./markdownlint").Rule */
|
|
21
34
|
module.exports = {
|
|
22
35
|
"names": [ "MD004", "ul-style" ],
|
|
23
36
|
"description": "Unordered list style",
|
|
24
37
|
"tags": [ "bullet", "ul" ],
|
|
38
|
+
"parser": "micromark",
|
|
25
39
|
"function": function MD004(params, onError) {
|
|
26
40
|
const style = String(params.config.style || "consistent");
|
|
27
|
-
let expectedStyle = style;
|
|
41
|
+
let expectedStyle = validStyles.has(style) ? style : "dash";
|
|
28
42
|
const nestingStyles = [];
|
|
29
|
-
for (const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
43
|
+
for (const listUnordered of filterByTypesCached([ "listUnordered" ])) {
|
|
44
|
+
let nesting = 0;
|
|
45
|
+
if (style === "sublist") {
|
|
46
|
+
/** @type {import("../helpers/micromark.cjs").Token | null} */
|
|
47
|
+
let parent = listUnordered;
|
|
48
|
+
// @ts-ignore
|
|
49
|
+
while ((parent = getTokenParentOfType(parent, [ "listOrdered", "listUnordered" ]))) {
|
|
50
|
+
nesting++;
|
|
33
51
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
expectedStyle = nestingStyles[nesting];
|
|
45
|
-
}
|
|
46
|
-
if (!validStyles.includes(expectedStyle)) {
|
|
47
|
-
expectedStyle = validStyles[0];
|
|
48
|
-
}
|
|
49
|
-
let range = null;
|
|
50
|
-
let fixInfo = null;
|
|
51
|
-
const match = item.line.match(listItemMarkerRe);
|
|
52
|
-
if (match) {
|
|
53
|
-
const column = match.index + 1;
|
|
54
|
-
const length = match[0].length;
|
|
55
|
-
range = [ column, length ];
|
|
56
|
-
fixInfo = {
|
|
57
|
-
"editColumn": match[1].length + 1,
|
|
58
|
-
"deleteCount": 1,
|
|
59
|
-
"insertText": expectedStyleToMarker[expectedStyle]
|
|
60
|
-
};
|
|
52
|
+
}
|
|
53
|
+
const listItemMarkers = getDescendantsByType(listUnordered, [ "listItemPrefix", "listItemMarker" ]);
|
|
54
|
+
for (const listItemMarker of listItemMarkers) {
|
|
55
|
+
const itemStyle = markerToStyle[listItemMarker.text];
|
|
56
|
+
if (style === "sublist") {
|
|
57
|
+
if (!nestingStyles[nesting]) {
|
|
58
|
+
nestingStyles[nesting] =
|
|
59
|
+
(itemStyle === nestingStyles[nesting - 1]) ?
|
|
60
|
+
differentItemStyle[itemStyle] :
|
|
61
|
+
itemStyle;
|
|
61
62
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
expectedStyle,
|
|
66
|
-
itemStyle,
|
|
67
|
-
null,
|
|
68
|
-
null,
|
|
69
|
-
range,
|
|
70
|
-
fixInfo
|
|
71
|
-
);
|
|
63
|
+
expectedStyle = nestingStyles[nesting];
|
|
64
|
+
} else if (expectedStyle === "consistent") {
|
|
65
|
+
expectedStyle = itemStyle;
|
|
72
66
|
}
|
|
67
|
+
const column = listItemMarker.startColumn;
|
|
68
|
+
const length = listItemMarker.endColumn - listItemMarker.startColumn;
|
|
69
|
+
addErrorDetailIf(
|
|
70
|
+
onError,
|
|
71
|
+
listItemMarker.startLine,
|
|
72
|
+
expectedStyle,
|
|
73
|
+
itemStyle,
|
|
74
|
+
undefined,
|
|
75
|
+
undefined,
|
|
76
|
+
[ column, length ],
|
|
77
|
+
{
|
|
78
|
+
"editColumn": column,
|
|
79
|
+
"deleteCount": length,
|
|
80
|
+
"insertText": styleToMarker[expectedStyle]
|
|
81
|
+
}
|
|
82
|
+
);
|
|
73
83
|
}
|
|
74
84
|
}
|
|
75
85
|
}
|
package/lib/md005.js
CHANGED
|
@@ -3,18 +3,17 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
5
|
const { addError, addErrorDetailIf } = require("../helpers");
|
|
6
|
-
const {
|
|
6
|
+
const { filterByTypesCached } = require("./cache");
|
|
7
7
|
|
|
8
|
+
// eslint-disable-next-line jsdoc/valid-types
|
|
9
|
+
/** @type import("./markdownlint").Rule */
|
|
8
10
|
module.exports = {
|
|
9
11
|
"names": [ "MD005", "list-indent" ],
|
|
10
12
|
"description": "Inconsistent indentation for list items at the same level",
|
|
11
13
|
"tags": [ "bullet", "ul", "indentation" ],
|
|
14
|
+
"parser": "micromark",
|
|
12
15
|
"function": function MD005(params, onError) {
|
|
13
|
-
const
|
|
14
|
-
params.parsers.micromark.tokens,
|
|
15
|
-
[ "listOrdered", "listUnordered" ]
|
|
16
|
-
).filter((list) => !inHtmlFlow(list));
|
|
17
|
-
for (const list of lists) {
|
|
16
|
+
for (const list of filterByTypesCached([ "listOrdered", "listUnordered" ])) {
|
|
18
17
|
const expectedIndent = list.startColumn - 1;
|
|
19
18
|
let expectedEnd = 0;
|
|
20
19
|
let endMatching = false;
|
|
@@ -30,8 +29,8 @@ module.exports = {
|
|
|
30
29
|
lineNumber,
|
|
31
30
|
expectedIndent,
|
|
32
31
|
actualIndent,
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
undefined,
|
|
33
|
+
undefined,
|
|
35
34
|
range
|
|
36
35
|
// No fixInfo; MD007 handles this scenario better
|
|
37
36
|
);
|
package/lib/md007.js
CHANGED
|
@@ -3,41 +3,42 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
5
|
const { addErrorDetailIf } = require("../helpers");
|
|
6
|
-
const {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @typedef {import("../helpers/micromark.cjs").Token} Token
|
|
11
|
-
*/
|
|
6
|
+
const { getTokenParentOfType } = require("../helpers/micromark.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
12
8
|
|
|
9
|
+
// eslint-disable-next-line jsdoc/valid-types
|
|
10
|
+
/** @type import("markdownlint-micromark").TokenType[] */
|
|
13
11
|
const unorderedListTypes =
|
|
14
12
|
[ "blockQuotePrefix", "listItemPrefix", "listUnordered" ];
|
|
13
|
+
// eslint-disable-next-line jsdoc/valid-types
|
|
14
|
+
/** @type import("markdownlint-micromark").TokenType[] */
|
|
15
15
|
const unorderedParentTypes =
|
|
16
16
|
[ "blockQuote", "listOrdered", "listUnordered" ];
|
|
17
17
|
|
|
18
|
+
// eslint-disable-next-line jsdoc/valid-types
|
|
19
|
+
/** @type import("./markdownlint").Rule */
|
|
18
20
|
module.exports = {
|
|
19
21
|
"names": [ "MD007", "ul-indent" ],
|
|
20
22
|
"description": "Unordered list indentation",
|
|
21
23
|
"tags": [ "bullet", "ul", "indentation" ],
|
|
24
|
+
"parser": "micromark",
|
|
22
25
|
"function": function MD007(params, onError) {
|
|
23
26
|
const indent = Number(params.config.indent || 2);
|
|
24
27
|
const startIndented = !!params.config.start_indented;
|
|
25
28
|
const startIndent = Number(params.config.start_indent || indent);
|
|
26
29
|
const unorderedListNesting = new Map();
|
|
27
30
|
let lastBlockQuotePrefix = null;
|
|
28
|
-
const tokens =
|
|
29
|
-
params.parsers.micromark.tokens,
|
|
30
|
-
unorderedListTypes
|
|
31
|
-
);
|
|
31
|
+
const tokens = filterByTypesCached(unorderedListTypes);
|
|
32
32
|
for (const token of tokens) {
|
|
33
33
|
const { endColumn, parent, startColumn, startLine, type } = token;
|
|
34
34
|
if (type === "blockQuotePrefix") {
|
|
35
35
|
lastBlockQuotePrefix = token;
|
|
36
36
|
} else if (type === "listUnordered") {
|
|
37
37
|
let nesting = 0;
|
|
38
|
-
/** @type {Token | null} */
|
|
38
|
+
/** @type {import("../helpers/micromark.cjs").Token | null} */
|
|
39
39
|
let current = token;
|
|
40
40
|
while (
|
|
41
|
+
// @ts-ignore
|
|
41
42
|
(current = getTokenParentOfType(current, unorderedParentTypes))
|
|
42
43
|
) {
|
|
43
44
|
if (current.type === "listUnordered") {
|
|
@@ -52,7 +53,7 @@ module.exports = {
|
|
|
52
53
|
if (nesting >= 0) {
|
|
53
54
|
unorderedListNesting.set(token, nesting);
|
|
54
55
|
}
|
|
55
|
-
} else
|
|
56
|
+
} else {
|
|
56
57
|
// listItemPrefix
|
|
57
58
|
const nesting = unorderedListNesting.get(parent);
|
|
58
59
|
if (nesting !== undefined) {
|
package/lib/md009.js
CHANGED
|
@@ -2,70 +2,78 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addError
|
|
6
|
-
|
|
7
|
-
const {
|
|
5
|
+
const { addError } = require("../helpers");
|
|
6
|
+
const { addRangeToSet } = require("../helpers/micromark.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
8
8
|
|
|
9
|
+
// eslint-disable-next-line jsdoc/valid-types
|
|
10
|
+
/** @type import("./markdownlint").Rule */
|
|
9
11
|
module.exports = {
|
|
10
12
|
"names": [ "MD009", "no-trailing-spaces" ],
|
|
11
13
|
"description": "Trailing spaces",
|
|
12
14
|
"tags": [ "whitespace" ],
|
|
15
|
+
"parser": "micromark",
|
|
13
16
|
"function": function MD009(params, onError) {
|
|
14
17
|
let brSpaces = params.config.br_spaces;
|
|
15
18
|
brSpaces = Number((brSpaces === undefined) ? 2 : brSpaces);
|
|
16
19
|
const listItemEmptyLines = !!params.config.list_item_empty_lines;
|
|
17
20
|
const strict = !!params.config.strict;
|
|
18
|
-
const
|
|
21
|
+
const codeBlockLineNumbers = new Set();
|
|
22
|
+
for (const codeBlock of filterByTypesCached([ "codeFenced" ])) {
|
|
23
|
+
addRangeToSet(codeBlockLineNumbers, codeBlock.startLine + 1, codeBlock.endLine - 1);
|
|
24
|
+
}
|
|
25
|
+
for (const codeBlock of filterByTypesCached([ "codeIndented" ])) {
|
|
26
|
+
addRangeToSet(codeBlockLineNumbers, codeBlock.startLine, codeBlock.endLine);
|
|
27
|
+
}
|
|
28
|
+
const listItemLineNumbers = new Set();
|
|
19
29
|
if (listItemEmptyLines) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
30
|
+
for (const listBlock of filterByTypesCached([ "listOrdered", "listUnordered" ])) {
|
|
31
|
+
addRangeToSet(listItemLineNumbers, listBlock.startLine, listBlock.endLine);
|
|
32
|
+
let trailingIndent = true;
|
|
33
|
+
for (let i = listBlock.children.length - 1; i >= 0; i--) {
|
|
34
|
+
const child = listBlock.children[i];
|
|
35
|
+
switch (child.type) {
|
|
36
|
+
case "content":
|
|
37
|
+
trailingIndent = false;
|
|
38
|
+
break;
|
|
39
|
+
case "listItemIndent":
|
|
40
|
+
if (trailingIndent) {
|
|
41
|
+
listItemLineNumbers.delete(child.startLine);
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
44
|
+
case "listItemPrefix":
|
|
45
|
+
trailingIndent = true;
|
|
46
|
+
break;
|
|
47
|
+
default:
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
23
50
|
}
|
|
24
|
-
}
|
|
25
|
-
listItemLineNumbers.sort(numericSortAscending);
|
|
51
|
+
}
|
|
26
52
|
}
|
|
27
|
-
const paragraphLineNumbers =
|
|
28
|
-
const codeInlineLineNumbers =
|
|
53
|
+
const paragraphLineNumbers = new Set();
|
|
54
|
+
const codeInlineLineNumbers = new Set();
|
|
29
55
|
if (strict) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
for (let i = start; i < end; i++) {
|
|
37
|
-
codeInlineLineNumbers.push(i);
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
filterTokens(params, "inline", (token) => {
|
|
41
|
-
let start = 0;
|
|
42
|
-
for (const child of token.children) {
|
|
43
|
-
if (start > 0) {
|
|
44
|
-
addLineNumberRange(start, child.lineNumber);
|
|
45
|
-
start = 0;
|
|
46
|
-
}
|
|
47
|
-
if (child.type === "code_inline") {
|
|
48
|
-
start = child.lineNumber;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
if (start > 0) {
|
|
52
|
-
addLineNumberRange(start, token.map[1]);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
56
|
+
for (const paragraph of filterByTypesCached([ "paragraph" ])) {
|
|
57
|
+
addRangeToSet(paragraphLineNumbers, paragraph.startLine, paragraph.endLine - 1);
|
|
58
|
+
}
|
|
59
|
+
for (const codeText of filterByTypesCached([ "codeText" ])) {
|
|
60
|
+
addRangeToSet(codeInlineLineNumbers, codeText.startLine, codeText.endLine - 1);
|
|
61
|
+
}
|
|
55
62
|
}
|
|
56
63
|
const expected = (brSpaces < 2) ? 0 : brSpaces;
|
|
57
|
-
|
|
64
|
+
for (let lineIndex = 0; lineIndex < params.lines.length; lineIndex++) {
|
|
65
|
+
const line = params.lines[lineIndex];
|
|
58
66
|
const lineNumber = lineIndex + 1;
|
|
59
67
|
const trailingSpaces = line.length - line.trimEnd().length;
|
|
60
68
|
if (
|
|
61
69
|
trailingSpaces &&
|
|
62
|
-
!
|
|
63
|
-
!
|
|
70
|
+
!codeBlockLineNumbers.has(lineNumber) &&
|
|
71
|
+
!listItemLineNumbers.has(lineNumber) &&
|
|
64
72
|
(
|
|
65
73
|
(expected !== trailingSpaces) ||
|
|
66
74
|
(strict &&
|
|
67
|
-
(!
|
|
68
|
-
|
|
75
|
+
(!paragraphLineNumbers.has(lineNumber) ||
|
|
76
|
+
codeInlineLineNumbers.has(lineNumber)))
|
|
69
77
|
)
|
|
70
78
|
) {
|
|
71
79
|
const column = line.length - trailingSpaces + 1;
|
|
@@ -79,8 +87,9 @@ module.exports = {
|
|
|
79
87
|
{
|
|
80
88
|
"editColumn": column,
|
|
81
89
|
"deleteCount": trailingSpaces
|
|
82
|
-
}
|
|
90
|
+
}
|
|
91
|
+
);
|
|
83
92
|
}
|
|
84
|
-
}
|
|
93
|
+
}
|
|
85
94
|
}
|
|
86
95
|
};
|
package/lib/md010.js
CHANGED
|
@@ -2,16 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addError,
|
|
6
|
-
|
|
7
|
-
const {
|
|
5
|
+
const { addError, withinAnyRange } = require("../helpers");
|
|
6
|
+
const { getDescendantsByType, getExclusionsForToken } = require("../helpers/micromark.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
8
8
|
|
|
9
9
|
const tabRe = /\t+/g;
|
|
10
10
|
|
|
11
|
+
// eslint-disable-next-line jsdoc/valid-types
|
|
12
|
+
/** @type import("./markdownlint").Rule */
|
|
11
13
|
module.exports = {
|
|
12
14
|
"names": [ "MD010", "no-hard-tabs" ],
|
|
13
15
|
"description": "Hard tabs",
|
|
14
16
|
"tags": [ "whitespace", "hard_tab" ],
|
|
17
|
+
"parser": "micromark",
|
|
15
18
|
"function": function MD010(params, onError) {
|
|
16
19
|
const codeBlocks = params.config.code_blocks;
|
|
17
20
|
const includeCode = (codeBlocks === undefined) ? true : !!codeBlocks;
|
|
@@ -23,38 +26,53 @@ module.exports = {
|
|
|
23
26
|
const spaceMultiplier = (spacesPerTab === undefined) ?
|
|
24
27
|
1 :
|
|
25
28
|
Math.max(0, Number(spacesPerTab));
|
|
26
|
-
const exclusions =
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
const exclusions = [];
|
|
30
|
+
// eslint-disable-next-line jsdoc/valid-types
|
|
31
|
+
/** @type import("../helpers/micromark.cjs").TokenType[] */
|
|
32
|
+
const exclusionTypes = [];
|
|
33
|
+
if (includeCode) {
|
|
34
|
+
if (ignoreCodeLanguages.size > 0) {
|
|
35
|
+
exclusionTypes.push("codeFenced");
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
exclusionTypes.push("codeFenced", "codeIndented", "codeText");
|
|
39
|
+
}
|
|
40
|
+
const codeTokens = filterByTypesCached(exclusionTypes).filter((token) => {
|
|
41
|
+
if ((token.type === "codeFenced") && (ignoreCodeLanguages.size > 0)) {
|
|
42
|
+
const fenceInfos = getDescendantsByType(token, [ "codeFencedFence", "codeFencedFenceInfo" ]);
|
|
43
|
+
return fenceInfos.every((fenceInfo) => ignoreCodeLanguages.has(fenceInfo.text.toLowerCase()));
|
|
33
44
|
}
|
|
45
|
+
return true;
|
|
34
46
|
});
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
47
|
+
for (const codeToken of codeTokens) {
|
|
48
|
+
const exclusionsForToken = getExclusionsForToken(params.lines, codeToken);
|
|
49
|
+
if (codeToken.type === "codeFenced") {
|
|
50
|
+
exclusionsForToken.pop();
|
|
51
|
+
exclusionsForToken.shift();
|
|
52
|
+
}
|
|
53
|
+
exclusions.push(...exclusionsForToken);
|
|
54
|
+
}
|
|
55
|
+
for (let lineIndex = 0; lineIndex < params.lines.length; lineIndex++) {
|
|
56
|
+
const line = params.lines[lineIndex];
|
|
57
|
+
let match = null;
|
|
58
|
+
while ((match = tabRe.exec(line)) !== null) {
|
|
59
|
+
const column = match.index + 1;
|
|
60
|
+
const length = match[0].length;
|
|
61
|
+
if (!withinAnyRange(exclusions, lineIndex + 1, column, length)) {
|
|
62
|
+
addError(
|
|
63
|
+
onError,
|
|
64
|
+
lineIndex + 1,
|
|
65
|
+
"Column: " + column,
|
|
66
|
+
undefined,
|
|
67
|
+
[ column, length ],
|
|
68
|
+
{
|
|
69
|
+
"editColumn": column,
|
|
70
|
+
"deleteCount": length,
|
|
71
|
+
"insertText": "".padEnd(length * spaceMultiplier)
|
|
72
|
+
}
|
|
73
|
+
);
|
|
56
74
|
}
|
|
57
75
|
}
|
|
58
|
-
}
|
|
76
|
+
}
|
|
59
77
|
}
|
|
60
78
|
};
|
package/lib/md011.js
CHANGED
|
@@ -2,20 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addError,
|
|
6
|
-
const {
|
|
5
|
+
const { addError, withinAnyRange } = require("../helpers");
|
|
6
|
+
const { addRangeToSet, getExclusionsForToken } = require("../helpers/micromark.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
7
8
|
|
|
8
9
|
const reversedLinkRe =
|
|
9
10
|
/(^|[^\\])\(([^()]+)\)\[([^\]^][^\]]*)\](?!\()/g;
|
|
10
11
|
|
|
12
|
+
// eslint-disable-next-line jsdoc/valid-types
|
|
13
|
+
/** @type import("./markdownlint").Rule */
|
|
11
14
|
module.exports = {
|
|
12
15
|
"names": [ "MD011", "no-reversed-links" ],
|
|
13
16
|
"description": "Reversed link syntax",
|
|
14
17
|
"tags": [ "links" ],
|
|
18
|
+
"parser": "micromark",
|
|
15
19
|
"function": function MD011(params, onError) {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
const codeBlockLineNumbers = new Set();
|
|
21
|
+
for (const codeBlock of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
|
|
22
|
+
addRangeToSet(codeBlockLineNumbers, codeBlock.startLine, codeBlock.endLine);
|
|
23
|
+
}
|
|
24
|
+
const exclusions = [];
|
|
25
|
+
for (const codeText of filterByTypesCached([ "codeText" ])) {
|
|
26
|
+
exclusions.push(...getExclusionsForToken(params.lines, codeText));
|
|
27
|
+
}
|
|
28
|
+
for (const [ lineIndex, line ] of params.lines.entries()) {
|
|
29
|
+
if (!codeBlockLineNumbers.has(lineIndex + 1)) {
|
|
19
30
|
let match = null;
|
|
20
31
|
while ((match = reversedLinkRe.exec(line)) !== null) {
|
|
21
32
|
const [ reversedLink, preChar, linkText, linkDestination ] = match;
|
|
@@ -24,7 +35,7 @@ module.exports = {
|
|
|
24
35
|
if (
|
|
25
36
|
!linkText.endsWith("\\") &&
|
|
26
37
|
!linkDestination.endsWith("\\") &&
|
|
27
|
-
!withinAnyRange(exclusions, lineIndex, index, length)
|
|
38
|
+
!withinAnyRange(exclusions, lineIndex + 1, index, length)
|
|
28
39
|
) {
|
|
29
40
|
addError(
|
|
30
41
|
onError,
|
|
@@ -41,6 +52,6 @@ module.exports = {
|
|
|
41
52
|
}
|
|
42
53
|
}
|
|
43
54
|
}
|
|
44
|
-
}
|
|
55
|
+
}
|
|
45
56
|
}
|
|
46
57
|
};
|
package/lib/md012.js
CHANGED
|
@@ -2,17 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorDetailIf
|
|
6
|
-
const {
|
|
5
|
+
const { addErrorDetailIf } = require("../helpers");
|
|
6
|
+
const { addRangeToSet } = require("../helpers/micromark.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
7
8
|
|
|
9
|
+
// eslint-disable-next-line jsdoc/valid-types
|
|
10
|
+
/** @type import("./markdownlint").Rule */
|
|
8
11
|
module.exports = {
|
|
9
12
|
"names": [ "MD012", "no-multiple-blanks" ],
|
|
10
13
|
"description": "Multiple consecutive blank lines",
|
|
11
14
|
"tags": [ "whitespace", "blank_lines" ],
|
|
15
|
+
"parser": "micromark",
|
|
12
16
|
"function": function MD012(params, onError) {
|
|
13
17
|
const maximum = Number(params.config.maximum || 1);
|
|
18
|
+
const { lines } = params;
|
|
19
|
+
const codeBlockLineNumbers = new Set();
|
|
20
|
+
for (const codeBlock of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
|
|
21
|
+
addRangeToSet(codeBlockLineNumbers, codeBlock.startLine, codeBlock.endLine);
|
|
22
|
+
}
|
|
14
23
|
let count = 0;
|
|
15
|
-
|
|
24
|
+
for (const [ lineIndex, line ] of lines.entries()) {
|
|
25
|
+
const inCode = codeBlockLineNumbers.has(lineIndex + 1);
|
|
16
26
|
count = (inCode || (line.trim().length > 0)) ? 0 : count + 1;
|
|
17
27
|
if (maximum < count) {
|
|
18
28
|
addErrorDetailIf(
|
|
@@ -20,13 +30,14 @@ module.exports = {
|
|
|
20
30
|
lineIndex + 1,
|
|
21
31
|
maximum,
|
|
22
32
|
count,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
33
|
+
undefined,
|
|
34
|
+
undefined,
|
|
35
|
+
undefined,
|
|
26
36
|
{
|
|
27
37
|
"deleteCount": -1
|
|
28
|
-
}
|
|
38
|
+
}
|
|
39
|
+
);
|
|
29
40
|
}
|
|
30
|
-
}
|
|
41
|
+
}
|
|
31
42
|
}
|
|
32
43
|
};
|