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/md001.js CHANGED
@@ -2,21 +2,30 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorDetailIf, filterTokens } = require("../helpers");
5
+ const { addErrorDetailIf } = require("../helpers");
6
+ const { getHeadingLevel } = require("../helpers/micromark.cjs");
7
+ const { filterByTypesCached } = require("./cache");
6
8
 
9
+ // eslint-disable-next-line jsdoc/valid-types
10
+ /** @type import("./markdownlint").Rule */
7
11
  module.exports = {
8
12
  "names": [ "MD001", "heading-increment" ],
9
13
  "description": "Heading levels should only increment by one level at a time",
10
14
  "tags": [ "headings" ],
15
+ "parser": "micromark",
11
16
  "function": function MD001(params, onError) {
12
- let prevLevel = 0;
13
- filterTokens(params, "heading_open", function forToken(token) {
14
- const level = Number.parseInt(token.tag.slice(1), 10);
15
- if (prevLevel && (level > prevLevel)) {
16
- addErrorDetailIf(onError, token.lineNumber,
17
- "h" + (prevLevel + 1), "h" + level);
17
+ let prevLevel = Number.MAX_SAFE_INTEGER;
18
+ for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
19
+ const level = getHeadingLevel(heading);
20
+ if (level > prevLevel) {
21
+ addErrorDetailIf(
22
+ onError,
23
+ heading.startLine,
24
+ `h${prevLevel + 1}`,
25
+ `h${level}`
26
+ );
18
27
  }
19
28
  prevLevel = level;
20
- });
29
+ }
21
30
  }
22
31
  };
package/lib/md003.js CHANGED
@@ -2,22 +2,26 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorDetailIf, filterTokens, headingStyleFor } =
6
- require("../helpers");
5
+ const { addErrorDetailIf } = require("../helpers");
6
+ const { getHeadingLevel, getHeadingStyle } = require("../helpers/micromark.cjs");
7
+ const { filterByTypesCached } = require("./cache");
7
8
 
9
+ // eslint-disable-next-line jsdoc/valid-types
10
+ /** @type import("./markdownlint").Rule */
8
11
  module.exports = {
9
12
  "names": [ "MD003", "heading-style" ],
10
13
  "description": "Heading style",
11
14
  "tags": [ "headings" ],
15
+ "parser": "micromark",
12
16
  "function": function MD003(params, onError) {
13
17
  let style = String(params.config.style || "consistent");
14
- filterTokens(params, "heading_open", function forToken(token) {
15
- const styleForToken = headingStyleFor(token);
18
+ for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
19
+ const styleForToken = getHeadingStyle(heading);
16
20
  if (style === "consistent") {
17
21
  style = styleForToken;
18
22
  }
19
23
  if (styleForToken !== style) {
20
- const h12 = /h[12]/.test(token.tag);
24
+ const h12 = getHeadingLevel(heading) <= 2;
21
25
  const setextWithAtx =
22
26
  (style === "setext_with_atx") &&
23
27
  ((h12 && (styleForToken === "setext")) ||
@@ -33,10 +37,14 @@ module.exports = {
33
37
  } else if (style === "setext_with_atx_closed") {
34
38
  expected = h12 ? "setext" : "atx_closed";
35
39
  }
36
- addErrorDetailIf(onError, token.lineNumber,
37
- expected, styleForToken);
40
+ addErrorDetailIf(
41
+ onError,
42
+ heading.startLine,
43
+ expected,
44
+ styleForToken
45
+ );
38
46
  }
39
47
  }
40
- });
48
+ }
41
49
  }
42
50
  };
package/lib/md004.js CHANGED
@@ -2,11 +2,16 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorDetailIf, listItemMarkerRe, unorderedListStyleFor } =
6
- require("../helpers");
7
- const { flattenedLists } = require("./cache");
5
+ const { addErrorDetailIf } = require("../helpers");
6
+ const { getDescendantsByType, getTokenParentOfType } = require("../helpers/micromark.cjs");
7
+ const { filterByTypesCached } = require("./cache");
8
8
 
9
- const expectedStyleToMarker = {
9
+ const markerToStyle = {
10
+ "-": "dash",
11
+ "+": "plus",
12
+ "*": "asterisk"
13
+ };
14
+ const styleToMarker = {
10
15
  "dash": "-",
11
16
  "plus": "+",
12
17
  "asterisk": "*"
@@ -16,60 +21,65 @@ const differentItemStyle = {
16
21
  "plus": "asterisk",
17
22
  "asterisk": "dash"
18
23
  };
19
- const validStyles = Object.keys(expectedStyleToMarker);
24
+ const validStyles = new Set([
25
+ "asterisk",
26
+ "consistent",
27
+ "dash",
28
+ "plus",
29
+ "sublist"
30
+ ]);
20
31
 
32
+ // eslint-disable-next-line jsdoc/valid-types
33
+ /** @type import("./markdownlint").Rule */
21
34
  module.exports = {
22
35
  "names": [ "MD004", "ul-style" ],
23
36
  "description": "Unordered list style",
24
37
  "tags": [ "bullet", "ul" ],
38
+ "parser": "micromark",
25
39
  "function": function MD004(params, onError) {
26
40
  const style = String(params.config.style || "consistent");
27
- let expectedStyle = style;
41
+ let expectedStyle = validStyles.has(style) ? style : "dash";
28
42
  const nestingStyles = [];
29
- for (const list of flattenedLists()) {
30
- if (list.unordered) {
31
- if (expectedStyle === "consistent") {
32
- expectedStyle = unorderedListStyleFor(list.items[0]);
43
+ for (const listUnordered of filterByTypesCached([ "listUnordered" ])) {
44
+ let nesting = 0;
45
+ if (style === "sublist") {
46
+ /** @type {import("../helpers/micromark.cjs").Token | null} */
47
+ let parent = listUnordered;
48
+ // @ts-ignore
49
+ while ((parent = getTokenParentOfType(parent, [ "listOrdered", "listUnordered" ]))) {
50
+ nesting++;
33
51
  }
34
- for (const item of list.items) {
35
- const itemStyle = unorderedListStyleFor(item);
36
- if (style === "sublist") {
37
- const nesting = list.nesting;
38
- if (!nestingStyles[nesting]) {
39
- nestingStyles[nesting] =
40
- (itemStyle === nestingStyles[nesting - 1]) ?
41
- differentItemStyle[itemStyle] :
42
- itemStyle;
43
- }
44
- expectedStyle = nestingStyles[nesting];
45
- }
46
- if (!validStyles.includes(expectedStyle)) {
47
- expectedStyle = validStyles[0];
48
- }
49
- let range = null;
50
- let fixInfo = null;
51
- const match = item.line.match(listItemMarkerRe);
52
- if (match) {
53
- const column = match.index + 1;
54
- const length = match[0].length;
55
- range = [ column, length ];
56
- fixInfo = {
57
- "editColumn": match[1].length + 1,
58
- "deleteCount": 1,
59
- "insertText": expectedStyleToMarker[expectedStyle]
60
- };
52
+ }
53
+ const listItemMarkers = getDescendantsByType(listUnordered, [ "listItemPrefix", "listItemMarker" ]);
54
+ for (const listItemMarker of listItemMarkers) {
55
+ const itemStyle = markerToStyle[listItemMarker.text];
56
+ if (style === "sublist") {
57
+ if (!nestingStyles[nesting]) {
58
+ nestingStyles[nesting] =
59
+ (itemStyle === nestingStyles[nesting - 1]) ?
60
+ differentItemStyle[itemStyle] :
61
+ itemStyle;
61
62
  }
62
- addErrorDetailIf(
63
- onError,
64
- item.lineNumber,
65
- expectedStyle,
66
- itemStyle,
67
- null,
68
- null,
69
- range,
70
- fixInfo
71
- );
63
+ expectedStyle = nestingStyles[nesting];
64
+ } else if (expectedStyle === "consistent") {
65
+ expectedStyle = itemStyle;
72
66
  }
67
+ const column = listItemMarker.startColumn;
68
+ const length = listItemMarker.endColumn - listItemMarker.startColumn;
69
+ addErrorDetailIf(
70
+ onError,
71
+ listItemMarker.startLine,
72
+ expectedStyle,
73
+ itemStyle,
74
+ undefined,
75
+ undefined,
76
+ [ column, length ],
77
+ {
78
+ "editColumn": column,
79
+ "deleteCount": length,
80
+ "insertText": styleToMarker[expectedStyle]
81
+ }
82
+ );
73
83
  }
74
84
  }
75
85
  }
package/lib/md005.js CHANGED
@@ -3,18 +3,17 @@
3
3
  "use strict";
4
4
 
5
5
  const { addError, addErrorDetailIf } = require("../helpers");
6
- const { filterByTypes, inHtmlFlow } = require("../helpers/micromark.cjs");
6
+ const { filterByTypesCached } = require("./cache");
7
7
 
8
+ // eslint-disable-next-line jsdoc/valid-types
9
+ /** @type import("./markdownlint").Rule */
8
10
  module.exports = {
9
11
  "names": [ "MD005", "list-indent" ],
10
12
  "description": "Inconsistent indentation for list items at the same level",
11
13
  "tags": [ "bullet", "ul", "indentation" ],
14
+ "parser": "micromark",
12
15
  "function": function MD005(params, onError) {
13
- const lists = filterByTypes(
14
- params.parsers.micromark.tokens,
15
- [ "listOrdered", "listUnordered" ]
16
- ).filter((list) => !inHtmlFlow(list));
17
- for (const list of lists) {
16
+ for (const list of filterByTypesCached([ "listOrdered", "listUnordered" ])) {
18
17
  const expectedIndent = list.startColumn - 1;
19
18
  let expectedEnd = 0;
20
19
  let endMatching = false;
@@ -30,8 +29,8 @@ module.exports = {
30
29
  lineNumber,
31
30
  expectedIndent,
32
31
  actualIndent,
33
- null,
34
- null,
32
+ undefined,
33
+ undefined,
35
34
  range
36
35
  // No fixInfo; MD007 handles this scenario better
37
36
  );
package/lib/md007.js CHANGED
@@ -3,41 +3,42 @@
3
3
  "use strict";
4
4
 
5
5
  const { addErrorDetailIf } = require("../helpers");
6
- const { filterByTypes, getTokenParentOfType, inHtmlFlow } =
7
- require("../helpers/micromark.cjs");
8
-
9
- /**
10
- * @typedef {import("../helpers/micromark.cjs").Token} Token
11
- */
6
+ const { getTokenParentOfType } = require("../helpers/micromark.cjs");
7
+ const { filterByTypesCached } = require("./cache");
12
8
 
9
+ // eslint-disable-next-line jsdoc/valid-types
10
+ /** @type import("markdownlint-micromark").TokenType[] */
13
11
  const unorderedListTypes =
14
12
  [ "blockQuotePrefix", "listItemPrefix", "listUnordered" ];
13
+ // eslint-disable-next-line jsdoc/valid-types
14
+ /** @type import("markdownlint-micromark").TokenType[] */
15
15
  const unorderedParentTypes =
16
16
  [ "blockQuote", "listOrdered", "listUnordered" ];
17
17
 
18
+ // eslint-disable-next-line jsdoc/valid-types
19
+ /** @type import("./markdownlint").Rule */
18
20
  module.exports = {
19
21
  "names": [ "MD007", "ul-indent" ],
20
22
  "description": "Unordered list indentation",
21
23
  "tags": [ "bullet", "ul", "indentation" ],
24
+ "parser": "micromark",
22
25
  "function": function MD007(params, onError) {
23
26
  const indent = Number(params.config.indent || 2);
24
27
  const startIndented = !!params.config.start_indented;
25
28
  const startIndent = Number(params.config.start_indent || indent);
26
29
  const unorderedListNesting = new Map();
27
30
  let lastBlockQuotePrefix = null;
28
- const tokens = filterByTypes(
29
- params.parsers.micromark.tokens,
30
- unorderedListTypes
31
- );
31
+ const tokens = filterByTypesCached(unorderedListTypes);
32
32
  for (const token of tokens) {
33
33
  const { endColumn, parent, startColumn, startLine, type } = token;
34
34
  if (type === "blockQuotePrefix") {
35
35
  lastBlockQuotePrefix = token;
36
36
  } else if (type === "listUnordered") {
37
37
  let nesting = 0;
38
- /** @type {Token | null} */
38
+ /** @type {import("../helpers/micromark.cjs").Token | null} */
39
39
  let current = token;
40
40
  while (
41
+ // @ts-ignore
41
42
  (current = getTokenParentOfType(current, unorderedParentTypes))
42
43
  ) {
43
44
  if (current.type === "listUnordered") {
@@ -52,7 +53,7 @@ module.exports = {
52
53
  if (nesting >= 0) {
53
54
  unorderedListNesting.set(token, nesting);
54
55
  }
55
- } else if (!inHtmlFlow(token)) {
56
+ } else {
56
57
  // listItemPrefix
57
58
  const nesting = unorderedListNesting.get(parent);
58
59
  if (nesting !== undefined) {
package/lib/md009.js CHANGED
@@ -2,70 +2,78 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addError, filterTokens, forEachLine, includesSorted,
6
- numericSortAscending } = require("../helpers");
7
- const { lineMetadata } = require("./cache");
5
+ const { addError } = require("../helpers");
6
+ const { addRangeToSet } = require("../helpers/micromark.cjs");
7
+ const { filterByTypesCached } = require("./cache");
8
8
 
9
+ // eslint-disable-next-line jsdoc/valid-types
10
+ /** @type import("./markdownlint").Rule */
9
11
  module.exports = {
10
12
  "names": [ "MD009", "no-trailing-spaces" ],
11
13
  "description": "Trailing spaces",
12
14
  "tags": [ "whitespace" ],
15
+ "parser": "micromark",
13
16
  "function": function MD009(params, onError) {
14
17
  let brSpaces = params.config.br_spaces;
15
18
  brSpaces = Number((brSpaces === undefined) ? 2 : brSpaces);
16
19
  const listItemEmptyLines = !!params.config.list_item_empty_lines;
17
20
  const strict = !!params.config.strict;
18
- const listItemLineNumbers = [];
21
+ const codeBlockLineNumbers = new Set();
22
+ for (const codeBlock of filterByTypesCached([ "codeFenced" ])) {
23
+ addRangeToSet(codeBlockLineNumbers, codeBlock.startLine + 1, codeBlock.endLine - 1);
24
+ }
25
+ for (const codeBlock of filterByTypesCached([ "codeIndented" ])) {
26
+ addRangeToSet(codeBlockLineNumbers, codeBlock.startLine, codeBlock.endLine);
27
+ }
28
+ const listItemLineNumbers = new Set();
19
29
  if (listItemEmptyLines) {
20
- filterTokens(params, "list_item_open", (token) => {
21
- for (let i = token.map[0]; i < token.map[1]; i++) {
22
- listItemLineNumbers.push(i + 1);
30
+ for (const listBlock of filterByTypesCached([ "listOrdered", "listUnordered" ])) {
31
+ addRangeToSet(listItemLineNumbers, listBlock.startLine, listBlock.endLine);
32
+ let trailingIndent = true;
33
+ for (let i = listBlock.children.length - 1; i >= 0; i--) {
34
+ const child = listBlock.children[i];
35
+ switch (child.type) {
36
+ case "content":
37
+ trailingIndent = false;
38
+ break;
39
+ case "listItemIndent":
40
+ if (trailingIndent) {
41
+ listItemLineNumbers.delete(child.startLine);
42
+ }
43
+ break;
44
+ case "listItemPrefix":
45
+ trailingIndent = true;
46
+ break;
47
+ default:
48
+ break;
49
+ }
23
50
  }
24
- });
25
- listItemLineNumbers.sort(numericSortAscending);
51
+ }
26
52
  }
27
- const paragraphLineNumbers = [];
28
- const codeInlineLineNumbers = [];
53
+ const paragraphLineNumbers = new Set();
54
+ const codeInlineLineNumbers = new Set();
29
55
  if (strict) {
30
- filterTokens(params, "paragraph_open", (token) => {
31
- for (let i = token.map[0]; i < token.map[1] - 1; i++) {
32
- paragraphLineNumbers.push(i + 1);
33
- }
34
- });
35
- const addLineNumberRange = (start, end) => {
36
- for (let i = start; i < end; i++) {
37
- codeInlineLineNumbers.push(i);
38
- }
39
- };
40
- filterTokens(params, "inline", (token) => {
41
- let start = 0;
42
- for (const child of token.children) {
43
- if (start > 0) {
44
- addLineNumberRange(start, child.lineNumber);
45
- start = 0;
46
- }
47
- if (child.type === "code_inline") {
48
- start = child.lineNumber;
49
- }
50
- }
51
- if (start > 0) {
52
- addLineNumberRange(start, token.map[1]);
53
- }
54
- });
56
+ for (const paragraph of filterByTypesCached([ "paragraph" ])) {
57
+ addRangeToSet(paragraphLineNumbers, paragraph.startLine, paragraph.endLine - 1);
58
+ }
59
+ for (const codeText of filterByTypesCached([ "codeText" ])) {
60
+ addRangeToSet(codeInlineLineNumbers, codeText.startLine, codeText.endLine - 1);
61
+ }
55
62
  }
56
63
  const expected = (brSpaces < 2) ? 0 : brSpaces;
57
- forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
64
+ for (let lineIndex = 0; lineIndex < params.lines.length; lineIndex++) {
65
+ const line = params.lines[lineIndex];
58
66
  const lineNumber = lineIndex + 1;
59
67
  const trailingSpaces = line.length - line.trimEnd().length;
60
68
  if (
61
69
  trailingSpaces &&
62
- !inCode &&
63
- !includesSorted(listItemLineNumbers, lineNumber) &&
70
+ !codeBlockLineNumbers.has(lineNumber) &&
71
+ !listItemLineNumbers.has(lineNumber) &&
64
72
  (
65
73
  (expected !== trailingSpaces) ||
66
74
  (strict &&
67
- (!includesSorted(paragraphLineNumbers, lineNumber) ||
68
- includesSorted(codeInlineLineNumbers, lineNumber)))
75
+ (!paragraphLineNumbers.has(lineNumber) ||
76
+ codeInlineLineNumbers.has(lineNumber)))
69
77
  )
70
78
  ) {
71
79
  const column = line.length - trailingSpaces + 1;
@@ -79,8 +87,9 @@ module.exports = {
79
87
  {
80
88
  "editColumn": column,
81
89
  "deleteCount": trailingSpaces
82
- });
90
+ }
91
+ );
83
92
  }
84
- });
93
+ }
85
94
  }
86
95
  };
package/lib/md010.js CHANGED
@@ -2,16 +2,19 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addError, filterTokens, forEachLine, withinAnyRange } =
6
- require("../helpers");
7
- const { codeBlockAndSpanRanges, lineMetadata } = require("./cache");
5
+ const { addError, withinAnyRange } = require("../helpers");
6
+ const { getDescendantsByType, getExclusionsForToken } = require("../helpers/micromark.cjs");
7
+ const { filterByTypesCached } = require("./cache");
8
8
 
9
9
  const tabRe = /\t+/g;
10
10
 
11
+ // eslint-disable-next-line jsdoc/valid-types
12
+ /** @type import("./markdownlint").Rule */
11
13
  module.exports = {
12
14
  "names": [ "MD010", "no-hard-tabs" ],
13
15
  "description": "Hard tabs",
14
16
  "tags": [ "whitespace", "hard_tab" ],
17
+ "parser": "micromark",
15
18
  "function": function MD010(params, onError) {
16
19
  const codeBlocks = params.config.code_blocks;
17
20
  const includeCode = (codeBlocks === undefined) ? true : !!codeBlocks;
@@ -23,38 +26,53 @@ module.exports = {
23
26
  const spaceMultiplier = (spacesPerTab === undefined) ?
24
27
  1 :
25
28
  Math.max(0, Number(spacesPerTab));
26
- const exclusions = includeCode ? [] : codeBlockAndSpanRanges();
27
- filterTokens(params, "fence", (token) => {
28
- const language = token.info.trim().toLowerCase();
29
- if (ignoreCodeLanguages.has(language)) {
30
- for (let i = token.map[0] + 1; i < token.map[1] - 1; i++) {
31
- exclusions.push([ i, 0, params.lines[i].length ]);
32
- }
29
+ const exclusions = [];
30
+ // eslint-disable-next-line jsdoc/valid-types
31
+ /** @type import("../helpers/micromark.cjs").TokenType[] */
32
+ const exclusionTypes = [];
33
+ if (includeCode) {
34
+ if (ignoreCodeLanguages.size > 0) {
35
+ exclusionTypes.push("codeFenced");
36
+ }
37
+ } else {
38
+ exclusionTypes.push("codeFenced", "codeIndented", "codeText");
39
+ }
40
+ const codeTokens = filterByTypesCached(exclusionTypes).filter((token) => {
41
+ if ((token.type === "codeFenced") && (ignoreCodeLanguages.size > 0)) {
42
+ const fenceInfos = getDescendantsByType(token, [ "codeFencedFence", "codeFencedFenceInfo" ]);
43
+ return fenceInfos.every((fenceInfo) => ignoreCodeLanguages.has(fenceInfo.text.toLowerCase()));
33
44
  }
45
+ return true;
34
46
  });
35
- forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
36
- if (includeCode || !inCode) {
37
- let match = null;
38
- while ((match = tabRe.exec(line)) !== null) {
39
- const { index } = match;
40
- const column = index + 1;
41
- const length = match[0].length;
42
- if (!withinAnyRange(exclusions, lineIndex, index, length)) {
43
- addError(
44
- onError,
45
- lineIndex + 1,
46
- "Column: " + column,
47
- null,
48
- [ column, length ],
49
- {
50
- "editColumn": column,
51
- "deleteCount": length,
52
- "insertText": "".padEnd(length * spaceMultiplier)
53
- }
54
- );
55
- }
47
+ for (const codeToken of codeTokens) {
48
+ const exclusionsForToken = getExclusionsForToken(params.lines, codeToken);
49
+ if (codeToken.type === "codeFenced") {
50
+ exclusionsForToken.pop();
51
+ exclusionsForToken.shift();
52
+ }
53
+ exclusions.push(...exclusionsForToken);
54
+ }
55
+ for (let lineIndex = 0; lineIndex < params.lines.length; lineIndex++) {
56
+ const line = params.lines[lineIndex];
57
+ let match = null;
58
+ while ((match = tabRe.exec(line)) !== null) {
59
+ const column = match.index + 1;
60
+ const length = match[0].length;
61
+ if (!withinAnyRange(exclusions, lineIndex + 1, column, length)) {
62
+ addError(
63
+ onError,
64
+ lineIndex + 1,
65
+ "Column: " + column,
66
+ undefined,
67
+ [ column, length ],
68
+ {
69
+ "editColumn": column,
70
+ "deleteCount": length,
71
+ "insertText": "".padEnd(length * spaceMultiplier)
72
+ }
73
+ );
56
74
  }
57
75
  }
58
- });
76
+ }
59
77
  }
60
78
  };
package/lib/md011.js CHANGED
@@ -2,20 +2,31 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addError, forEachLine, withinAnyRange } = require("../helpers");
6
- const { codeBlockAndSpanRanges, lineMetadata } = require("./cache");
5
+ const { addError, withinAnyRange } = require("../helpers");
6
+ const { addRangeToSet, getExclusionsForToken } = require("../helpers/micromark.cjs");
7
+ const { filterByTypesCached } = require("./cache");
7
8
 
8
9
  const reversedLinkRe =
9
10
  /(^|[^\\])\(([^()]+)\)\[([^\]^][^\]]*)\](?!\()/g;
10
11
 
12
+ // eslint-disable-next-line jsdoc/valid-types
13
+ /** @type import("./markdownlint").Rule */
11
14
  module.exports = {
12
15
  "names": [ "MD011", "no-reversed-links" ],
13
16
  "description": "Reversed link syntax",
14
17
  "tags": [ "links" ],
18
+ "parser": "micromark",
15
19
  "function": function MD011(params, onError) {
16
- const exclusions = codeBlockAndSpanRanges();
17
- forEachLine(lineMetadata(), (line, lineIndex, inCode, onFence) => {
18
- if (!inCode && !onFence) {
20
+ const codeBlockLineNumbers = new Set();
21
+ for (const codeBlock of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
22
+ addRangeToSet(codeBlockLineNumbers, codeBlock.startLine, codeBlock.endLine);
23
+ }
24
+ const exclusions = [];
25
+ for (const codeText of filterByTypesCached([ "codeText" ])) {
26
+ exclusions.push(...getExclusionsForToken(params.lines, codeText));
27
+ }
28
+ for (const [ lineIndex, line ] of params.lines.entries()) {
29
+ if (!codeBlockLineNumbers.has(lineIndex + 1)) {
19
30
  let match = null;
20
31
  while ((match = reversedLinkRe.exec(line)) !== null) {
21
32
  const [ reversedLink, preChar, linkText, linkDestination ] = match;
@@ -24,7 +35,7 @@ module.exports = {
24
35
  if (
25
36
  !linkText.endsWith("\\") &&
26
37
  !linkDestination.endsWith("\\") &&
27
- !withinAnyRange(exclusions, lineIndex, index, length)
38
+ !withinAnyRange(exclusions, lineIndex + 1, index, length)
28
39
  ) {
29
40
  addError(
30
41
  onError,
@@ -41,6 +52,6 @@ module.exports = {
41
52
  }
42
53
  }
43
54
  }
44
- });
55
+ }
45
56
  }
46
57
  };
package/lib/md012.js CHANGED
@@ -2,17 +2,27 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorDetailIf, forEachLine } = require("../helpers");
6
- const { lineMetadata } = require("./cache");
5
+ const { addErrorDetailIf } = require("../helpers");
6
+ const { addRangeToSet } = require("../helpers/micromark.cjs");
7
+ const { filterByTypesCached } = require("./cache");
7
8
 
9
+ // eslint-disable-next-line jsdoc/valid-types
10
+ /** @type import("./markdownlint").Rule */
8
11
  module.exports = {
9
12
  "names": [ "MD012", "no-multiple-blanks" ],
10
13
  "description": "Multiple consecutive blank lines",
11
14
  "tags": [ "whitespace", "blank_lines" ],
15
+ "parser": "micromark",
12
16
  "function": function MD012(params, onError) {
13
17
  const maximum = Number(params.config.maximum || 1);
18
+ const { lines } = params;
19
+ const codeBlockLineNumbers = new Set();
20
+ for (const codeBlock of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
21
+ addRangeToSet(codeBlockLineNumbers, codeBlock.startLine, codeBlock.endLine);
22
+ }
14
23
  let count = 0;
15
- forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
24
+ for (const [ lineIndex, line ] of lines.entries()) {
25
+ const inCode = codeBlockLineNumbers.has(lineIndex + 1);
16
26
  count = (inCode || (line.trim().length > 0)) ? 0 : count + 1;
17
27
  if (maximum < count) {
18
28
  addErrorDetailIf(
@@ -20,13 +30,14 @@ module.exports = {
20
30
  lineIndex + 1,
21
31
  maximum,
22
32
  count,
23
- null,
24
- null,
25
- null,
33
+ undefined,
34
+ undefined,
35
+ undefined,
26
36
  {
27
37
  "deleteCount": -1
28
- });
38
+ }
39
+ );
29
40
  }
30
- });
41
+ }
31
42
  }
32
43
  };