markdownlint 0.34.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 +11 -0
- package/README.md +25 -10
- package/demo/markdownlint-browser.js +1144 -1166
- package/doc/CustomRules.md +11 -6
- package/doc/Rules.md +66 -0
- package/doc/md034.md +5 -0
- package/doc/md044.md +10 -0
- package/doc/md058.md +48 -0
- package/helpers/README.md +3 -29
- package/helpers/helpers.js +56 -288
- package/helpers/micromark.cjs +111 -12
- package/helpers/package.json +1 -1
- 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 +65 -56
- package/lib/markdownlint.js +59 -37
- package/lib/md001.js +15 -9
- package/lib/md003.js +14 -9
- package/lib/md004.js +55 -48
- package/lib/md005.js +4 -13
- package/lib/md007.js +5 -9
- package/lib/md009.js +50 -44
- package/lib/md010.js +48 -33
- package/lib/md011.js +16 -8
- package/lib/md012.js +17 -9
- package/lib/md013.js +44 -38
- package/lib/md014.js +35 -38
- package/lib/md018.js +17 -9
- package/lib/md019-md021.js +80 -0
- package/lib/md020.js +13 -7
- package/lib/md022.js +6 -16
- package/lib/md023.js +22 -23
- package/lib/md024.js +12 -9
- package/lib/md025.js +15 -10
- package/lib/md026.js +2 -7
- package/lib/md027.js +5 -10
- package/lib/md028.js +3 -8
- package/lib/md029.js +40 -34
- package/lib/md030.js +4 -10
- package/lib/md031.js +43 -23
- package/lib/md032.js +16 -26
- package/lib/md033.js +3 -8
- package/lib/md034.js +5 -10
- package/lib/md035.js +2 -7
- package/lib/md036.js +3 -2
- package/lib/md037.js +2 -7
- package/lib/md038.js +3 -9
- package/lib/md039.js +66 -54
- package/lib/md040.js +3 -8
- package/lib/md041.js +1 -1
- package/lib/md042.js +48 -38
- package/lib/md043.js +20 -14
- package/lib/md044.js +3 -8
- package/lib/md045.js +3 -8
- package/lib/md046.js +2 -8
- package/lib/md048.js +3 -7
- package/lib/md049-md050.js +2 -7
- package/lib/md051.js +6 -12
- package/lib/md052.js +2 -2
- package/lib/md053.js +2 -2
- package/lib/md054.js +8 -13
- package/lib/md055.js +7 -8
- package/lib/md056.js +15 -11
- package/lib/md058.js +43 -0
- package/lib/rules.js +11 -4
- package/package.json +19 -17
- 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 -42
- package/lib/md021.js +0 -65
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const { addErrorContext } = require("../helpers/helpers");
|
|
6
|
+
const { getHeadingStyle } = require("../helpers/micromark.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Validate heading sequence and whitespace length at start or end.
|
|
11
|
+
*
|
|
12
|
+
* @param {import("./markdownlint").RuleOnError} onError Error-reporting callback.
|
|
13
|
+
* @param {import("./markdownlint").MicromarkToken} heading ATX heading token.
|
|
14
|
+
* @param {number} delta Direction to scan.
|
|
15
|
+
* @returns {void}
|
|
16
|
+
*/
|
|
17
|
+
function validateHeadingSpaces(onError, heading, delta) {
|
|
18
|
+
const { children, startLine, text } = heading;
|
|
19
|
+
let index = (delta > 0) ? 0 : (children.length - 1);
|
|
20
|
+
while (
|
|
21
|
+
children[index] &&
|
|
22
|
+
(children[index].type !== "atxHeadingSequence")
|
|
23
|
+
) {
|
|
24
|
+
index += delta;
|
|
25
|
+
}
|
|
26
|
+
const headingSequence = children[index];
|
|
27
|
+
const whitespace = children[index + delta];
|
|
28
|
+
if (
|
|
29
|
+
(headingSequence?.type === "atxHeadingSequence") &&
|
|
30
|
+
(whitespace?.type === "whitespace") &&
|
|
31
|
+
(whitespace.text.length > 1)
|
|
32
|
+
) {
|
|
33
|
+
const column = whitespace.startColumn + 1;
|
|
34
|
+
const length = whitespace.endColumn - column;
|
|
35
|
+
addErrorContext(
|
|
36
|
+
onError,
|
|
37
|
+
startLine,
|
|
38
|
+
text.trim(),
|
|
39
|
+
delta > 0,
|
|
40
|
+
delta < 0,
|
|
41
|
+
[ column, length ],
|
|
42
|
+
{
|
|
43
|
+
"editColumn": column,
|
|
44
|
+
"deleteCount": length
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// eslint-disable-next-line jsdoc/valid-types
|
|
51
|
+
/** @type import("./markdownlint").Rule[] */
|
|
52
|
+
module.exports = [
|
|
53
|
+
{
|
|
54
|
+
"names": [ "MD019", "no-multiple-space-atx" ],
|
|
55
|
+
"description": "Multiple spaces after hash on atx style heading",
|
|
56
|
+
"tags": [ "headings", "atx", "spaces" ],
|
|
57
|
+
"parser": "micromark",
|
|
58
|
+
"function": function MD019(params, onError) {
|
|
59
|
+
const atxHeadings = filterByTypesCached([ "atxHeading" ])
|
|
60
|
+
.filter((heading) => getHeadingStyle(heading) === "atx");
|
|
61
|
+
for (const atxHeading of atxHeadings) {
|
|
62
|
+
validateHeadingSpaces(onError, atxHeading, 1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"names": [ "MD021", "no-multiple-space-closed-atx" ],
|
|
68
|
+
"description": "Multiple spaces inside hashes on closed atx style heading",
|
|
69
|
+
"tags": [ "headings", "atx_closed", "spaces" ],
|
|
70
|
+
"parser": "micromark",
|
|
71
|
+
"function": function MD021(params, onError) {
|
|
72
|
+
const atxClosedHeadings = filterByTypesCached([ "atxHeading" ])
|
|
73
|
+
.filter((heading) => getHeadingStyle(heading) === "atx_closed");
|
|
74
|
+
for (const atxClosedHeading of atxClosedHeadings) {
|
|
75
|
+
validateHeadingSpaces(onError, atxClosedHeading, 1);
|
|
76
|
+
validateHeadingSpaces(onError, atxClosedHeading, -1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
];
|
package/lib/md020.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorContext
|
|
6
|
-
const {
|
|
5
|
+
const { addErrorContext } = require("../helpers");
|
|
6
|
+
const { addRangeToSet } = require("../helpers/micromark.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
7
8
|
|
|
8
9
|
// eslint-disable-next-line jsdoc/valid-types
|
|
9
10
|
/** @type import("./markdownlint").Rule */
|
|
@@ -11,10 +12,15 @@ module.exports = {
|
|
|
11
12
|
"names": [ "MD020", "no-missing-space-closed-atx" ],
|
|
12
13
|
"description": "No space inside hashes on closed atx style heading",
|
|
13
14
|
"tags": [ "headings", "atx_closed", "spaces" ],
|
|
14
|
-
"parser": "
|
|
15
|
+
"parser": "micromark",
|
|
15
16
|
"function": function MD020(params, onError) {
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
const { lines } = params;
|
|
18
|
+
const ignoreBlockLineNumbers = new Set();
|
|
19
|
+
for (const ignoreBlock of filterByTypesCached([ "codeFenced", "codeIndented", "htmlFlow" ])) {
|
|
20
|
+
addRangeToSet(ignoreBlockLineNumbers, ignoreBlock.startLine, ignoreBlock.endLine);
|
|
21
|
+
}
|
|
22
|
+
for (const [ lineIndex, line ] of lines.entries()) {
|
|
23
|
+
if (!ignoreBlockLineNumbers.has(lineIndex + 1)) {
|
|
18
24
|
const match =
|
|
19
25
|
/^(#+)([ \t]*)([^#]*?[^#\\])([ \t]*)((?:\\#)?)(#+)(\s*)$/.exec(line);
|
|
20
26
|
if (match) {
|
|
@@ -31,7 +37,7 @@ module.exports = {
|
|
|
31
37
|
const leftHashLength = leftHash.length;
|
|
32
38
|
const rightHashLength = rightHash.length;
|
|
33
39
|
const left = !leftSpaceLength;
|
|
34
|
-
const right = !rightSpaceLength || rightEscape;
|
|
40
|
+
const right = !rightSpaceLength || !!rightEscape;
|
|
35
41
|
const rightEscapeReplacement = rightEscape ? `${rightEscape} ` : "";
|
|
36
42
|
if (left || right) {
|
|
37
43
|
const range = left ?
|
|
@@ -60,6 +66,6 @@ module.exports = {
|
|
|
60
66
|
}
|
|
61
67
|
}
|
|
62
68
|
}
|
|
63
|
-
}
|
|
69
|
+
}
|
|
64
70
|
}
|
|
65
71
|
};
|
package/lib/md022.js
CHANGED
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorDetailIf, blockquotePrefixRe, isBlankLine } =
|
|
6
|
-
|
|
7
|
-
const {
|
|
8
|
-
require("../helpers/micromark.cjs");
|
|
5
|
+
const { addErrorDetailIf, blockquotePrefixRe, isBlankLine } = require("../helpers");
|
|
6
|
+
const { getHeadingLevel } = require("../helpers/micromark.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
9
8
|
|
|
10
9
|
const defaultLines = 1;
|
|
11
10
|
|
|
@@ -41,17 +40,8 @@ module.exports = {
|
|
|
41
40
|
"function": function MD022(params, onError) {
|
|
42
41
|
const getLinesAbove = getLinesFunction(params.config.lines_above);
|
|
43
42
|
const getLinesBelow = getLinesFunction(params.config.lines_below);
|
|
44
|
-
// eslint-disable-next-line jsdoc/valid-types
|
|
45
|
-
/** @type import("../helpers/micromark.cjs").Token[] */
|
|
46
|
-
const micromarkTokens =
|
|
47
|
-
// @ts-ignore
|
|
48
|
-
params.parsers.micromark.tokens;
|
|
49
43
|
const { lines } = params;
|
|
50
|
-
const
|
|
51
|
-
micromarkTokens,
|
|
52
|
-
[ "atxHeading", "setextHeading" ]
|
|
53
|
-
).filter((heading) => !inHtmlFlow(heading));
|
|
54
|
-
for (const heading of headings) {
|
|
44
|
+
for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
|
|
55
45
|
const { startLine, endLine } = heading;
|
|
56
46
|
const line = lines[startLine - 1].trim();
|
|
57
47
|
|
|
@@ -73,7 +63,7 @@ module.exports = {
|
|
|
73
63
|
actualAbove,
|
|
74
64
|
"Above",
|
|
75
65
|
line,
|
|
76
|
-
|
|
66
|
+
undefined,
|
|
77
67
|
{
|
|
78
68
|
"insertText": getBlockQuote(
|
|
79
69
|
lines[startLine - 2],
|
|
@@ -101,7 +91,7 @@ module.exports = {
|
|
|
101
91
|
actualBelow,
|
|
102
92
|
"Below",
|
|
103
93
|
line,
|
|
104
|
-
|
|
94
|
+
undefined,
|
|
105
95
|
{
|
|
106
96
|
"lineNumber": endLine + 1,
|
|
107
97
|
"insertText": getBlockQuote(
|
package/lib/md023.js
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorContext
|
|
6
|
-
|
|
7
|
-
const spaceBeforeHeadingRe = /^(\s+|[>\s]+\s\s)[^>\s]/;
|
|
5
|
+
const { addErrorContext } = require("../helpers");
|
|
6
|
+
const { filterByTypesCached } = require("./cache");
|
|
8
7
|
|
|
9
8
|
// eslint-disable-next-line jsdoc/valid-types
|
|
10
9
|
/** @type import("./markdownlint").Rule */
|
|
@@ -12,30 +11,30 @@ module.exports = {
|
|
|
12
11
|
"names": [ "MD023", "heading-start-left" ],
|
|
13
12
|
"description": "Headings must start at the beginning of the line",
|
|
14
13
|
"tags": [ "headings", "spaces" ],
|
|
15
|
-
"parser": "
|
|
14
|
+
"parser": "micromark",
|
|
16
15
|
"function": function MD023(params, onError) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
16
|
+
const headings = filterByTypesCached([ "atxHeading", "linePrefix", "setextHeading" ]);
|
|
17
|
+
for (let i = 0; i < headings.length - 1; i++) {
|
|
18
|
+
if (
|
|
19
|
+
(headings[i].type === "linePrefix") &&
|
|
20
|
+
(headings[i + 1].type !== "linePrefix") &&
|
|
21
|
+
(headings[i].startLine === headings[i + 1].startLine)
|
|
22
|
+
) {
|
|
23
|
+
const { endColumn, startColumn, startLine } = headings[i];
|
|
24
|
+
const length = endColumn - startColumn;
|
|
27
25
|
addErrorContext(
|
|
28
26
|
onError,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
[
|
|
27
|
+
startLine,
|
|
28
|
+
params.lines[startLine - 1],
|
|
29
|
+
true,
|
|
30
|
+
false,
|
|
31
|
+
[ startColumn, length ],
|
|
34
32
|
{
|
|
35
|
-
"editColumn":
|
|
36
|
-
"deleteCount":
|
|
37
|
-
}
|
|
33
|
+
"editColumn": startColumn,
|
|
34
|
+
"deleteCount": length
|
|
35
|
+
}
|
|
36
|
+
);
|
|
38
37
|
}
|
|
39
|
-
}
|
|
38
|
+
}
|
|
40
39
|
}
|
|
41
40
|
};
|
package/lib/md024.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorContext
|
|
5
|
+
const { addErrorContext } = require("../helpers");
|
|
6
|
+
const { getHeadingLevel, getHeadingText } = require("../helpers/micromark.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
6
8
|
|
|
7
9
|
// eslint-disable-next-line jsdoc/valid-types
|
|
8
10
|
/** @type import("./markdownlint").Rule */
|
|
@@ -10,15 +12,16 @@ module.exports = {
|
|
|
10
12
|
"names": [ "MD024", "no-duplicate-heading" ],
|
|
11
13
|
"description": "Multiple headings with the same content",
|
|
12
14
|
"tags": [ "headings" ],
|
|
13
|
-
"parser": "
|
|
15
|
+
"parser": "micromark",
|
|
14
16
|
"function": function MD024(params, onError) {
|
|
15
17
|
const siblingsOnly = !!params.config.siblings_only || false;
|
|
16
18
|
const knownContents = [ null, [] ];
|
|
17
19
|
let lastLevel = 1;
|
|
18
20
|
let knownContent = knownContents[lastLevel];
|
|
19
|
-
|
|
21
|
+
for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
|
|
22
|
+
const headingText = getHeadingText(heading);
|
|
20
23
|
if (siblingsOnly) {
|
|
21
|
-
const newLevel = heading
|
|
24
|
+
const newLevel = getHeadingLevel(heading);
|
|
22
25
|
while (lastLevel < newLevel) {
|
|
23
26
|
lastLevel++;
|
|
24
27
|
knownContents[lastLevel] = [];
|
|
@@ -30,16 +33,16 @@ module.exports = {
|
|
|
30
33
|
knownContent = knownContents[newLevel];
|
|
31
34
|
}
|
|
32
35
|
// @ts-ignore
|
|
33
|
-
if (knownContent.includes(
|
|
36
|
+
if (knownContent.includes(headingText)) {
|
|
34
37
|
addErrorContext(
|
|
35
38
|
onError,
|
|
36
|
-
heading.
|
|
37
|
-
|
|
39
|
+
heading.startLine,
|
|
40
|
+
headingText.trim()
|
|
38
41
|
);
|
|
39
42
|
} else {
|
|
40
43
|
// @ts-ignore
|
|
41
|
-
knownContent.push(
|
|
44
|
+
knownContent.push(headingText);
|
|
42
45
|
}
|
|
43
|
-
}
|
|
46
|
+
}
|
|
44
47
|
}
|
|
45
48
|
};
|
package/lib/md025.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorContext,
|
|
6
|
-
|
|
5
|
+
const { addErrorContext, frontMatterHasTitle } = require("../helpers");
|
|
6
|
+
const { getHeadingLevel, getHeadingText } = require("../helpers/micromark.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
7
8
|
|
|
8
9
|
// eslint-disable-next-line jsdoc/valid-types
|
|
9
10
|
/** @type import("./markdownlint").Rule */
|
|
@@ -11,25 +12,29 @@ module.exports = {
|
|
|
11
12
|
"names": [ "MD025", "single-title", "single-h1" ],
|
|
12
13
|
"description": "Multiple top-level headings in the same document",
|
|
13
14
|
"tags": [ "headings" ],
|
|
14
|
-
"parser": "
|
|
15
|
+
"parser": "micromark",
|
|
15
16
|
"function": function MD025(params, onError) {
|
|
16
17
|
const level = Number(params.config.level || 1);
|
|
17
|
-
const tag = "h" + level;
|
|
18
18
|
const foundFrontMatterTitle =
|
|
19
19
|
frontMatterHasTitle(
|
|
20
20
|
params.frontMatterLines,
|
|
21
21
|
params.config.front_matter_title
|
|
22
22
|
);
|
|
23
23
|
let hasTopLevelHeading = false;
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
|
|
25
|
+
const headingLevel = getHeadingLevel(heading);
|
|
26
|
+
if (headingLevel === level) {
|
|
26
27
|
if (hasTopLevelHeading || foundFrontMatterTitle) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
const headingText = getHeadingText(heading);
|
|
29
|
+
addErrorContext(
|
|
30
|
+
onError,
|
|
31
|
+
heading.startLine,
|
|
32
|
+
headingText
|
|
33
|
+
);
|
|
34
|
+
} else if (heading.startLine === 1) {
|
|
30
35
|
hasTopLevelHeading = true;
|
|
31
36
|
}
|
|
32
37
|
}
|
|
33
|
-
}
|
|
38
|
+
}
|
|
34
39
|
}
|
|
35
40
|
};
|
package/lib/md026.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const { addError, allPunctuationNoQuestion, endOfLineGemojiCodeRe,
|
|
6
6
|
endOfLineHtmlEntityRe, escapeForRegExp } = require("../helpers");
|
|
7
|
-
const {
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
8
8
|
|
|
9
9
|
// eslint-disable-next-line jsdoc/valid-types
|
|
10
10
|
/** @type import("./markdownlint").Rule */
|
|
@@ -20,12 +20,7 @@ module.exports = {
|
|
|
20
20
|
);
|
|
21
21
|
const trailingPunctuationRe =
|
|
22
22
|
new RegExp("\\s*[" + escapeForRegExp(punctuation) + "]+$");
|
|
23
|
-
|
|
24
|
-
/** @type import("../helpers/micromark.cjs").Token[] */
|
|
25
|
-
const micromarkTokens =
|
|
26
|
-
// @ts-ignore
|
|
27
|
-
params.parsers.micromark.tokens;
|
|
28
|
-
const headings = filterByTypes(micromarkTokens, [ "atxHeadingText", "setextHeadingText" ]);
|
|
23
|
+
const headings = filterByTypesCached([ "atxHeadingText", "setextHeadingText" ]);
|
|
29
24
|
for (const heading of headings) {
|
|
30
25
|
const { endColumn, endLine, text } = heading;
|
|
31
26
|
const match = trailingPunctuationRe.exec(text);
|
package/lib/md027.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
5
|
const { addErrorContext } = require("../helpers");
|
|
6
|
-
const {
|
|
6
|
+
const { filterByTypesCached } = require("./cache");
|
|
7
7
|
|
|
8
8
|
// eslint-disable-next-line jsdoc/valid-types
|
|
9
9
|
/** @type import("./markdownlint").Rule */
|
|
@@ -13,13 +13,8 @@ module.exports = {
|
|
|
13
13
|
"tags": ["blockquote", "whitespace", "indentation"],
|
|
14
14
|
"parser": "micromark",
|
|
15
15
|
"function": function MD027(params, onError) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const micromarkTokens =
|
|
19
|
-
// @ts-ignore
|
|
20
|
-
params.parsers.micromark.tokens;
|
|
21
|
-
for (const token of filterByTypes(micromarkTokens, [ "linePrefix" ])) {
|
|
22
|
-
const siblings = token.parent?.children || micromarkTokens;
|
|
16
|
+
for (const token of filterByTypesCached([ "linePrefix" ])) {
|
|
17
|
+
const siblings = token.parent?.children || params.parsers.micromark.tokens;
|
|
23
18
|
if (siblings[siblings.indexOf(token) - 1]?.type === "blockQuotePrefix") {
|
|
24
19
|
const { startColumn, startLine, text } = token;
|
|
25
20
|
const { length } = text;
|
|
@@ -28,8 +23,8 @@ module.exports = {
|
|
|
28
23
|
onError,
|
|
29
24
|
startLine,
|
|
30
25
|
line,
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
undefined,
|
|
27
|
+
undefined,
|
|
33
28
|
[ startColumn, length ],
|
|
34
29
|
{
|
|
35
30
|
"editColumn": startColumn,
|
package/lib/md028.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
5
|
const { addError } = require("../helpers");
|
|
6
|
-
const {
|
|
6
|
+
const { filterByTypesCached } = require("./cache");
|
|
7
7
|
|
|
8
8
|
const ignoreTypes = new Set([ "lineEnding", "listItemIndent", "linePrefix" ]);
|
|
9
9
|
|
|
@@ -15,14 +15,9 @@ module.exports = {
|
|
|
15
15
|
"tags": [ "blockquote", "whitespace" ],
|
|
16
16
|
"parser": "micromark",
|
|
17
17
|
"function": function MD028(params, onError) {
|
|
18
|
-
|
|
19
|
-
/** @type import("../helpers/micromark.cjs").Token[] */
|
|
20
|
-
const micromarkTokens =
|
|
21
|
-
// @ts-ignore
|
|
22
|
-
params.parsers.micromark.tokens;
|
|
23
|
-
for (const token of filterByTypes(micromarkTokens, [ "blockQuote" ])) {
|
|
18
|
+
for (const token of filterByTypesCached([ "blockQuote" ])) {
|
|
24
19
|
const errorLineNumbers = [];
|
|
25
|
-
const siblings = token.parent?.children ||
|
|
20
|
+
const siblings = token.parent?.children || params.parsers.micromark.tokens;
|
|
26
21
|
for (let i = siblings.indexOf(token) + 1; i < siblings.length; i++) {
|
|
27
22
|
const sibling = siblings[i];
|
|
28
23
|
const { startLine, type } = sibling;
|
package/lib/md029.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorDetailIf
|
|
6
|
-
|
|
7
|
-
const {
|
|
5
|
+
const { addErrorDetailIf } = require("../helpers");
|
|
6
|
+
const { getDescendantsByType, getTokenTextByType } = require("../helpers/micromark.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
8
8
|
|
|
9
9
|
const listStyleExamples = {
|
|
10
10
|
"one": "1/1/1",
|
|
@@ -12,32 +12,37 @@ const listStyleExamples = {
|
|
|
12
12
|
"zero": "0/0/0"
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Gets the value of an ordered list item prefix token.
|
|
17
|
+
*
|
|
18
|
+
* @param {import("../helpers/micromark.cjs").Token} listItemPrefix List item prefix token.
|
|
19
|
+
* @returns {number} List item value.
|
|
20
|
+
*/
|
|
21
|
+
function getOrderedListItemValue(listItemPrefix) {
|
|
22
|
+
return Number(getTokenTextByType(listItemPrefix.children, "listItemValue"));
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
// eslint-disable-next-line jsdoc/valid-types
|
|
16
26
|
/** @type import("./markdownlint").Rule */
|
|
17
27
|
module.exports = {
|
|
18
28
|
"names": [ "MD029", "ol-prefix" ],
|
|
19
29
|
"description": "Ordered list item prefix",
|
|
20
30
|
"tags": [ "ol" ],
|
|
21
|
-
"parser": "
|
|
31
|
+
"parser": "micromark",
|
|
22
32
|
"function": function MD029(params, onError) {
|
|
23
33
|
const style = String(params.config.style || "one_or_ordered");
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
let current = 1;
|
|
34
|
+
for (const listOrdered of filterByTypesCached([ "listOrdered" ])) {
|
|
35
|
+
const listItemPrefixes = getDescendantsByType(listOrdered, [ "listItemPrefix" ]);
|
|
36
|
+
let expected = 1;
|
|
28
37
|
let incrementing = false;
|
|
29
38
|
// Check for incrementing number pattern 1/2/3 or 0/1/2
|
|
30
|
-
if (
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
incrementing = true;
|
|
38
|
-
if (firstNumber === "0") {
|
|
39
|
-
current = 0;
|
|
40
|
-
}
|
|
39
|
+
if (listItemPrefixes.length >= 2) {
|
|
40
|
+
const firstValue = getOrderedListItemValue(listItemPrefixes[0]);
|
|
41
|
+
const secondValue = getOrderedListItemValue(listItemPrefixes[1]);
|
|
42
|
+
if ((secondValue !== 1) || (firstValue === 0)) {
|
|
43
|
+
incrementing = true;
|
|
44
|
+
if (firstValue === 0) {
|
|
45
|
+
expected = 0;
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
48
|
}
|
|
@@ -45,24 +50,25 @@ module.exports = {
|
|
|
45
50
|
let listStyle = style;
|
|
46
51
|
if (listStyle === "one_or_ordered") {
|
|
47
52
|
listStyle = incrementing ? "ordered" : "one";
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (listStyle === "zero") {
|
|
51
|
-
current = 0;
|
|
53
|
+
} else if (listStyle === "zero") {
|
|
54
|
+
expected = 0;
|
|
52
55
|
} else if (listStyle === "one") {
|
|
53
|
-
|
|
56
|
+
expected = 1;
|
|
54
57
|
}
|
|
55
58
|
// Validate each list item marker
|
|
56
|
-
for (const
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
for (const listItemPrefix of listItemPrefixes) {
|
|
60
|
+
const actual = getOrderedListItemValue(listItemPrefix);
|
|
61
|
+
addErrorDetailIf(
|
|
62
|
+
onError,
|
|
63
|
+
listItemPrefix.startLine,
|
|
64
|
+
expected,
|
|
65
|
+
actual,
|
|
66
|
+
"Style: " + listStyleExamples[listStyle],
|
|
67
|
+
undefined,
|
|
68
|
+
[ listItemPrefix.startColumn, listItemPrefix.endColumn - listItemPrefix.startColumn ]
|
|
69
|
+
);
|
|
70
|
+
if (listStyle === "ordered") {
|
|
71
|
+
expected++;
|
|
66
72
|
}
|
|
67
73
|
}
|
|
68
74
|
}
|
package/lib/md030.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
5
|
const { addErrorDetailIf } = require("../helpers");
|
|
6
|
-
const {
|
|
6
|
+
const { filterByTypesCached } = require("./cache");
|
|
7
7
|
|
|
8
8
|
// eslint-disable-next-line jsdoc/valid-types
|
|
9
9
|
/** @type import("./markdownlint").Rule */
|
|
@@ -17,13 +17,7 @@ module.exports = {
|
|
|
17
17
|
const olSingle = Number(params.config.ol_single || 1);
|
|
18
18
|
const ulMulti = Number(params.config.ul_multi || 1);
|
|
19
19
|
const olMulti = Number(params.config.ol_multi || 1);
|
|
20
|
-
|
|
21
|
-
/** @type import("../helpers/micromark.cjs").Token[] */
|
|
22
|
-
const micromarkTokens =
|
|
23
|
-
// @ts-ignore
|
|
24
|
-
params.parsers.micromark.tokens;
|
|
25
|
-
const lists = filterByTypes(micromarkTokens, [ "listOrdered", "listUnordered" ]);
|
|
26
|
-
for (const list of lists) {
|
|
20
|
+
for (const list of filterByTypesCached([ "listOrdered", "listUnordered" ])) {
|
|
27
21
|
const ordered = (list.type === "listOrdered");
|
|
28
22
|
const listItemPrefixes =
|
|
29
23
|
list.children.filter((token) => (token.type === "listItemPrefix"));
|
|
@@ -54,8 +48,8 @@ module.exports = {
|
|
|
54
48
|
startLine,
|
|
55
49
|
expectedSpaces,
|
|
56
50
|
actualSpaces,
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
undefined,
|
|
52
|
+
undefined,
|
|
59
53
|
range,
|
|
60
54
|
fixInfo
|
|
61
55
|
);
|
package/lib/md031.js
CHANGED
|
@@ -2,42 +2,62 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorContext,
|
|
6
|
-
const {
|
|
5
|
+
const { addErrorContext, isBlankLine } = require("../helpers");
|
|
6
|
+
const { getTokenParentOfType } = require("../helpers/micromark.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
7
8
|
|
|
8
9
|
const codeFencePrefixRe = /^(.*?)[`~]/;
|
|
9
10
|
|
|
11
|
+
// eslint-disable-next-line jsdoc/valid-types
|
|
12
|
+
/** @typedef {readonly string[]} ReadonlyStringArray */
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Adds an error for the top or bottom of a code fence.
|
|
16
|
+
*
|
|
17
|
+
* @param {import("./markdownlint").RuleOnError} onError Error-reporting callback.
|
|
18
|
+
* @param {ReadonlyStringArray} lines Lines of Markdown content.
|
|
19
|
+
* @param {number} lineNumber Line number.
|
|
20
|
+
* @param {boolean} top True iff top fence.
|
|
21
|
+
* @returns {void}
|
|
22
|
+
*/
|
|
23
|
+
function addError(onError, lines, lineNumber, top) {
|
|
24
|
+
const line = lines[lineNumber - 1];
|
|
25
|
+
const [ , prefix ] = line.match(codeFencePrefixRe) || [];
|
|
26
|
+
const fixInfo = (prefix === undefined) ? null : {
|
|
27
|
+
"lineNumber": lineNumber + (top ? 0 : 1),
|
|
28
|
+
"insertText": `${prefix.replace(/[^>]/g, " ").trim()}\n`
|
|
29
|
+
};
|
|
30
|
+
addErrorContext(
|
|
31
|
+
onError,
|
|
32
|
+
lineNumber,
|
|
33
|
+
line.trim(),
|
|
34
|
+
undefined,
|
|
35
|
+
undefined,
|
|
36
|
+
undefined,
|
|
37
|
+
fixInfo
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
10
41
|
// eslint-disable-next-line jsdoc/valid-types
|
|
11
42
|
/** @type import("./markdownlint").Rule */
|
|
12
43
|
module.exports = {
|
|
13
44
|
"names": [ "MD031", "blanks-around-fences" ],
|
|
14
45
|
"description": "Fenced code blocks should be surrounded by blank lines",
|
|
15
46
|
"tags": [ "code", "blank_lines" ],
|
|
16
|
-
"parser": "
|
|
47
|
+
"parser": "micromark",
|
|
17
48
|
"function": function MD031(params, onError) {
|
|
18
49
|
const listItems = params.config.list_items;
|
|
19
50
|
const includeListItems = (listItems === undefined) ? true : !!listItems;
|
|
20
51
|
const { lines } = params;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"lineNumber": i + (onTopFence ? 1 : 2),
|
|
30
|
-
"insertText": `${prefix.replace(/[^>]/g, " ").trim()}\n`
|
|
31
|
-
};
|
|
32
|
-
addErrorContext(
|
|
33
|
-
onError,
|
|
34
|
-
i + 1,
|
|
35
|
-
lines[i].trim(),
|
|
36
|
-
null,
|
|
37
|
-
null,
|
|
38
|
-
null,
|
|
39
|
-
fixInfo);
|
|
52
|
+
for (const codeBlock of filterByTypesCached([ "codeFenced" ])) {
|
|
53
|
+
if (includeListItems || !(getTokenParentOfType(codeBlock, [ "listOrdered", "listUnordered" ]))) {
|
|
54
|
+
if (!isBlankLine(lines[codeBlock.startLine - 2])) {
|
|
55
|
+
addError(onError, lines, codeBlock.startLine, true);
|
|
56
|
+
}
|
|
57
|
+
if (!isBlankLine(lines[codeBlock.endLine]) && !isBlankLine(lines[codeBlock.endLine - 1])) {
|
|
58
|
+
addError(onError, lines, codeBlock.endLine, false);
|
|
59
|
+
}
|
|
40
60
|
}
|
|
41
|
-
}
|
|
61
|
+
}
|
|
42
62
|
}
|
|
43
63
|
};
|