markdownlint 0.41.0 → 0.42.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 +24 -0
- package/CONTRIBUTING.md +62 -77
- package/README.md +7 -7
- package/doc/CustomRules.md +6 -4
- package/doc/Rules.md +87 -36
- package/doc/md010.md +0 -1
- package/doc/md024.md +1 -1
- package/doc/md029.md +11 -1
- package/doc/md043.md +47 -9
- package/doc/md051.md +19 -19
- package/doc/md055.md +6 -3
- package/doc/md060.md +3 -2
- package/helpers/README.md +2 -2
- package/helpers/helpers.cjs +23 -7
- package/helpers/micromark-helpers.cjs +8 -12
- package/helpers/package.json +1 -5
- package/helpers/shared.cjs +1 -1
- package/lib/cache.mjs +1 -0
- package/lib/configuration-strict.d.ts +124 -112
- package/lib/constants.mjs +1 -1
- package/lib/defer-require.cjs +1 -0
- package/lib/exports.d.mts +29 -1
- package/lib/markdownit.cjs +8 -1
- package/lib/markdownlint.d.mts +337 -195
- package/lib/markdownlint.mjs +83 -87
- package/lib/md004.mjs +1 -0
- package/lib/md005.mjs +1 -1
- package/lib/md007.mjs +2 -2
- package/lib/md009.mjs +2 -3
- package/lib/md010.mjs +1 -1
- package/lib/md011.mjs +10 -7
- package/lib/md012.mjs +1 -1
- package/lib/md013.mjs +13 -9
- package/lib/md018.mjs +1 -1
- package/lib/md019-md021.mjs +1 -0
- package/lib/md020.mjs +1 -1
- package/lib/md022.mjs +6 -6
- package/lib/md024.mjs +1 -1
- package/lib/md025.mjs +1 -1
- package/lib/md027.mjs +1 -1
- package/lib/md028.mjs +1 -2
- package/lib/md029.mjs +34 -23
- package/lib/md030.mjs +4 -4
- package/lib/md031.mjs +1 -1
- package/lib/md032.mjs +5 -4
- package/lib/md033.mjs +1 -1
- package/lib/md034.mjs +1 -1
- package/lib/md037.mjs +1 -2
- package/lib/md039.mjs +5 -1
- package/lib/md041.mjs +1 -1
- package/lib/md043.mjs +98 -44
- package/lib/md044.mjs +2 -2
- package/lib/md048.mjs +1 -3
- package/lib/md051.mjs +3 -3
- package/lib/md055.mjs +11 -1
- package/lib/md056.mjs +1 -1
- package/lib/md058.mjs +1 -1
- package/lib/md060.mjs +24 -4
- package/lib/micromark-parse.mjs +3 -2
- package/lib/resolve-module.cjs +3 -2
- package/lib/resolve-module.d.cts +15 -10
- package/package.json +33 -34
- package/schema/.markdownlint.jsonc +54 -54
- package/schema/.markdownlint.yaml +76 -76
- package/schema/markdownlint-config-schema-strict.json +126 -118
- package/schema/markdownlint-config-schema.json +126 -118
package/lib/md022.mjs
CHANGED
|
@@ -11,14 +11,15 @@ const defaultLines = 1;
|
|
|
11
11
|
// eslint-disable-next-line jsdoc/reject-any-type
|
|
12
12
|
const getLinesFunction = (/** @type {any} */ linesParam) => {
|
|
13
13
|
if (Array.isArray(linesParam)) {
|
|
14
|
+
// eslint-disable-next-line unicorn/no-unreadable-new-expression
|
|
14
15
|
const linesArray = new Array(6).fill(defaultLines);
|
|
15
|
-
for (const [ index, value ] of
|
|
16
|
+
for (const [ index, value ] of linesParam.slice(0, 6).entries()) {
|
|
16
17
|
linesArray[index] = value;
|
|
17
18
|
}
|
|
18
19
|
return (/** @type {MicromarkToken} */ heading) => linesArray[getHeadingLevel(heading) - 1];
|
|
19
20
|
}
|
|
20
21
|
// Coerce linesParam to a number
|
|
21
|
-
const lines = (linesParam
|
|
22
|
+
const lines = Number(linesParam ?? defaultLines);
|
|
22
23
|
return () => lines;
|
|
23
24
|
};
|
|
24
25
|
|
|
@@ -26,10 +27,9 @@ const getLine = (/** @type {readonly string[]} */ lines, /** @type {number} */ i
|
|
|
26
27
|
if ((index >= 0) && (index < lines.length)) {
|
|
27
28
|
return lines[index];
|
|
28
29
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return "";
|
|
30
|
+
return (includeFrontMatter && (frontMatterLines.length > 0) && (index < 0) && (index >= -frontMatterLines.length)) ?
|
|
31
|
+
frontMatterLines[frontMatterLines.length + index] :
|
|
32
|
+
"";
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
/** @type {import("markdownlint").Rule} */
|
package/lib/md024.mjs
CHANGED
|
@@ -11,7 +11,7 @@ export default {
|
|
|
11
11
|
"tags": [ "headings" ],
|
|
12
12
|
"parser": "micromark",
|
|
13
13
|
"function": function MD024(params, onError) {
|
|
14
|
-
const siblingsOnly = !!params.config.siblings_only
|
|
14
|
+
const siblingsOnly = !!params.config.siblings_only;
|
|
15
15
|
const knownContents = [ null, [] ];
|
|
16
16
|
let lastLevel = 1;
|
|
17
17
|
let knownContent = knownContents[lastLevel];
|
package/lib/md025.mjs
CHANGED
|
@@ -11,7 +11,7 @@ export default {
|
|
|
11
11
|
"tags": [ "headings" ],
|
|
12
12
|
"parser": "micromark",
|
|
13
13
|
"function": function MD025(params, onError) {
|
|
14
|
-
const level = Number(params.config.level
|
|
14
|
+
const level = Number(params.config.level ?? 1);
|
|
15
15
|
const { tokens } = params.parsers.micromark;
|
|
16
16
|
const matchingHeadings = filterByTypesCached([ "atxHeading", "setextHeading" ])
|
|
17
17
|
.filter((heading) => (level === getHeadingLevel(heading)) && !isDocfxTab(heading));
|
package/lib/md027.mjs
CHANGED
|
@@ -15,7 +15,7 @@ export default {
|
|
|
15
15
|
"parser": "micromark",
|
|
16
16
|
"function": function MD027(params, onError) {
|
|
17
17
|
const listItems = params.config.list_items;
|
|
18
|
-
const includeListItems = (listItems === undefined)
|
|
18
|
+
const includeListItems = (listItems === undefined) || !!listItems;
|
|
19
19
|
const { tokens } = params.parsers.micromark;
|
|
20
20
|
for (const token of filterByTypesCached([ "linePrefix" ])) {
|
|
21
21
|
const parent = token.parent;
|
package/lib/md028.mjs
CHANGED
|
@@ -16,8 +16,7 @@ export default {
|
|
|
16
16
|
const errorLineNumbers = [];
|
|
17
17
|
const siblings = token.parent?.children || params.parsers.micromark.tokens;
|
|
18
18
|
for (let i = siblings.indexOf(token) + 1; i < siblings.length; i++) {
|
|
19
|
-
const
|
|
20
|
-
const { startLine, type } = sibling;
|
|
19
|
+
const { startLine, type } = siblings[i];
|
|
21
20
|
if (type === "lineEndingBlank") {
|
|
22
21
|
// Possible blank between blockquotes
|
|
23
22
|
errorLineNumbers.push(startLine);
|
package/lib/md029.mjs
CHANGED
|
@@ -15,13 +15,15 @@ const listStyles = Object.keys(listStyleExamples);
|
|
|
15
15
|
* Gets the column and text of an ordered list item prefix token.
|
|
16
16
|
*
|
|
17
17
|
* @param {import("markdownlint").MicromarkToken} listItemPrefix List item prefix token.
|
|
18
|
-
* @returns {{
|
|
18
|
+
* @returns {{startColumn: number, endColumn: number, text: string, number: number}} List item value column and text.
|
|
19
19
|
*/
|
|
20
20
|
function getOrderedListItemValue(listItemPrefix) {
|
|
21
|
-
const
|
|
21
|
+
const { startColumn, endColumn, text } = getDescendantsByType(listItemPrefix, [ "listItemValue" ])[0];
|
|
22
22
|
return {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
startColumn,
|
|
24
|
+
endColumn,
|
|
25
|
+
text,
|
|
26
|
+
"number": Number(text)
|
|
25
27
|
};
|
|
26
28
|
}
|
|
27
29
|
|
|
@@ -35,40 +37,49 @@ export default {
|
|
|
35
37
|
const style = String(params.config.style);
|
|
36
38
|
for (const listOrdered of filterByTypesCached([ "listOrdered" ])) {
|
|
37
39
|
const listItemPrefixes = getDescendantsByType(listOrdered, [ "listItemPrefix" ]);
|
|
38
|
-
let
|
|
40
|
+
let expectedNumber = 1;
|
|
39
41
|
let incrementing = false;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
let rightAligned = false;
|
|
43
|
+
let zeroPrefixed = false;
|
|
44
|
+
if (listItemPrefixes.length > 0) {
|
|
45
|
+
// Check for right alignment
|
|
46
|
+
const firstPrefixEndColumn = listItemPrefixes[0].endColumn;
|
|
47
|
+
rightAligned = listItemPrefixes.every((prefix) => prefix.endColumn === firstPrefixEndColumn);
|
|
48
|
+
if (listItemPrefixes.length > 1) {
|
|
49
|
+
// Check for incrementing number pattern 1/2/3 or 0/1/2
|
|
50
|
+
const firstValue = getOrderedListItemValue(listItemPrefixes[0]);
|
|
51
|
+
const secondValue = getOrderedListItemValue(listItemPrefixes[1]);
|
|
52
|
+
if ((secondValue.number !== 1) || (firstValue.number === 0)) {
|
|
53
|
+
incrementing = true;
|
|
54
|
+
if (firstValue.number === 0) {
|
|
55
|
+
expectedNumber = 0;
|
|
56
|
+
}
|
|
48
57
|
}
|
|
58
|
+
zeroPrefixed = firstValue.text.startsWith("0");
|
|
49
59
|
}
|
|
50
60
|
}
|
|
51
61
|
// Determine effective style
|
|
52
62
|
const listStyle = listStyles.includes(style) ? style : (incrementing ? "ordered" : "one");
|
|
53
63
|
if (listStyle === "zero") {
|
|
54
|
-
|
|
64
|
+
expectedNumber = 0;
|
|
55
65
|
} else if (listStyle === "one") {
|
|
56
|
-
|
|
66
|
+
expectedNumber = 1;
|
|
57
67
|
}
|
|
58
68
|
// Validate each list item marker
|
|
59
69
|
for (const listItemPrefix of listItemPrefixes) {
|
|
60
|
-
const
|
|
61
|
-
const
|
|
70
|
+
const expectedText = expectedNumber.toString();
|
|
71
|
+
const { endColumn, startColumn, "number": actualNumber } = getOrderedListItemValue(listItemPrefix);
|
|
72
|
+
const length = endColumn - startColumn;
|
|
62
73
|
const fixInfo = {
|
|
63
|
-
"editColumn":
|
|
64
|
-
"deleteCount":
|
|
65
|
-
"insertText":
|
|
74
|
+
"editColumn": startColumn,
|
|
75
|
+
"deleteCount": length,
|
|
76
|
+
"insertText": rightAligned ? expectedText.padStart(length, (zeroPrefixed ? "0" : " ")) : expectedText
|
|
66
77
|
};
|
|
67
78
|
addErrorDetailIf(
|
|
68
79
|
onError,
|
|
69
80
|
listItemPrefix.startLine,
|
|
70
|
-
|
|
71
|
-
|
|
81
|
+
expectedNumber,
|
|
82
|
+
actualNumber,
|
|
72
83
|
// @ts-ignore
|
|
73
84
|
"Style: " + listStyleExamples[listStyle],
|
|
74
85
|
undefined,
|
|
@@ -76,7 +87,7 @@ export default {
|
|
|
76
87
|
fixInfo
|
|
77
88
|
);
|
|
78
89
|
if (listStyle === "ordered") {
|
|
79
|
-
|
|
90
|
+
expectedNumber++;
|
|
80
91
|
}
|
|
81
92
|
}
|
|
82
93
|
}
|
package/lib/md030.mjs
CHANGED
|
@@ -10,10 +10,10 @@ export default {
|
|
|
10
10
|
"tags": [ "ol", "ul", "whitespace" ],
|
|
11
11
|
"parser": "micromark",
|
|
12
12
|
"function": function MD030(params, onError) {
|
|
13
|
-
const ulSingle = Number(params.config.ul_single
|
|
14
|
-
const olSingle = Number(params.config.ol_single
|
|
15
|
-
const ulMulti = Number(params.config.ul_multi
|
|
16
|
-
const olMulti = Number(params.config.ol_multi
|
|
13
|
+
const ulSingle = Number(params.config.ul_single ?? 1);
|
|
14
|
+
const olSingle = Number(params.config.ol_single ?? 1);
|
|
15
|
+
const ulMulti = Number(params.config.ul_multi ?? 1);
|
|
16
|
+
const olMulti = Number(params.config.ol_multi ?? 1);
|
|
17
17
|
for (const list of filterByTypesCached([ "listOrdered", "listUnordered" ])) {
|
|
18
18
|
const ordered = (list.type === "listOrdered");
|
|
19
19
|
const listItemPrefixes =
|
package/lib/md031.mjs
CHANGED
|
@@ -45,7 +45,7 @@ export default {
|
|
|
45
45
|
"parser": "micromark",
|
|
46
46
|
"function": function MD031(params, onError) {
|
|
47
47
|
const listItems = params.config.list_items;
|
|
48
|
-
const includeListItems = (listItems === undefined)
|
|
48
|
+
const includeListItems = (listItems === undefined) || !!listItems;
|
|
49
49
|
const { lines } = params;
|
|
50
50
|
for (const codeBlock of filterByTypesCached([ "codeFenced" ])) {
|
|
51
51
|
if (includeListItems || !(getParentOfType(codeBlock, [ "listOrdered", "listUnordered" ]))) {
|
package/lib/md032.mjs
CHANGED
|
@@ -52,10 +52,11 @@ export default {
|
|
|
52
52
|
(token) => !nonContentTokens.has(token.type),
|
|
53
53
|
(token) => nonContentTokens.has(token.type) ? [] : token.children
|
|
54
54
|
);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
const endLine = (
|
|
56
|
+
(flattenedChildren.length > 0) ?
|
|
57
|
+
flattenedChildren[flattenedChildren.length - 1] :
|
|
58
|
+
list
|
|
59
|
+
).endLine;
|
|
59
60
|
|
|
60
61
|
// Look for a blank line below the list
|
|
61
62
|
const lastLineNumber = endLine;
|
package/lib/md033.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { addError, nextLinesRe } from "../helpers/helpers.cjs";
|
|
|
4
4
|
import { getHtmlTagInfo, getParentOfType } from "../helpers/micromark-helpers.cjs";
|
|
5
5
|
import { filterByTypesCached } from "./cache.mjs";
|
|
6
6
|
|
|
7
|
-
/** @typedef {import("micromark-extension-gfm-table")} */
|
|
7
|
+
/** @typedef {typeof import("micromark-extension-gfm-table")} _ambient_ */
|
|
8
8
|
|
|
9
9
|
// eslint-disable-next-line jsdoc/reject-any-type
|
|
10
10
|
const toLowerCaseStringArray = (/** @type {any} */ arr) => Array.isArray(arr) ? arr.map((elm) => String(elm).toLowerCase()) : [];
|
package/lib/md034.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { addErrorContext } from "../helpers/helpers.cjs";
|
|
|
4
4
|
import { filterByPredicate, getHtmlTagInfo, inHtmlFlow } from "../helpers/micromark-helpers.cjs";
|
|
5
5
|
|
|
6
6
|
/** @typedef {import("markdownlint").MicromarkToken} MicromarkToken */
|
|
7
|
-
/** @typedef {import("micromark-extension-gfm-autolink-literal")} */
|
|
7
|
+
/** @typedef {typeof import("micromark-extension-gfm-autolink-literal")} _ambient_ */
|
|
8
8
|
|
|
9
9
|
/** @type {import("markdownlint").Rule} */
|
|
10
10
|
export default {
|
package/lib/md037.mjs
CHANGED
|
@@ -38,8 +38,7 @@ export default {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
// Process bare tokens for each emphasis marker type
|
|
41
|
-
for (const
|
|
42
|
-
const [ marker, emphasisTokens ] = entry;
|
|
41
|
+
for (const [ marker, emphasisTokens ] of emphasisTokensByMarker) {
|
|
43
42
|
for (let i = 0; i + 1 < emphasisTokens.length; i += 2) {
|
|
44
43
|
|
|
45
44
|
// Process start token of start/end pair
|
package/lib/md039.mjs
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
import { re } from "re2js";
|
|
3
4
|
import { addErrorContext } from "../helpers/helpers.cjs";
|
|
4
5
|
import { filterByTypesCached } from "./cache.mjs";
|
|
5
6
|
|
|
7
|
+
const startRe = re`^[^\S\r\n]+`;
|
|
8
|
+
const endRe = re`[^\S\r\n]+$`;
|
|
9
|
+
|
|
6
10
|
/**
|
|
7
11
|
* Adds an error for a label space issue.
|
|
8
12
|
*
|
|
@@ -12,7 +16,7 @@ import { filterByTypesCached } from "./cache.mjs";
|
|
|
12
16
|
* @param {boolean} isStart True iff error is at the start of the link.
|
|
13
17
|
*/
|
|
14
18
|
function addLabelSpaceError(onError, label, labelText, isStart) {
|
|
15
|
-
const match =
|
|
19
|
+
const match = (isStart ? startRe : endRe).exec(labelText.text);
|
|
16
20
|
const range = match ?
|
|
17
21
|
[
|
|
18
22
|
(isStart ? (labelText.startColumn) : (labelText.endColumn - match[0].length)),
|
package/lib/md041.mjs
CHANGED
|
@@ -31,7 +31,7 @@ export default {
|
|
|
31
31
|
"parser": "micromark",
|
|
32
32
|
"function": function MD041(params, onError) {
|
|
33
33
|
const allowPreamble = !!params.config.allow_preamble;
|
|
34
|
-
const level = Number(params.config.level
|
|
34
|
+
const level = Number(params.config.level ?? 1);
|
|
35
35
|
const { tokens } = params.parsers.micromark;
|
|
36
36
|
if (
|
|
37
37
|
!frontMatterHasTitle(
|
package/lib/md043.mjs
CHANGED
|
@@ -1,9 +1,32 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { addError, addErrorDetailIf } from "../helpers/helpers.cjs";
|
|
4
4
|
import { getHeadingLevel, getHeadingText } from "../helpers/micromark-helpers.cjs";
|
|
5
5
|
import { filterByTypesCached } from "./cache.mjs";
|
|
6
6
|
|
|
7
|
+
export const headingsItemsRe = /^(?:[*+?]|\^+|[#%]{1,6}\s+\S.*)$/u;
|
|
8
|
+
const prefixedHeadingPartsRe = /^([#%]{1,6})\s+(\S.*)$/u;
|
|
9
|
+
const loopHeadingRe = /^\^+$/u;
|
|
10
|
+
const canMatchNothingHeadingsRe = /^(?:\*|\^+)$/u;
|
|
11
|
+
const maximumLoopsAllowed = 1000;
|
|
12
|
+
|
|
13
|
+
const headingIsExpected = (/** @type {string} */ actual, /** @type {string} */ expected, /** @type {boolean} */ matchCase) => {
|
|
14
|
+
if (expected.startsWith("%")) {
|
|
15
|
+
// Regular expression comparison
|
|
16
|
+
const [ , actualLevel, actualText ] = prefixedHeadingPartsRe.exec(actual) || [];
|
|
17
|
+
const [ , expectedLevel, expectedText ] = prefixedHeadingPartsRe.exec(expected) || [];
|
|
18
|
+
return (
|
|
19
|
+
(actualLevel.length === expectedLevel.length) &&
|
|
20
|
+
// eslint-disable-next-line unicorn/no-unreadable-new-expression
|
|
21
|
+
(new RegExp(expectedText, `${matchCase ? "" : "i"}u`)).test(actualText)
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
// Text comparison
|
|
25
|
+
const actualLower = matchCase ? actual : actual.toLowerCase();
|
|
26
|
+
const expectedLower = matchCase ? expected : expected.toLowerCase();
|
|
27
|
+
return actualLower === expectedLower;
|
|
28
|
+
};
|
|
29
|
+
|
|
7
30
|
/** @type {import("markdownlint").Rule} */
|
|
8
31
|
export default {
|
|
9
32
|
"names": [ "MD043", "required-headings" ],
|
|
@@ -11,62 +34,93 @@ export default {
|
|
|
11
34
|
"tags": [ "headings" ],
|
|
12
35
|
"parser": "micromark",
|
|
13
36
|
"function": function MD043(params, onError) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
37
|
+
/** @type {string[] | null | undefined} */
|
|
38
|
+
const headings = params.config.headings;
|
|
39
|
+
if (!headings) {
|
|
17
40
|
return;
|
|
18
41
|
}
|
|
19
|
-
const
|
|
42
|
+
const requiredHeadings = headings.map((heading) => headingsItemsRe.test(heading) ? heading : "[Invalid syntax]");
|
|
43
|
+
const matchCase = Boolean(params.config.match_case);
|
|
20
44
|
let i = 0;
|
|
21
|
-
let
|
|
45
|
+
let remainingLoopsAllowed = maximumLoopsAllowed;
|
|
46
|
+
let allowAnyMatchFallback = false;
|
|
22
47
|
let hasError = false;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
48
|
+
const getExpected = (/** @type {boolean} */ skipLoopHeadings) => {
|
|
49
|
+
let expected = null;
|
|
50
|
+
do {
|
|
51
|
+
expected = requiredHeadings[i++] || "[No heading]";
|
|
52
|
+
// eslint-disable-next-line no-unmodified-loop-condition
|
|
53
|
+
} while (skipLoopHeadings && loopHeadingRe.test(expected));
|
|
54
|
+
return expected;
|
|
55
|
+
};
|
|
26
56
|
for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
57
|
+
const headingText = getHeadingText(heading);
|
|
58
|
+
const headingLevel = getHeadingLevel(heading);
|
|
59
|
+
const actual = `${"".padEnd(headingLevel, "#")} ${headingText}`;
|
|
60
|
+
let expected = getExpected(false);
|
|
61
|
+
if (expected.startsWith("^")) {
|
|
62
|
+
// Handle loop
|
|
63
|
+
const current = i;
|
|
64
|
+
const nextExpected = getExpected(true);
|
|
65
|
+
if (headingIsExpected(actual, nextExpected, matchCase)) {
|
|
66
|
+
// No need to loop (again)
|
|
67
|
+
expected = nextExpected;
|
|
68
|
+
} else {
|
|
69
|
+
// Try to loop to previous matching marker
|
|
70
|
+
remainingLoopsAllowed--;
|
|
71
|
+
let j = current - 2;
|
|
72
|
+
for (; j >= 0; j--) {
|
|
73
|
+
if (requiredHeadings[j] === expected) {
|
|
74
|
+
// Perform loop
|
|
75
|
+
i = j + 1;
|
|
76
|
+
expected = getExpected(false);
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
38
79
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
80
|
+
if (j < 0) {
|
|
81
|
+
// No matching loop marker
|
|
82
|
+
expected = nextExpected;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (remainingLoopsAllowed < 0) {
|
|
87
|
+
// Too many loops performed
|
|
88
|
+
addError(onError, heading.startLine, "Too many loops", `Limit: ${maximumLoopsAllowed}`);
|
|
89
|
+
hasError = true;
|
|
90
|
+
break;
|
|
91
|
+
} else if (expected === "*") {
|
|
92
|
+
// Handle "zero or more of anything"
|
|
93
|
+
const nextExpected = getExpected(true);
|
|
94
|
+
if (!headingIsExpected(actual, nextExpected, matchCase)) {
|
|
95
|
+
allowAnyMatchFallback = true;
|
|
46
96
|
i--;
|
|
47
|
-
} else {
|
|
48
|
-
addErrorDetailIf(
|
|
49
|
-
onError,
|
|
50
|
-
heading.startLine,
|
|
51
|
-
expected,
|
|
52
|
-
actual
|
|
53
|
-
);
|
|
54
|
-
hasError = true;
|
|
55
97
|
}
|
|
98
|
+
} else if (expected === "+") {
|
|
99
|
+
// Handle "one or more of anything"
|
|
100
|
+
allowAnyMatchFallback = true;
|
|
101
|
+
} else if (expected === "?") {
|
|
102
|
+
// Handle "exactly one of anything"
|
|
103
|
+
// Nothing to do
|
|
104
|
+
} else if (headingIsExpected(actual, expected, matchCase)) {
|
|
105
|
+
// Handle successful match
|
|
106
|
+
allowAnyMatchFallback = false;
|
|
107
|
+
} else if (allowAnyMatchFallback) {
|
|
108
|
+
// Handle "anything" match by restoring index
|
|
109
|
+
i--;
|
|
110
|
+
} else {
|
|
111
|
+
// Report heading mismatch
|
|
112
|
+
addErrorDetailIf(onError, heading.startLine, expected, actual);
|
|
113
|
+
hasError = true;
|
|
114
|
+
break;
|
|
56
115
|
}
|
|
57
116
|
}
|
|
58
|
-
const
|
|
117
|
+
const remainingRequiredHeadings = requiredHeadings.slice(i).filter((heading) => !canMatchNothingHeadingsRe.test(heading));
|
|
59
118
|
if (
|
|
60
119
|
!hasError &&
|
|
61
|
-
(
|
|
62
|
-
((extraHeadings === 1) && (requiredHeadings[i] !== "*"))) &&
|
|
63
|
-
(anyHeadings || !requiredHeadings.every((heading) => heading === "*"))
|
|
120
|
+
(remainingRequiredHeadings.length > 0)
|
|
64
121
|
) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
params.lines.length,
|
|
68
|
-
requiredHeadings[i]
|
|
69
|
-
);
|
|
122
|
+
// Report unmatched heading(s)
|
|
123
|
+
addErrorDetailIf(onError, params.lines.length, remainingRequiredHeadings[0], "[End of file]");
|
|
70
124
|
}
|
|
71
125
|
}
|
|
72
126
|
};
|
package/lib/md044.mjs
CHANGED
|
@@ -24,10 +24,10 @@ export default {
|
|
|
24
24
|
}
|
|
25
25
|
const codeBlocks = params.config.code_blocks;
|
|
26
26
|
const includeCodeBlocks =
|
|
27
|
-
(codeBlocks === undefined)
|
|
27
|
+
(codeBlocks === undefined) || !!codeBlocks;
|
|
28
28
|
const htmlElements = params.config.html_elements;
|
|
29
29
|
const includeHtmlElements =
|
|
30
|
-
(htmlElements === undefined)
|
|
30
|
+
(htmlElements === undefined) || !!htmlElements;
|
|
31
31
|
const scannedTypes = new Set([ "data" ]);
|
|
32
32
|
if (includeCodeBlocks) {
|
|
33
33
|
scannedTypes.add("codeFlowValue");
|
package/lib/md048.mjs
CHANGED
|
@@ -30,9 +30,7 @@ export default {
|
|
|
30
30
|
let expectedStyle = style;
|
|
31
31
|
const codeFenceds = filterByTypesCached([ "codeFenced" ]);
|
|
32
32
|
for (const codeFenced of codeFenceds) {
|
|
33
|
-
const codeFencedFenceSequence
|
|
34
|
-
getDescendantsByType(codeFenced, [ "codeFencedFence", "codeFencedFenceSequence" ])[0];
|
|
35
|
-
const { startLine, text } = codeFencedFenceSequence;
|
|
33
|
+
const { startLine, text } = getDescendantsByType(codeFenced, [ "codeFencedFence", "codeFencedFenceSequence" ])[0];
|
|
36
34
|
if (expectedStyle === "consistent") {
|
|
37
35
|
expectedStyle = fencedCodeBlockStyleFor(text);
|
|
38
36
|
}
|
package/lib/md051.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { filterByTypesCached } from "./cache.mjs";
|
|
|
7
7
|
// Regular expression for identifying HTML anchor names
|
|
8
8
|
const idRe = getHtmlAttributeRe("id");
|
|
9
9
|
const nameRe = getHtmlAttributeRe("name");
|
|
10
|
-
const anchorRe = /\{(#[a-z\d]+(?:[-_][a-z\d]+)*)\}/gu;
|
|
10
|
+
const anchorRe = /\{(#[a-z\d]+(?:[-_]+[a-z\d]+)*)\}/gu;
|
|
11
11
|
const lineFragmentRe = /^#(?:L\d+(?:C\d+)?-L\d+(?:C\d+)?|L\d+)$/;
|
|
12
12
|
|
|
13
13
|
// Sets for filtering heading tokens during conversion
|
|
@@ -17,8 +17,7 @@ const tokensInclude = new Set(
|
|
|
17
17
|
);
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Converts a Markdown heading into an HTML fragment according to the rules
|
|
21
|
-
* used by GitHub.
|
|
20
|
+
* Converts a Markdown heading into an HTML fragment according to the rules used by GitHub.
|
|
22
21
|
*
|
|
23
22
|
* @param {import("markdownlint").MicromarkToken} headingText Heading text token.
|
|
24
23
|
* @returns {string} Fragment string for heading.
|
|
@@ -141,6 +140,7 @@ export default {
|
|
|
141
140
|
};
|
|
142
141
|
}
|
|
143
142
|
const textLower = text.toLowerCase();
|
|
143
|
+
// eslint-disable-next-line unicorn/prefer-iterator-helpers
|
|
144
144
|
const mixedCaseKey = [ ...fragments.keys() ]
|
|
145
145
|
.find((key) => textLower === key.toLowerCase());
|
|
146
146
|
if (mixedCaseKey) {
|
package/lib/md055.mjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
import { addErrorDetailIf } from "../helpers/helpers.cjs";
|
|
4
|
+
import { getParentOfType } from "../helpers/micromark-helpers.cjs";
|
|
4
5
|
import { filterByTypesCached } from "./cache.mjs";
|
|
5
6
|
|
|
6
7
|
/** @typedef {import("markdownlint").MicromarkToken} MicromarkToken */
|
|
7
|
-
/** @typedef {import("micromark-extension-gfm-table")} */
|
|
8
|
+
/** @typedef {typeof import("micromark-extension-gfm-table")} _ambient_ */
|
|
8
9
|
|
|
9
10
|
const whitespaceTypes = new Set([ "linePrefix", "whitespace" ]);
|
|
10
11
|
const ignoreWhitespace = (/** @type {MicromarkToken[]} */ tokens) => tokens.filter(
|
|
@@ -27,8 +28,17 @@ export default {
|
|
|
27
28
|
((expectedStyle !== "no_leading_or_trailing") && (expectedStyle !== "trailing_only"));
|
|
28
29
|
let expectedTrailingPipe =
|
|
29
30
|
((expectedStyle !== "no_leading_or_trailing") && (expectedStyle !== "leading_only"));
|
|
31
|
+
let currentTable = null;
|
|
30
32
|
const rows = filterByTypesCached([ "tableDelimiterRow", "tableRow" ]);
|
|
31
33
|
for (const row of rows) {
|
|
34
|
+
if (style === "per_table") {
|
|
35
|
+
// Reset style for each new table
|
|
36
|
+
const rowTable = getParentOfType(row, [ "table" ]);
|
|
37
|
+
if (rowTable !== currentTable) {
|
|
38
|
+
expectedStyle = "consistent";
|
|
39
|
+
currentTable = rowTable;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
32
42
|
// The following uses of first/lastOrNothing lack fallback handling
|
|
33
43
|
// because it seems not to be possible (i.e., 0% coverage)
|
|
34
44
|
const firstCell = firstOrNothing(row.children);
|
package/lib/md056.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { filterByTypesCached } from "./cache.mjs";
|
|
|
6
6
|
|
|
7
7
|
const makeRange = (/** @type {number} */ start, /** @type {number} */ end) => [ start, end - start + 1 ];
|
|
8
8
|
|
|
9
|
-
/** @typedef {import("micromark-extension-gfm-table")} */
|
|
9
|
+
/** @typedef {typeof import("micromark-extension-gfm-table")} _ambient_ */
|
|
10
10
|
|
|
11
11
|
/** @type {import("markdownlint").Rule} */
|
|
12
12
|
export default {
|
package/lib/md058.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { addErrorContext, isBlankLine } from "../helpers/helpers.cjs";
|
|
|
4
4
|
import { getBlockQuotePrefixText } from "../helpers/micromark-helpers.cjs";
|
|
5
5
|
import { filterByTypesCached } from "./cache.mjs";
|
|
6
6
|
|
|
7
|
-
/** @typedef {import("micromark-extension-gfm-table")} */
|
|
7
|
+
/** @typedef {typeof import("micromark-extension-gfm-table")} _ambient_ */
|
|
8
8
|
|
|
9
9
|
/** @type {import("markdownlint").Rule} */
|
|
10
10
|
export default {
|