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.
Files changed (82) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/CONTRIBUTING.md +4 -3
  3. package/README.md +25 -10
  4. package/demo/markdownlint-browser.js +3286 -2119
  5. package/doc/CustomRules.md +25 -13
  6. package/doc/Rules.md +106 -7
  7. package/doc/md013.md +8 -7
  8. package/doc/md034.md +5 -0
  9. package/doc/md044.md +10 -0
  10. package/doc/md049.md +3 -0
  11. package/doc/md050.md +3 -0
  12. package/doc/md051.md +26 -0
  13. package/doc/md058.md +48 -0
  14. package/helpers/README.md +3 -27
  15. package/helpers/helpers.js +60 -292
  16. package/helpers/micromark.cjs +153 -36
  17. package/helpers/package.json +2 -2
  18. package/lib/cache.js +62 -13
  19. package/lib/configuration-strict.d.ts +1185 -0
  20. package/lib/configuration.d.ts +3 -1173
  21. package/lib/constants.js +3 -2
  22. package/lib/markdownlint.d.ts +143 -61
  23. package/lib/markdownlint.js +151 -54
  24. package/lib/md001.js +17 -8
  25. package/lib/md003.js +16 -8
  26. package/lib/md004.js +57 -47
  27. package/lib/md005.js +7 -8
  28. package/lib/md007.js +13 -12
  29. package/lib/md009.js +52 -43
  30. package/lib/md010.js +50 -32
  31. package/lib/md011.js +18 -7
  32. package/lib/md012.js +19 -8
  33. package/lib/md013.js +46 -37
  34. package/lib/md014.js +37 -36
  35. package/lib/md018.js +20 -8
  36. package/lib/md019-md021.js +80 -0
  37. package/lib/md020.js +15 -6
  38. package/lib/md022.js +10 -12
  39. package/lib/md023.js +24 -22
  40. package/lib/md024.js +14 -8
  41. package/lib/md025.js +17 -9
  42. package/lib/md026.js +5 -6
  43. package/lib/md027.js +24 -43
  44. package/lib/md028.js +26 -14
  45. package/lib/md029.js +42 -33
  46. package/lib/md030.js +7 -8
  47. package/lib/md031.js +45 -22
  48. package/lib/md032.js +18 -30
  49. package/lib/md033.js +6 -4
  50. package/lib/md034.js +30 -9
  51. package/lib/md035.js +5 -3
  52. package/lib/md036.js +34 -39
  53. package/lib/md037.js +3 -0
  54. package/lib/md038.js +6 -5
  55. package/lib/md039.js +68 -52
  56. package/lib/md040.js +21 -21
  57. package/lib/md041.js +21 -26
  58. package/lib/md042.js +50 -36
  59. package/lib/md043.js +22 -13
  60. package/lib/md044.js +5 -2
  61. package/lib/md045.js +6 -4
  62. package/lib/md046.js +9 -8
  63. package/lib/md047.js +5 -2
  64. package/lib/md048.js +22 -13
  65. package/lib/md049-md050.js +19 -5
  66. package/lib/md051.js +18 -20
  67. package/lib/md052.js +5 -2
  68. package/lib/md053.js +5 -2
  69. package/lib/md054.js +12 -12
  70. package/lib/md055.js +10 -3
  71. package/lib/md056.js +18 -6
  72. package/lib/md058.js +43 -0
  73. package/lib/rules.js +11 -4
  74. package/package.json +22 -25
  75. package/schema/.markdownlint.jsonc +54 -51
  76. package/schema/.markdownlint.yaml +53 -50
  77. package/schema/ValidatingConfiguration.md +6 -8
  78. package/schema/markdownlint-config-schema-strict.json +1819 -0
  79. package/schema/markdownlint-config-schema.json +115 -105
  80. package/style/prettier.json +1 -0
  81. package/lib/md019.js +0 -39
  82. package/lib/md021.js +0 -62
package/lib/md037.js CHANGED
@@ -5,10 +5,13 @@
5
5
  const { addError } = require("../helpers");
6
6
  const { filterByPredicate, inHtmlFlow } = require("../helpers/micromark.cjs");
7
7
 
8
+ // eslint-disable-next-line jsdoc/valid-types
9
+ /** @type import("./markdownlint").Rule */
8
10
  module.exports = {
9
11
  "names": [ "MD037", "no-space-in-emphasis" ],
10
12
  "description": "Spaces inside emphasis markers",
11
13
  "tags": [ "whitespace", "emphasis" ],
14
+ "parser": "micromark",
12
15
  "function": function MD037(params, onError) {
13
16
 
14
17
  // Initialize variables
package/lib/md038.js CHANGED
@@ -3,8 +3,8 @@
3
3
  "use strict";
4
4
 
5
5
  const { addErrorContext } = require("../helpers");
6
- const { filterByTypes, inHtmlFlow, tokenIfType } =
7
- require("../helpers/micromark.cjs");
6
+ const { tokenIfType } = require("../helpers/micromark.cjs");
7
+ const { filterByTypesCached } = require("./cache");
8
8
 
9
9
  const leftSpaceRe = /^\s(?:[^`]|$)/;
10
10
  const rightSpaceRe = /[^`]\s$/;
@@ -19,14 +19,15 @@ const trimCodeText = (text, start, end) => {
19
19
  return text;
20
20
  };
21
21
 
22
+ // eslint-disable-next-line jsdoc/valid-types
23
+ /** @type import("./markdownlint").Rule */
22
24
  module.exports = {
23
25
  "names": [ "MD038", "no-space-in-code" ],
24
26
  "description": "Spaces inside code span elements",
25
27
  "tags": [ "whitespace", "code" ],
28
+ "parser": "micromark",
26
29
  "function": function MD038(params, onError) {
27
- const codeTexts =
28
- filterByTypes(params.parsers.micromark.tokens, [ "codeText" ])
29
- .filter((codeText) => !inHtmlFlow(codeText));
30
+ const codeTexts = filterByTypesCached([ "codeText" ]);
30
31
  for (const codeText of codeTexts) {
31
32
  const { children } = codeText;
32
33
  const first = 0;
package/lib/md039.js CHANGED
@@ -2,66 +2,82 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorContext, filterTokens } = require("../helpers");
5
+ const { addErrorContext } = require("../helpers");
6
+ const { filterByTypes } = require("../helpers/micromark.cjs");
7
+ const { getReferenceLinkImageData, filterByTypesCached } = require("./cache");
6
8
 
7
- const spaceInLinkRe =
8
- /\[(?:\s[^\]]*|[^\]]*?\s)\](?=(\([^)]*\)|\[[^\]]*\]))/;
9
+ /**
10
+ * Adds an error for a label space issue.
11
+ *
12
+ * @param {import("./markdownlint").RuleOnError} onError Error-reporting callback.
13
+ * @param {import("../helpers/micromark.cjs").Token} label Label token.
14
+ * @param {import("../helpers/micromark.cjs").Token} labelText LabelText token.
15
+ * @param {boolean} isStart True iff error is at the start of the link.
16
+ */
17
+ function addLabelSpaceError(onError, label, labelText, isStart) {
18
+ const match = labelText.text.match(isStart ? /^[^\S\r\n]+/ : /[^\S\r\n]+$/);
19
+ const range = match ?
20
+ [
21
+ (isStart ? (labelText.startColumn) : (labelText.endColumn - match[0].length)),
22
+ match[0].length
23
+ ] :
24
+ undefined;
25
+ addErrorContext(
26
+ onError,
27
+ isStart ? (labelText.startLine + (match ? 0 : 1)) : (labelText.endLine - (match ? 0 : 1)),
28
+ label.text.replace(/\s+/g, " "),
29
+ isStart,
30
+ !isStart,
31
+ range,
32
+ range ? {
33
+ "editColumn": range[0],
34
+ "deleteCount": range[1]
35
+ } : undefined
36
+ );
37
+ }
9
38
 
39
+ /**
40
+ * Determines if a link is a valid link (and not a fake shortcut link due to parser tricks).
41
+ *
42
+ * @param {import("../helpers/micromark.cjs").Token} label Label token.
43
+ * @param {import("../helpers/micromark.cjs").Token} labelText LabelText token.
44
+ * @param {Map<string, any>} definitions Map of link definitions.
45
+ * @returns {boolean} True iff the link is valid.
46
+ */
47
+ function validLink(label, labelText, definitions) {
48
+ return (label.parent?.children.length !== 1) || definitions.has(labelText.text.trim());
49
+ }
50
+
51
+ // eslint-disable-next-line jsdoc/valid-types
52
+ /** @type import("./markdownlint").Rule */
10
53
  module.exports = {
11
54
  "names": [ "MD039", "no-space-in-links" ],
12
55
  "description": "Spaces inside link text",
13
56
  "tags": [ "whitespace", "links" ],
57
+ "parser": "micromark",
14
58
  "function": function MD039(params, onError) {
15
- filterTokens(params, "inline", (token) => {
16
- const { children } = token;
17
- let { lineNumber } = token;
18
- let inLink = false;
19
- let linkText = "";
20
- let lineIndex = 0;
21
- for (const child of children) {
22
- const { content, markup, type } = child;
23
- if (type === "link_open") {
24
- inLink = true;
25
- linkText = "";
26
- } else if (type === "link_close") {
27
- inLink = false;
28
- const left = linkText.trimStart().length !== linkText.length;
29
- const right = linkText.trimEnd().length !== linkText.length;
30
- if (left || right) {
31
- const line = params.lines[lineNumber - 1];
32
- let range = null;
33
- let fixInfo = null;
34
- const match = line.slice(lineIndex).match(spaceInLinkRe);
35
- if (match) {
36
- const column = match.index + lineIndex + 1;
37
- const length = match[0].length;
38
- range = [ column, length ];
39
- fixInfo = {
40
- "editColumn": column + 1,
41
- "deleteCount": length - 2,
42
- "insertText": linkText.trim()
43
- };
44
- lineIndex = column + length - 1;
45
- }
46
- addErrorContext(
47
- onError,
48
- lineNumber,
49
- `[${linkText}]`,
50
- left,
51
- right,
52
- range,
53
- fixInfo
54
- );
55
- }
56
- } else if ((type === "softbreak") || (type === "hardbreak")) {
57
- lineNumber++;
58
- lineIndex = 0;
59
- } else if (inLink) {
60
- linkText += type.endsWith("_inline") ?
61
- `${markup}${content}${markup}` :
62
- (content || markup);
59
+ const { definitions } = getReferenceLinkImageData();
60
+ const labels = filterByTypesCached([ "label" ]).
61
+ filter((label) => label.parent?.type === "link");
62
+ for (const label of labels) {
63
+ const labelTexts = filterByTypes(
64
+ label.children,
65
+ [ "labelText" ]
66
+ );
67
+ for (const labelText of labelTexts) {
68
+ if (
69
+ (labelText.text.trimStart().length !== labelText.text.length) &&
70
+ validLink(label, labelText, definitions)
71
+ ) {
72
+ addLabelSpaceError(onError, label, labelText, true);
73
+ }
74
+ if (
75
+ (labelText.text.trimEnd().length !== labelText.text.length) &&
76
+ validLink(label, labelText, definitions)
77
+ ) {
78
+ addLabelSpaceError(onError, label, labelText, false);
63
79
  }
64
80
  }
65
- });
81
+ }
66
82
  }
67
83
  };
package/lib/md040.js CHANGED
@@ -2,36 +2,36 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addError, addErrorContext, filterTokens } = require("../helpers");
5
+ const { addError, addErrorContext } = require("../helpers");
6
+ const { getTokenTextByType, tokenIfType } = 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": [ "MD040", "fenced-code-language" ],
9
13
  "description": "Fenced code blocks should have a language specified",
10
14
  "tags": [ "code", "language" ],
15
+ "parser": "micromark",
11
16
  "function": function MD040(params, onError) {
12
17
  let allowed = params.config.allowed_languages;
13
18
  allowed = Array.isArray(allowed) ? allowed : [];
14
19
  const languageOnly = !!params.config.language_only;
15
-
16
- filterTokens(params, "fence", function forToken(token) {
17
- const lang = token.info.trim().split(/\s+/u).shift();
18
- if (lang === "") {
19
- addErrorContext(onError, token.lineNumber, token.line);
20
- } else if ((allowed.length > 0) && !allowed.includes(lang)) {
21
- addError(
22
- onError,
23
- token.lineNumber,
24
- `"${lang}" is not allowed`
25
- );
26
- }
27
-
28
- if (languageOnly && (token.info !== lang)) {
29
- addError(
30
- onError,
31
- token.lineNumber,
32
- `Info string contains more than language: "${token.info}"`
33
- );
20
+ const fencedCodes = filterByTypesCached([ "codeFenced" ]);
21
+ for (const fencedCode of fencedCodes) {
22
+ const openingFence = tokenIfType(fencedCode.children[0], "codeFencedFence");
23
+ if (openingFence) {
24
+ const { children, startLine, text } = openingFence;
25
+ const info = getTokenTextByType(children, "codeFencedFenceInfo");
26
+ if (!info) {
27
+ addErrorContext(onError, startLine, text);
28
+ } else if ((allowed.length > 0) && !allowed.includes(info)) {
29
+ addError(onError, startLine, `"${info}" is not allowed`);
30
+ }
31
+ if (languageOnly && getTokenTextByType(children, "codeFencedFenceMeta")) {
32
+ addError(onError, startLine, `Info string contains more than language: "${text}"`);
33
+ }
34
34
  }
35
- });
35
+ }
36
36
  }
37
37
  };
package/lib/md041.js CHANGED
@@ -3,40 +3,35 @@
3
3
  "use strict";
4
4
 
5
5
  const { addErrorContext, frontMatterHasTitle } = require("../helpers");
6
+ const { filterByTypes, getHeadingLevel, getHtmlTagInfo, isHtmlFlowComment, nonContentTokens } =
7
+ require("../helpers/micromark.cjs");
6
8
 
9
+ // eslint-disable-next-line jsdoc/valid-types
10
+ /** @type import("./markdownlint").Rule */
7
11
  module.exports = {
8
12
  "names": [ "MD041", "first-line-heading", "first-line-h1" ],
9
13
  "description": "First line in a file should be a top-level heading",
10
14
  "tags": [ "headings" ],
15
+ "parser": "micromark",
11
16
  "function": function MD041(params, onError) {
12
17
  const level = Number(params.config.level || 1);
13
- const tag = "h" + level;
14
- const foundFrontMatterTitle =
15
- frontMatterHasTitle(
16
- params.frontMatterLines,
17
- params.config.front_matter_title
18
- );
19
- if (!foundFrontMatterTitle) {
20
- const htmlHeadingRe = new RegExp(`^<h${level}[ />]`, "i");
21
- params.parsers.markdownit.tokens.every((token) => {
22
- let isError = false;
23
- if (token.type === "html_block") {
24
- if (token.content.startsWith("<!--")) {
25
- // Ignore leading HTML comments
26
- return true;
27
- } else if (!htmlHeadingRe.test(token.content)) {
28
- // Something other than an HTML heading
29
- isError = true;
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) => {
22
+ let isError = true;
23
+ if ((token.type === "atxHeading") || (token.type === "setextHeading")) {
24
+ isError = (getHeadingLevel(token) !== level);
25
+ } else if (token.type === "htmlFlow") {
26
+ const htmlTexts = filterByTypes(token.children, [ "htmlText" ], true);
27
+ const tagInfo = (htmlTexts.length > 0) && getHtmlTagInfo(htmlTexts[0]);
28
+ isError = !tagInfo || (tagInfo.name.toLowerCase() !== `h${level}`);
30
29
  }
31
- } else if ((token.type !== "heading_open") || (token.tag !== tag)) {
32
- // Something other than a Markdown heading
33
- isError = true;
34
- }
35
- if (isError) {
36
- addErrorContext(onError, token.lineNumber, token.line);
37
- }
38
- return false;
39
- });
30
+ if (isError) {
31
+ addErrorContext(onError, token.startLine, params.lines[token.startLine - 1]);
32
+ }
33
+ return false;
34
+ });
40
35
  }
41
36
  }
42
37
  };
package/lib/md042.js CHANGED
@@ -2,48 +2,62 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorContext, escapeForRegExp, filterTokens } =
6
- require("../helpers");
5
+ const { addErrorContext } = require("../helpers");
6
+ const { getDescendantsByType } = require("../helpers/micromark.cjs");
7
+ const { getReferenceLinkImageData, 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": [ "MD042", "no-empty-links" ],
10
13
  "description": "No empty links",
11
14
  "tags": [ "links" ],
15
+ "parser": "micromark",
12
16
  "function": function MD042(params, onError) {
13
- filterTokens(params, "inline", function forToken(token) {
14
- let inLink = false;
15
- let linkText = "";
16
- let emptyLink = false;
17
- for (const child of token.children) {
18
- if (child.type === "link_open") {
19
- inLink = true;
20
- linkText = "";
21
- for (const attr of child.attrs) {
22
- if (attr[0] === "href" && (!attr[1] || (attr[1] === "#"))) {
23
- emptyLink = true;
24
- }
25
- }
26
- } else if (child.type === "link_close") {
27
- inLink = false;
28
- if (emptyLink) {
29
- let context = `[${linkText}]`;
30
- let range = null;
31
- const match = child.line.match(
32
- new RegExp(`${escapeForRegExp(context)}\\((?:|#|<>)\\)`)
33
- );
34
- if (match) {
35
- context = match[0];
36
- range = [ match.index + 1, match[0].length ];
37
- }
38
- addErrorContext(
39
- onError, child.lineNumber, context, null, null, range
40
- );
41
- emptyLink = false;
42
- }
43
- } else if (inLink) {
44
- linkText += child.content;
45
- }
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 resourceDestination = getDescendantsByType(resource, [ "resourceDestination" ]);
29
+ const resourceDestinationString = [
30
+ ...getDescendantsByType(resourceDestination, [ "resourceDestinationRaw", "resourceDestinationString" ]),
31
+ ...getDescendantsByType(resourceDestination, [ "resourceDestinationLiteral", "resourceDestinationString" ])
32
+ ];
33
+ const hasLabelText = labelText.length > 0;
34
+ const hasReference = reference.length > 0;
35
+ const hasResource = resource.length > 0;
36
+ const hasReferenceString = referenceString.length > 0;
37
+ const hasResourceDestinationString = resourceDestinationString.length > 0;
38
+ let error = false;
39
+ if (
40
+ hasLabelText &&
41
+ ((!hasReference && !hasResource) || (hasReference && !hasReferenceString))
42
+ ) {
43
+ error = isReferenceDefinitionHash(labelText[0]);
44
+ } else if (hasReferenceString && !hasResourceDestinationString) {
45
+ error = isReferenceDefinitionHash(referenceString[0]);
46
+ } else if (!hasReferenceString && hasResourceDestinationString) {
47
+ error = (resourceDestinationString[0].text.trim() === "#");
48
+ } else if (!hasReferenceString && !hasResourceDestinationString) {
49
+ error = true;
46
50
  }
47
- });
51
+ if (error) {
52
+ addErrorContext(
53
+ onError,
54
+ link.startLine,
55
+ link.text,
56
+ undefined,
57
+ undefined,
58
+ [ link.startColumn, link.endColumn - link.startColumn ]
59
+ );
60
+ }
61
+ }
48
62
  }
49
63
  };
package/lib/md043.js CHANGED
@@ -2,13 +2,17 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorContext, addErrorDetailIf, forEachHeading } =
6
- require("../helpers");
5
+ const { addErrorContext, addErrorDetailIf } = require("../helpers");
6
+ const { getHeadingLevel, getHeadingText } = 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": [ "MD043", "required-headings" ],
10
13
  "description": "Required heading structure",
11
14
  "tags": [ "headings" ],
15
+ "parser": "micromark",
12
16
  "function": function MD043(params, onError) {
13
17
  const requiredHeadings = params.config.headings;
14
18
  if (!Array.isArray(requiredHeadings)) {
@@ -16,20 +20,18 @@ module.exports = {
16
20
  return;
17
21
  }
18
22
  const matchCase = params.config.match_case || false;
19
- const levels = {};
20
- for (const level of [ 1, 2, 3, 4, 5, 6 ]) {
21
- levels["h" + level] = "######".substr(-level);
22
- }
23
23
  let i = 0;
24
24
  let matchAny = false;
25
25
  let hasError = false;
26
26
  let anyHeadings = false;
27
27
  const getExpected = () => requiredHeadings[i++] || "[None]";
28
28
  const handleCase = (str) => (matchCase ? str : str.toLowerCase());
29
- forEachHeading(params, (heading, content) => {
29
+ for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
30
30
  if (!hasError) {
31
+ const headingText = getHeadingText(heading);
32
+ const headingLevel = getHeadingLevel(heading);
31
33
  anyHeadings = true;
32
- const actual = levels[heading.tag] + " " + content;
34
+ const actual = `${"".padEnd(headingLevel, "#")} ${headingText}`;
33
35
  const expected = getExpected();
34
36
  if (expected === "*") {
35
37
  const nextExpected = getExpected();
@@ -44,12 +46,16 @@ module.exports = {
44
46
  } else if (matchAny) {
45
47
  i--;
46
48
  } else {
47
- addErrorDetailIf(onError, heading.lineNumber,
48
- expected, actual);
49
+ addErrorDetailIf(
50
+ onError,
51
+ heading.startLine,
52
+ expected,
53
+ actual
54
+ );
49
55
  hasError = true;
50
56
  }
51
57
  }
52
- });
58
+ }
53
59
  const extraHeadings = requiredHeadings.length - i;
54
60
  if (
55
61
  !hasError &&
@@ -57,8 +63,11 @@ module.exports = {
57
63
  ((extraHeadings === 1) && (requiredHeadings[i] !== "*"))) &&
58
64
  (anyHeadings || !requiredHeadings.every((heading) => heading === "*"))
59
65
  ) {
60
- addErrorContext(onError, params.lines.length,
61
- requiredHeadings[i]);
66
+ addErrorContext(
67
+ onError,
68
+ params.lines.length,
69
+ requiredHeadings[i]
70
+ );
62
71
  }
63
72
  }
64
73
  };
package/lib/md044.js CHANGED
@@ -11,10 +11,13 @@ const ignoredChildTypes = new Set(
11
11
  [ "codeFencedFence", "definition", "reference", "resource" ]
12
12
  );
13
13
 
14
+ // eslint-disable-next-line jsdoc/valid-types
15
+ /** @type import("./markdownlint").Rule */
14
16
  module.exports = {
15
17
  "names": [ "MD044", "proper-names" ],
16
18
  "description": "Proper names should have the correct capitalization",
17
19
  "tags": [ "spelling" ],
20
+ "parser": "micromark",
18
21
  "function": function MD044(params, onError) {
19
22
  let names = params.config.names;
20
23
  names = Array.isArray(names) ? names : [];
@@ -88,8 +91,8 @@ module.exports = {
88
91
  token.startLine,
89
92
  name,
90
93
  nameMatch,
91
- null,
92
- null,
94
+ undefined,
95
+ undefined,
93
96
  [ column, length ],
94
97
  {
95
98
  "editColumn": column,
package/lib/md045.js CHANGED
@@ -4,18 +4,20 @@
4
4
 
5
5
  const { addError, getHtmlAttributeRe, nextLinesRe } = require("../helpers");
6
6
  const { filterByTypes, getHtmlTagInfo } = require("../helpers/micromark.cjs");
7
+ const { filterByTypesCached } = require("./cache");
7
8
 
8
9
  const altRe = getHtmlAttributeRe("alt");
9
10
 
11
+ // eslint-disable-next-line jsdoc/valid-types
12
+ /** @type import("./markdownlint").Rule */
10
13
  module.exports = {
11
14
  "names": [ "MD045", "no-alt-text" ],
12
15
  "description": "Images should have alternate text (alt text)",
13
16
  "tags": [ "accessibility", "images" ],
17
+ "parser": "micromark",
14
18
  "function": function MD045(params, onError) {
15
- const { tokens } = params.parsers.micromark;
16
-
17
19
  // Process Markdown images
18
- const images = filterByTypes(tokens, [ "image" ]);
20
+ const images = filterByTypesCached([ "image" ]);
19
21
  for (const image of images) {
20
22
  const labelTexts = filterByTypes(image.children, [ "labelText" ]);
21
23
  if (labelTexts.some((labelText) => labelText.text.length === 0)) {
@@ -33,7 +35,7 @@ module.exports = {
33
35
  }
34
36
 
35
37
  // Process HTML images
36
- const htmlTexts = filterByTypes(tokens, [ "htmlText" ]);
38
+ const htmlTexts = filterByTypesCached([ "htmlText" ], true);
37
39
  for (const htmlText of htmlTexts) {
38
40
  const { startColumn, startLine, text } = htmlText;
39
41
  const htmlTagInfo = getHtmlTagInfo(htmlText);
package/lib/md046.js CHANGED
@@ -3,29 +3,30 @@
3
3
  "use strict";
4
4
 
5
5
  const { addErrorDetailIf } = require("../helpers");
6
+ const { filterByTypesCached } = require("./cache");
6
7
 
7
8
  const tokenTypeToStyle = {
8
- "fence": "fenced",
9
- "code_block": "indented"
9
+ "codeFenced": "fenced",
10
+ "codeIndented": "indented"
10
11
  };
11
12
 
13
+ // eslint-disable-next-line jsdoc/valid-types
14
+ /** @type import("./markdownlint").Rule */
12
15
  module.exports = {
13
16
  "names": [ "MD046", "code-block-style" ],
14
17
  "description": "Code block style",
15
18
  "tags": [ "code" ],
19
+ "parser": "micromark",
16
20
  "function": function MD046(params, onError) {
17
21
  let expectedStyle = String(params.config.style || "consistent");
18
- const codeBlocksAndFences = params.parsers.markdownit.tokens.filter(
19
- (token) => (token.type === "code_block") || (token.type === "fence")
20
- );
21
- for (const token of codeBlocksAndFences) {
22
- const { lineNumber, type } = token;
22
+ for (const token of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
23
+ const { startLine, type } = token;
23
24
  if (expectedStyle === "consistent") {
24
25
  expectedStyle = tokenTypeToStyle[type];
25
26
  }
26
27
  addErrorDetailIf(
27
28
  onError,
28
- lineNumber,
29
+ startLine,
29
30
  expectedStyle,
30
31
  tokenTypeToStyle[type]);
31
32
  }
package/lib/md047.js CHANGED
@@ -4,10 +4,13 @@
4
4
 
5
5
  const { addError, isBlankLine } = require("../helpers");
6
6
 
7
+ // eslint-disable-next-line jsdoc/valid-types
8
+ /** @type import("./markdownlint").Rule */
7
9
  module.exports = {
8
10
  "names": [ "MD047", "single-trailing-newline" ],
9
11
  "description": "Files should end with a single newline character",
10
12
  "tags": [ "blank_lines" ],
13
+ "parser": "none",
11
14
  "function": function MD047(params, onError) {
12
15
  const lastLineNumber = params.lines.length;
13
16
  const lastLine = params.lines[lastLineNumber - 1];
@@ -15,8 +18,8 @@ module.exports = {
15
18
  addError(
16
19
  onError,
17
20
  lastLineNumber,
18
- null,
19
- null,
21
+ undefined,
22
+ undefined,
20
23
  [ lastLine.length, 1 ],
21
24
  {
22
25
  "insertText": "\n",
package/lib/md048.js CHANGED
@@ -3,28 +3,37 @@
3
3
  "use strict";
4
4
 
5
5
  const { addErrorDetailIf, fencedCodeBlockStyleFor } = require("../helpers");
6
+ const { tokenIfType } = 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": [ "MD048", "code-fence-style" ],
9
13
  "description": "Code fence style",
10
14
  "tags": [ "code" ],
15
+ "parser": "micromark",
11
16
  "function": function MD048(params, onError) {
12
17
  const style = String(params.config.style || "consistent");
13
18
  let expectedStyle = style;
14
- const fenceTokens = params.parsers.markdownit.tokens.filter(
15
- (token) => token.type === "fence"
16
- );
17
- for (const fenceToken of fenceTokens) {
18
- const { lineNumber, markup } = fenceToken;
19
- if (expectedStyle === "consistent") {
20
- expectedStyle = fencedCodeBlockStyleFor(markup);
19
+ const codeFenceds = filterByTypesCached([ "codeFenced" ]);
20
+ for (const codeFenced of codeFenceds) {
21
+ const codeFencedFence = tokenIfType(codeFenced.children[0], "codeFencedFence");
22
+ if (codeFencedFence) {
23
+ const codeFencedFenceSequence = tokenIfType(codeFencedFence.children[0], "codeFencedFenceSequence");
24
+ if (codeFencedFenceSequence) {
25
+ const { startLine, text } = codeFencedFenceSequence;
26
+ if (expectedStyle === "consistent") {
27
+ expectedStyle = fencedCodeBlockStyleFor(text);
28
+ }
29
+ addErrorDetailIf(
30
+ onError,
31
+ startLine,
32
+ expectedStyle,
33
+ fencedCodeBlockStyleFor(text)
34
+ );
35
+ }
21
36
  }
22
- addErrorDetailIf(
23
- onError,
24
- lineNumber,
25
- expectedStyle,
26
- fencedCodeBlockStyleFor(markup)
27
- );
28
37
  }
29
38
  }
30
39
  };