markdownlint 0.34.0 → 0.36.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 +21 -0
- package/README.md +97 -45
- package/demo/markdownlint-browser.js +2186 -2164
- package/doc/CustomRules.md +66 -255
- package/doc/Rules.md +102 -29
- package/doc/md003.md +2 -2
- package/doc/md004.md +4 -3
- package/doc/md034.md +5 -0
- package/doc/md035.md +3 -8
- package/doc/md044.md +10 -0
- package/doc/md046.md +2 -2
- package/doc/md048.md +3 -2
- package/doc/md049.md +4 -3
- package/doc/md050.md +4 -4
- package/doc/md051.md +11 -3
- package/doc/md055.md +3 -2
- package/doc/md058.md +48 -0
- package/helpers/README.md +2 -45
- package/helpers/helpers.js +115 -607
- package/helpers/micromark-helpers.cjs +296 -0
- package/helpers/micromark-parse.cjs +313 -0
- package/helpers/package.json +5 -2
- package/helpers/shared.js +6 -0
- package/helpers/test.cjs +28 -0
- package/lib/cache.js +62 -13
- package/lib/configuration-strict.d.ts +1199 -0
- package/lib/configuration.d.ts +3 -1173
- package/lib/constants.js +3 -2
- package/lib/markdownit.cjs +172 -0
- package/lib/markdownlint.d.ts +103 -56
- package/lib/markdownlint.js +180 -120
- 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 +7 -11
- package/lib/md009.js +50 -44
- package/lib/md010.js +52 -33
- package/lib/md011.js +32 -23
- package/lib/md012.js +17 -9
- package/lib/md013.js +44 -38
- package/lib/md014.js +31 -38
- package/lib/md018.js +17 -9
- package/lib/md019-md021.js +80 -0
- package/lib/md020.js +14 -8
- package/lib/md022.js +13 -29
- 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 +7 -12
- package/lib/md028.js +3 -8
- package/lib/md029.js +40 -34
- package/lib/md030.js +4 -10
- package/lib/md031.js +45 -23
- package/lib/md032.js +33 -31
- package/lib/md033.js +3 -8
- package/lib/md034.js +21 -34
- package/lib/md035.js +2 -7
- package/lib/md036.js +19 -24
- package/lib/md037.js +3 -8
- package/lib/md038.js +11 -22
- package/lib/md039.js +64 -54
- package/lib/md040.js +13 -20
- package/lib/md041.js +5 -5
- package/lib/md042.js +44 -38
- package/lib/md043.js +20 -14
- package/lib/md044.js +34 -36
- package/lib/md045.js +5 -10
- package/lib/md046.js +2 -8
- package/lib/md048.js +30 -23
- package/lib/md049-md050.js +22 -12
- package/lib/md051.js +20 -28
- package/lib/md052.js +2 -2
- package/lib/md053.js +7 -6
- package/lib/md054.js +16 -23
- package/lib/md055.js +41 -49
- package/lib/md056.js +32 -33
- package/lib/md058.js +58 -0
- package/lib/rules.js +11 -4
- package/package.json +23 -20
- package/schema/.markdownlint.jsonc +58 -52
- package/schema/.markdownlint.yaml +56 -51
- package/schema/ValidatingConfiguration.md +6 -8
- package/schema/markdownlint-config-schema-strict.json +1841 -0
- package/schema/markdownlint-config-schema.json +141 -109
- package/style/prettier.json +1 -0
- package/helpers/micromark.cjs +0 -445
- package/lib/md019.js +0 -42
- package/lib/md021.js +0 -65
package/lib/md039.js
CHANGED
|
@@ -2,10 +2,52 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorContext
|
|
5
|
+
const { addErrorContext } = require("../helpers");
|
|
6
|
+
const { getReferenceLinkImageData, filterByTypesCached } = require("./cache");
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Adds an error for a label space issue.
|
|
10
|
+
*
|
|
11
|
+
* @param {import("./markdownlint").RuleOnError} onError Error-reporting callback.
|
|
12
|
+
* @param {import("../helpers/micromark-helpers.cjs").Token} label Label token.
|
|
13
|
+
* @param {import("../helpers/micromark-helpers.cjs").Token} labelText LabelText token.
|
|
14
|
+
* @param {boolean} isStart True iff error is at the start of the link.
|
|
15
|
+
*/
|
|
16
|
+
function addLabelSpaceError(onError, label, labelText, isStart) {
|
|
17
|
+
const match = labelText.text.match(isStart ? /^[^\S\r\n]+/ : /[^\S\r\n]+$/);
|
|
18
|
+
const range = match ?
|
|
19
|
+
[
|
|
20
|
+
(isStart ? (labelText.startColumn) : (labelText.endColumn - match[0].length)),
|
|
21
|
+
match[0].length
|
|
22
|
+
] :
|
|
23
|
+
undefined;
|
|
24
|
+
addErrorContext(
|
|
25
|
+
onError,
|
|
26
|
+
isStart ? (labelText.startLine + (match ? 0 : 1)) : (labelText.endLine - (match ? 0 : 1)),
|
|
27
|
+
label.text.replace(/\s+/g, " "),
|
|
28
|
+
isStart,
|
|
29
|
+
!isStart,
|
|
30
|
+
range,
|
|
31
|
+
range ?
|
|
32
|
+
{
|
|
33
|
+
"editColumn": range[0],
|
|
34
|
+
"deleteCount": range[1]
|
|
35
|
+
} :
|
|
36
|
+
undefined
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Determines if a link is a valid link (and not a fake shortcut link due to parser tricks).
|
|
42
|
+
*
|
|
43
|
+
* @param {import("../helpers/micromark-helpers.cjs").Token} label Label token.
|
|
44
|
+
* @param {import("../helpers/micromark-helpers.cjs").Token} labelText LabelText token.
|
|
45
|
+
* @param {Map<string, any>} definitions Map of link definitions.
|
|
46
|
+
* @returns {boolean} True iff the link is valid.
|
|
47
|
+
*/
|
|
48
|
+
function validLink(label, labelText, definitions) {
|
|
49
|
+
return (label.parent?.children.length !== 1) || definitions.has(labelText.text.trim());
|
|
50
|
+
}
|
|
9
51
|
|
|
10
52
|
// eslint-disable-next-line jsdoc/valid-types
|
|
11
53
|
/** @type import("./markdownlint").Rule */
|
|
@@ -13,59 +55,27 @@ module.exports = {
|
|
|
13
55
|
"names": [ "MD039", "no-space-in-links" ],
|
|
14
56
|
"description": "Spaces inside link text",
|
|
15
57
|
"tags": [ "whitespace", "links" ],
|
|
16
|
-
"parser": "
|
|
58
|
+
"parser": "micromark",
|
|
17
59
|
"function": function MD039(params, onError) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
let range = null;
|
|
36
|
-
let fixInfo = null;
|
|
37
|
-
const match = line.slice(lineIndex).match(spaceInLinkRe);
|
|
38
|
-
if (match) {
|
|
39
|
-
// @ts-ignore
|
|
40
|
-
const column = match.index + lineIndex + 1;
|
|
41
|
-
const length = match[0].length;
|
|
42
|
-
range = [ column, length ];
|
|
43
|
-
fixInfo = {
|
|
44
|
-
"editColumn": column + 1,
|
|
45
|
-
"deleteCount": length - 2,
|
|
46
|
-
"insertText": linkText.trim()
|
|
47
|
-
};
|
|
48
|
-
lineIndex = column + length - 1;
|
|
49
|
-
}
|
|
50
|
-
addErrorContext(
|
|
51
|
-
onError,
|
|
52
|
-
lineNumber,
|
|
53
|
-
`[${linkText}]`,
|
|
54
|
-
left,
|
|
55
|
-
right,
|
|
56
|
-
range,
|
|
57
|
-
fixInfo
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
} else if ((type === "softbreak") || (type === "hardbreak")) {
|
|
61
|
-
lineNumber++;
|
|
62
|
-
lineIndex = 0;
|
|
63
|
-
} else if (inLink) {
|
|
64
|
-
linkText += type.endsWith("_inline") ?
|
|
65
|
-
`${markup}${content}${markup}` :
|
|
66
|
-
(content || markup);
|
|
60
|
+
const { definitions } = getReferenceLinkImageData();
|
|
61
|
+
const labels = filterByTypesCached([ "label" ])
|
|
62
|
+
.filter((label) => label.parent?.type === "link");
|
|
63
|
+
for (const label of labels) {
|
|
64
|
+
const labelTexts = label.children.filter((child) => child.type === "labelText");
|
|
65
|
+
for (const labelText of labelTexts) {
|
|
66
|
+
if (
|
|
67
|
+
(labelText.text.trimStart().length !== labelText.text.length) &&
|
|
68
|
+
validLink(label, labelText, definitions)
|
|
69
|
+
) {
|
|
70
|
+
addLabelSpaceError(onError, label, labelText, true);
|
|
71
|
+
}
|
|
72
|
+
if (
|
|
73
|
+
(labelText.text.trimEnd().length !== labelText.text.length) &&
|
|
74
|
+
validLink(label, labelText, definitions)
|
|
75
|
+
) {
|
|
76
|
+
addLabelSpaceError(onError, label, labelText, false);
|
|
67
77
|
}
|
|
68
78
|
}
|
|
69
|
-
}
|
|
79
|
+
}
|
|
70
80
|
}
|
|
71
81
|
};
|
package/lib/md040.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
5
|
const { addError, addErrorContext } = require("../helpers");
|
|
6
|
-
const {
|
|
7
|
-
|
|
6
|
+
const { getDescendantsByType } = require("../helpers/micromark-helpers.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
8
8
|
|
|
9
9
|
// eslint-disable-next-line jsdoc/valid-types
|
|
10
10
|
/** @type import("./markdownlint").Rule */
|
|
@@ -17,25 +17,18 @@ module.exports = {
|
|
|
17
17
|
let allowed = params.config.allowed_languages;
|
|
18
18
|
allowed = Array.isArray(allowed) ? allowed : [];
|
|
19
19
|
const languageOnly = !!params.config.language_only;
|
|
20
|
-
|
|
21
|
-
/** @type import("../helpers/micromark.cjs").Token[] */
|
|
22
|
-
const micromarkTokens =
|
|
23
|
-
// @ts-ignore
|
|
24
|
-
params.parsers.micromark.tokens;
|
|
25
|
-
const fencedCodes = filterByTypes(micromarkTokens, [ "codeFenced" ]);
|
|
20
|
+
const fencedCodes = filterByTypesCached([ "codeFenced" ]);
|
|
26
21
|
for (const fencedCode of fencedCodes) {
|
|
27
|
-
const openingFence =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
addError(onError, startLine, `Info string contains more than language: "${text}"`);
|
|
38
|
-
}
|
|
22
|
+
const openingFence = getDescendantsByType(fencedCode, [ "codeFencedFence" ])[0];
|
|
23
|
+
const { startLine, text } = openingFence;
|
|
24
|
+
const info = getDescendantsByType(openingFence, [ "codeFencedFenceInfo" ])[0]?.text;
|
|
25
|
+
if (!info) {
|
|
26
|
+
addErrorContext(onError, startLine, text);
|
|
27
|
+
} else if ((allowed.length > 0) && !allowed.includes(info)) {
|
|
28
|
+
addError(onError, startLine, `"${info}" is not allowed`);
|
|
29
|
+
}
|
|
30
|
+
if (languageOnly && getDescendantsByType(openingFence, [ "codeFencedFenceMeta" ]).length > 0) {
|
|
31
|
+
addError(onError, startLine, `Info string contains more than language: "${text}"`);
|
|
39
32
|
}
|
|
40
33
|
}
|
|
41
34
|
}
|
package/lib/md041.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const { addErrorContext, frontMatterHasTitle } = require("../helpers");
|
|
6
6
|
const { filterByTypes, getHeadingLevel, getHtmlTagInfo, isHtmlFlowComment, nonContentTokens } =
|
|
7
|
-
require("../helpers/micromark.cjs");
|
|
7
|
+
require("../helpers/micromark-helpers.cjs");
|
|
8
8
|
|
|
9
9
|
// eslint-disable-next-line jsdoc/valid-types
|
|
10
10
|
/** @type import("./markdownlint").Rule */
|
|
@@ -16,14 +16,14 @@ module.exports = {
|
|
|
16
16
|
"function": function MD041(params, onError) {
|
|
17
17
|
const level = Number(params.config.level || 1);
|
|
18
18
|
if (!frontMatterHasTitle(params.frontMatterLines, params.config.front_matter_title)) {
|
|
19
|
-
params.parsers.micromark.tokens
|
|
20
|
-
filter((token) => !nonContentTokens.has(token.type) && !isHtmlFlowComment(token))
|
|
21
|
-
every((token) => {
|
|
19
|
+
params.parsers.micromark.tokens
|
|
20
|
+
.filter((token) => !nonContentTokens.has(token.type) && !isHtmlFlowComment(token))
|
|
21
|
+
.every((token) => {
|
|
22
22
|
let isError = true;
|
|
23
23
|
if ((token.type === "atxHeading") || (token.type === "setextHeading")) {
|
|
24
24
|
isError = (getHeadingLevel(token) !== level);
|
|
25
25
|
} else if (token.type === "htmlFlow") {
|
|
26
|
-
const htmlTexts = filterByTypes(token.children, [ "htmlText" ]);
|
|
26
|
+
const htmlTexts = filterByTypes(token.children, [ "htmlText" ], true);
|
|
27
27
|
const tagInfo = (htmlTexts.length > 0) && getHtmlTagInfo(htmlTexts[0]);
|
|
28
28
|
isError = !tagInfo || (tagInfo.name.toLowerCase() !== `h${level}`);
|
|
29
29
|
}
|
package/lib/md042.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorContext
|
|
6
|
-
|
|
5
|
+
const { addErrorContext } = require("../helpers");
|
|
6
|
+
const { getDescendantsByType } = require("../helpers/micromark-helpers.cjs");
|
|
7
|
+
const { getReferenceLinkImageData, filterByTypesCached } = require("./cache");
|
|
7
8
|
|
|
8
9
|
// eslint-disable-next-line jsdoc/valid-types
|
|
9
10
|
/** @type import("./markdownlint").Rule */
|
|
@@ -11,43 +12,48 @@ module.exports = {
|
|
|
11
12
|
"names": [ "MD042", "no-empty-links" ],
|
|
12
13
|
"description": "No empty links",
|
|
13
14
|
"tags": [ "links" ],
|
|
14
|
-
"parser": "
|
|
15
|
+
"parser": "micromark",
|
|
15
16
|
"function": function MD042(params, onError) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
emptyLink = false;
|
|
46
|
-
}
|
|
47
|
-
} else if (inLink) {
|
|
48
|
-
linkText += child.content;
|
|
49
|
-
}
|
|
17
|
+
const { definitions } = getReferenceLinkImageData();
|
|
18
|
+
const isReferenceDefinitionHash = (token) => {
|
|
19
|
+
const definition = definitions.get(token.text.trim());
|
|
20
|
+
return (definition && (definition[1] === "#"));
|
|
21
|
+
};
|
|
22
|
+
const links = filterByTypesCached([ "link" ]);
|
|
23
|
+
for (const link of links) {
|
|
24
|
+
const labelText = getDescendantsByType(link, [ "label", "labelText" ]);
|
|
25
|
+
const reference = getDescendantsByType(link, [ "reference" ]);
|
|
26
|
+
const resource = getDescendantsByType(link, [ "resource" ]);
|
|
27
|
+
const referenceString = getDescendantsByType(reference, [ "referenceString" ]);
|
|
28
|
+
const resourceDestinationString = getDescendantsByType(resource, [ "resourceDestination", [ "resourceDestinationLiteral", "resourceDestinationRaw" ], "resourceDestinationString" ]);
|
|
29
|
+
const hasLabelText = labelText.length > 0;
|
|
30
|
+
const hasReference = reference.length > 0;
|
|
31
|
+
const hasResource = resource.length > 0;
|
|
32
|
+
const hasReferenceString = referenceString.length > 0;
|
|
33
|
+
const hasResourceDestinationString = resourceDestinationString.length > 0;
|
|
34
|
+
let error = false;
|
|
35
|
+
if (
|
|
36
|
+
hasLabelText &&
|
|
37
|
+
((!hasReference && !hasResource) || (hasReference && !hasReferenceString))
|
|
38
|
+
) {
|
|
39
|
+
error = isReferenceDefinitionHash(labelText[0]);
|
|
40
|
+
} else if (hasReferenceString && !hasResourceDestinationString) {
|
|
41
|
+
error = isReferenceDefinitionHash(referenceString[0]);
|
|
42
|
+
} else if (!hasReferenceString && hasResourceDestinationString) {
|
|
43
|
+
error = (resourceDestinationString[0].text.trim() === "#");
|
|
44
|
+
} else if (!hasReferenceString && !hasResourceDestinationString) {
|
|
45
|
+
error = true;
|
|
50
46
|
}
|
|
51
|
-
|
|
47
|
+
if (error) {
|
|
48
|
+
addErrorContext(
|
|
49
|
+
onError,
|
|
50
|
+
link.startLine,
|
|
51
|
+
link.text,
|
|
52
|
+
undefined,
|
|
53
|
+
undefined,
|
|
54
|
+
[ link.startColumn, link.endColumn - link.startColumn ]
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
52
58
|
}
|
|
53
59
|
};
|
package/lib/md043.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorContext, addErrorDetailIf
|
|
6
|
-
|
|
5
|
+
const { addErrorContext, addErrorDetailIf } = require("../helpers");
|
|
6
|
+
const { getHeadingLevel, getHeadingText } = require("../helpers/micromark-helpers.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
7
8
|
|
|
8
9
|
// eslint-disable-next-line jsdoc/valid-types
|
|
9
10
|
/** @type import("./markdownlint").Rule */
|
|
@@ -11,7 +12,7 @@ module.exports = {
|
|
|
11
12
|
"names": [ "MD043", "required-headings" ],
|
|
12
13
|
"description": "Required heading structure",
|
|
13
14
|
"tags": [ "headings" ],
|
|
14
|
-
"parser": "
|
|
15
|
+
"parser": "micromark",
|
|
15
16
|
"function": function MD043(params, onError) {
|
|
16
17
|
const requiredHeadings = params.config.headings;
|
|
17
18
|
if (!Array.isArray(requiredHeadings)) {
|
|
@@ -19,20 +20,18 @@ module.exports = {
|
|
|
19
20
|
return;
|
|
20
21
|
}
|
|
21
22
|
const matchCase = params.config.match_case || false;
|
|
22
|
-
const levels = {};
|
|
23
|
-
for (const level of [ 1, 2, 3, 4, 5, 6 ]) {
|
|
24
|
-
levels["h" + level] = "######".substr(-level);
|
|
25
|
-
}
|
|
26
23
|
let i = 0;
|
|
27
24
|
let matchAny = false;
|
|
28
25
|
let hasError = false;
|
|
29
26
|
let anyHeadings = false;
|
|
30
27
|
const getExpected = () => requiredHeadings[i++] || "[None]";
|
|
31
28
|
const handleCase = (str) => (matchCase ? str : str.toLowerCase());
|
|
32
|
-
|
|
29
|
+
for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
|
|
33
30
|
if (!hasError) {
|
|
31
|
+
const headingText = getHeadingText(heading);
|
|
32
|
+
const headingLevel = getHeadingLevel(heading);
|
|
34
33
|
anyHeadings = true;
|
|
35
|
-
const actual =
|
|
34
|
+
const actual = `${"".padEnd(headingLevel, "#")} ${headingText}`;
|
|
36
35
|
const expected = getExpected();
|
|
37
36
|
if (expected === "*") {
|
|
38
37
|
const nextExpected = getExpected();
|
|
@@ -47,12 +46,16 @@ module.exports = {
|
|
|
47
46
|
} else if (matchAny) {
|
|
48
47
|
i--;
|
|
49
48
|
} else {
|
|
50
|
-
addErrorDetailIf(
|
|
51
|
-
|
|
49
|
+
addErrorDetailIf(
|
|
50
|
+
onError,
|
|
51
|
+
heading.startLine,
|
|
52
|
+
expected,
|
|
53
|
+
actual
|
|
54
|
+
);
|
|
52
55
|
hasError = true;
|
|
53
56
|
}
|
|
54
57
|
}
|
|
55
|
-
}
|
|
58
|
+
}
|
|
56
59
|
const extraHeadings = requiredHeadings.length - i;
|
|
57
60
|
if (
|
|
58
61
|
!hasError &&
|
|
@@ -60,8 +63,11 @@ module.exports = {
|
|
|
60
63
|
((extraHeadings === 1) && (requiredHeadings[i] !== "*"))) &&
|
|
61
64
|
(anyHeadings || !requiredHeadings.every((heading) => heading === "*"))
|
|
62
65
|
) {
|
|
63
|
-
addErrorContext(
|
|
64
|
-
|
|
66
|
+
addErrorContext(
|
|
67
|
+
onError,
|
|
68
|
+
params.lines.length,
|
|
69
|
+
requiredHeadings[i]
|
|
70
|
+
);
|
|
65
71
|
}
|
|
66
72
|
}
|
|
67
73
|
};
|
package/lib/md044.js
CHANGED
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorDetailIf, escapeForRegExp,
|
|
6
|
-
|
|
7
|
-
const {
|
|
8
|
-
require("../helpers/micromark.cjs");
|
|
5
|
+
const { addErrorDetailIf, escapeForRegExp, hasOverlap } = require("../helpers");
|
|
6
|
+
const { filterByPredicate, filterByTypes } = require("../helpers/micromark-helpers.cjs");
|
|
7
|
+
const { parse } = require("../helpers/micromark-parse.cjs");
|
|
9
8
|
|
|
10
9
|
const ignoredChildTypes = new Set(
|
|
11
10
|
[ "codeFencedFence", "definition", "reference", "resource" ]
|
|
@@ -32,11 +31,6 @@ module.exports = {
|
|
|
32
31
|
const htmlElements = params.config.html_elements;
|
|
33
32
|
const includeHtmlElements =
|
|
34
33
|
(htmlElements === undefined) ? true : !!htmlElements;
|
|
35
|
-
// eslint-disable-next-line jsdoc/valid-types
|
|
36
|
-
/** @type import("../helpers/micromark.cjs").Token[] */
|
|
37
|
-
const micromarkTokens =
|
|
38
|
-
// @ts-ignore
|
|
39
|
-
params.parsers.micromark.tokens;
|
|
40
34
|
const scannedTypes = new Set([ "data" ]);
|
|
41
35
|
if (includeCodeBlocks) {
|
|
42
36
|
scannedTypes.add("codeFlowValue");
|
|
@@ -48,56 +42,60 @@ module.exports = {
|
|
|
48
42
|
}
|
|
49
43
|
const contentTokens =
|
|
50
44
|
filterByPredicate(
|
|
51
|
-
|
|
45
|
+
params.parsers.micromark.tokens,
|
|
52
46
|
(token) => scannedTypes.has(token.type),
|
|
53
47
|
(token) => (
|
|
54
48
|
token.children.filter((t) => !ignoredChildTypes.has(t.type))
|
|
55
49
|
)
|
|
56
50
|
);
|
|
51
|
+
/** @type {import("../helpers").FileRange[]} */
|
|
57
52
|
const exclusions = [];
|
|
58
|
-
const
|
|
53
|
+
const scannedTokens = new Set();
|
|
59
54
|
for (const name of names) {
|
|
60
55
|
const escapedName = escapeForRegExp(name);
|
|
61
56
|
const startNamePattern = /^\W/.test(name) ? "" : "\\b_*";
|
|
62
57
|
const endNamePattern = /\W$/.test(name) ? "" : "_*\\b";
|
|
63
|
-
const namePattern =
|
|
64
|
-
`(${startNamePattern})(${escapedName})${endNamePattern}`;
|
|
58
|
+
const namePattern = `(${startNamePattern})(${escapedName})${endNamePattern}`;
|
|
65
59
|
const nameRe = new RegExp(namePattern, "gi");
|
|
66
60
|
for (const token of contentTokens) {
|
|
67
61
|
let match = null;
|
|
68
62
|
while ((match = nameRe.exec(token.text)) !== null) {
|
|
69
63
|
const [ , leftMatch, nameMatch ] = match;
|
|
70
|
-
const
|
|
64
|
+
const column = token.startColumn + match.index + leftMatch.length;
|
|
71
65
|
const length = nameMatch.length;
|
|
72
|
-
const
|
|
66
|
+
const lineNumber = token.startLine;
|
|
67
|
+
/** @type {import("../helpers").FileRange} */
|
|
68
|
+
const nameRange = {
|
|
69
|
+
"startLine": lineNumber,
|
|
70
|
+
"startColumn": column,
|
|
71
|
+
"endLine": lineNumber,
|
|
72
|
+
"endColumn": column + length - 1
|
|
73
|
+
};
|
|
73
74
|
if (
|
|
74
|
-
!
|
|
75
|
-
!
|
|
75
|
+
!names.includes(nameMatch) &&
|
|
76
|
+
!exclusions.some((exclusion) => hasOverlap(exclusion, nameRange))
|
|
76
77
|
) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
token.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
);
|
|
89
|
-
exclusions.push(...urlRanges);
|
|
90
|
-
autoLinked.add(token);
|
|
78
|
+
/** @type {import("../helpers").FileRange[]} */
|
|
79
|
+
let autolinkRanges = [];
|
|
80
|
+
if (!scannedTokens.has(token)) {
|
|
81
|
+
autolinkRanges = filterByTypes(parse(token.text), [ "literalAutolink" ])
|
|
82
|
+
.map((tok) => ({
|
|
83
|
+
"startLine": lineNumber,
|
|
84
|
+
"startColumn": token.startColumn + tok.startColumn - 1,
|
|
85
|
+
"endLine": lineNumber,
|
|
86
|
+
"endColumn": token.endColumn + tok.endColumn - 1
|
|
87
|
+
}));
|
|
88
|
+
exclusions.push(...autolinkRanges);
|
|
89
|
+
scannedTokens.add(token);
|
|
91
90
|
}
|
|
92
|
-
if (!
|
|
93
|
-
const column = index + 1;
|
|
91
|
+
if (!autolinkRanges.some((autolinkRange) => hasOverlap(autolinkRange, nameRange))) {
|
|
94
92
|
addErrorDetailIf(
|
|
95
93
|
onError,
|
|
96
94
|
token.startLine,
|
|
97
95
|
name,
|
|
98
96
|
nameMatch,
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
undefined,
|
|
98
|
+
undefined,
|
|
101
99
|
[ column, length ],
|
|
102
100
|
{
|
|
103
101
|
"editColumn": column,
|
|
@@ -107,7 +105,7 @@ module.exports = {
|
|
|
107
105
|
);
|
|
108
106
|
}
|
|
109
107
|
}
|
|
110
|
-
exclusions.push(
|
|
108
|
+
exclusions.push(nameRange);
|
|
111
109
|
}
|
|
112
110
|
}
|
|
113
111
|
}
|
package/lib/md045.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
5
|
const { addError, getHtmlAttributeRe, nextLinesRe } = require("../helpers");
|
|
6
|
-
const {
|
|
6
|
+
const { getHtmlTagInfo, getDescendantsByType } = require("../helpers/micromark-helpers.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
7
8
|
|
|
8
9
|
const altRe = getHtmlAttributeRe("alt");
|
|
9
10
|
|
|
@@ -15,16 +16,10 @@ module.exports = {
|
|
|
15
16
|
"tags": [ "accessibility", "images" ],
|
|
16
17
|
"parser": "micromark",
|
|
17
18
|
"function": function MD045(params, onError) {
|
|
18
|
-
// eslint-disable-next-line jsdoc/valid-types
|
|
19
|
-
/** @type import("../helpers/micromark.cjs").Token[] */
|
|
20
|
-
const micromarkTokens =
|
|
21
|
-
// @ts-ignore
|
|
22
|
-
params.parsers.micromark.tokens;
|
|
23
|
-
|
|
24
19
|
// Process Markdown images
|
|
25
|
-
const images =
|
|
20
|
+
const images = filterByTypesCached([ "image" ]);
|
|
26
21
|
for (const image of images) {
|
|
27
|
-
const labelTexts =
|
|
22
|
+
const labelTexts = getDescendantsByType(image, [ "label", "labelText" ]);
|
|
28
23
|
if (labelTexts.some((labelText) => labelText.text.length === 0)) {
|
|
29
24
|
const range = (image.startLine === image.endLine) ?
|
|
30
25
|
[ image.startColumn, image.endColumn - image.startColumn ] :
|
|
@@ -40,7 +35,7 @@ module.exports = {
|
|
|
40
35
|
}
|
|
41
36
|
|
|
42
37
|
// Process HTML images
|
|
43
|
-
const htmlTexts =
|
|
38
|
+
const htmlTexts = filterByTypesCached([ "htmlText" ], true);
|
|
44
39
|
for (const htmlText of htmlTexts) {
|
|
45
40
|
const { startColumn, startLine, text } = htmlText;
|
|
46
41
|
const htmlTagInfo = getHtmlTagInfo(htmlText);
|
package/lib/md046.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
|
const tokenTypeToStyle = {
|
|
9
9
|
"codeFenced": "fenced",
|
|
@@ -19,13 +19,7 @@ module.exports = {
|
|
|
19
19
|
"parser": "micromark",
|
|
20
20
|
"function": function MD046(params, onError) {
|
|
21
21
|
let expectedStyle = String(params.config.style || "consistent");
|
|
22
|
-
|
|
23
|
-
/** @type import("../helpers/micromark.cjs").Token[] */
|
|
24
|
-
const micromarkTokens =
|
|
25
|
-
// @ts-ignore
|
|
26
|
-
params.parsers.micromark.tokens;
|
|
27
|
-
const codeBlocksAndFences = filterByTypes(micromarkTokens, [ "codeFenced", "codeIndented" ]);
|
|
28
|
-
for (const token of codeBlocksAndFences) {
|
|
22
|
+
for (const token of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
|
|
29
23
|
const { startLine, type } = token;
|
|
30
24
|
if (expectedStyle === "consistent") {
|
|
31
25
|
expectedStyle = tokenTypeToStyle[type];
|
package/lib/md048.js
CHANGED
|
@@ -2,8 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorDetailIf
|
|
6
|
-
const {
|
|
5
|
+
const { addErrorDetailIf } = require("../helpers");
|
|
6
|
+
const { getDescendantsByType } = require("../helpers/micromark-helpers.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Return the string representation of a fence markup character.
|
|
11
|
+
*
|
|
12
|
+
* @param {string} markup Fence string.
|
|
13
|
+
* @returns {"tilde" | "backtick"} String representation.
|
|
14
|
+
*/
|
|
15
|
+
function fencedCodeBlockStyleFor(markup) {
|
|
16
|
+
switch (markup[0]) {
|
|
17
|
+
case "~":
|
|
18
|
+
return "tilde";
|
|
19
|
+
default:
|
|
20
|
+
return "backtick";
|
|
21
|
+
}
|
|
22
|
+
};
|
|
7
23
|
|
|
8
24
|
// eslint-disable-next-line jsdoc/valid-types
|
|
9
25
|
/** @type import("./markdownlint").Rule */
|
|
@@ -14,30 +30,21 @@ module.exports = {
|
|
|
14
30
|
"parser": "micromark",
|
|
15
31
|
"function": function MD048(params, onError) {
|
|
16
32
|
const style = String(params.config.style || "consistent");
|
|
17
|
-
// eslint-disable-next-line jsdoc/valid-types
|
|
18
|
-
/** @type import("../helpers/micromark.cjs").Token[] */
|
|
19
|
-
const micromarkTokens =
|
|
20
|
-
// @ts-ignore
|
|
21
|
-
params.parsers.micromark.tokens;
|
|
22
33
|
let expectedStyle = style;
|
|
23
|
-
const codeFenceds =
|
|
34
|
+
const codeFenceds = filterByTypesCached([ "codeFenced" ]);
|
|
24
35
|
for (const codeFenced of codeFenceds) {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (expectedStyle === "consistent") {
|
|
31
|
-
expectedStyle = fencedCodeBlockStyleFor(text);
|
|
32
|
-
}
|
|
33
|
-
addErrorDetailIf(
|
|
34
|
-
onError,
|
|
35
|
-
startLine,
|
|
36
|
-
expectedStyle,
|
|
37
|
-
fencedCodeBlockStyleFor(text)
|
|
38
|
-
);
|
|
39
|
-
}
|
|
36
|
+
const codeFencedFenceSequence =
|
|
37
|
+
getDescendantsByType(codeFenced, [ "codeFencedFence", "codeFencedFenceSequence" ])[0];
|
|
38
|
+
const { startLine, text } = codeFencedFenceSequence;
|
|
39
|
+
if (expectedStyle === "consistent") {
|
|
40
|
+
expectedStyle = fencedCodeBlockStyleFor(text);
|
|
40
41
|
}
|
|
42
|
+
addErrorDetailIf(
|
|
43
|
+
onError,
|
|
44
|
+
startLine,
|
|
45
|
+
expectedStyle,
|
|
46
|
+
fencedCodeBlockStyleFor(text)
|
|
47
|
+
);
|
|
41
48
|
}
|
|
42
49
|
}
|
|
43
50
|
};
|