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.
Files changed (90) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +97 -45
  3. package/demo/markdownlint-browser.js +2186 -2164
  4. package/doc/CustomRules.md +66 -255
  5. package/doc/Rules.md +102 -29
  6. package/doc/md003.md +2 -2
  7. package/doc/md004.md +4 -3
  8. package/doc/md034.md +5 -0
  9. package/doc/md035.md +3 -8
  10. package/doc/md044.md +10 -0
  11. package/doc/md046.md +2 -2
  12. package/doc/md048.md +3 -2
  13. package/doc/md049.md +4 -3
  14. package/doc/md050.md +4 -4
  15. package/doc/md051.md +11 -3
  16. package/doc/md055.md +3 -2
  17. package/doc/md058.md +48 -0
  18. package/helpers/README.md +2 -45
  19. package/helpers/helpers.js +115 -607
  20. package/helpers/micromark-helpers.cjs +296 -0
  21. package/helpers/micromark-parse.cjs +313 -0
  22. package/helpers/package.json +5 -2
  23. package/helpers/shared.js +6 -0
  24. package/helpers/test.cjs +28 -0
  25. package/lib/cache.js +62 -13
  26. package/lib/configuration-strict.d.ts +1199 -0
  27. package/lib/configuration.d.ts +3 -1173
  28. package/lib/constants.js +3 -2
  29. package/lib/markdownit.cjs +172 -0
  30. package/lib/markdownlint.d.ts +103 -56
  31. package/lib/markdownlint.js +180 -120
  32. package/lib/md001.js +15 -9
  33. package/lib/md003.js +14 -9
  34. package/lib/md004.js +55 -48
  35. package/lib/md005.js +4 -13
  36. package/lib/md007.js +7 -11
  37. package/lib/md009.js +50 -44
  38. package/lib/md010.js +52 -33
  39. package/lib/md011.js +32 -23
  40. package/lib/md012.js +17 -9
  41. package/lib/md013.js +44 -38
  42. package/lib/md014.js +31 -38
  43. package/lib/md018.js +17 -9
  44. package/lib/md019-md021.js +80 -0
  45. package/lib/md020.js +14 -8
  46. package/lib/md022.js +13 -29
  47. package/lib/md023.js +22 -23
  48. package/lib/md024.js +12 -9
  49. package/lib/md025.js +15 -10
  50. package/lib/md026.js +2 -7
  51. package/lib/md027.js +7 -12
  52. package/lib/md028.js +3 -8
  53. package/lib/md029.js +40 -34
  54. package/lib/md030.js +4 -10
  55. package/lib/md031.js +45 -23
  56. package/lib/md032.js +33 -31
  57. package/lib/md033.js +3 -8
  58. package/lib/md034.js +21 -34
  59. package/lib/md035.js +2 -7
  60. package/lib/md036.js +19 -24
  61. package/lib/md037.js +3 -8
  62. package/lib/md038.js +11 -22
  63. package/lib/md039.js +64 -54
  64. package/lib/md040.js +13 -20
  65. package/lib/md041.js +5 -5
  66. package/lib/md042.js +44 -38
  67. package/lib/md043.js +20 -14
  68. package/lib/md044.js +34 -36
  69. package/lib/md045.js +5 -10
  70. package/lib/md046.js +2 -8
  71. package/lib/md048.js +30 -23
  72. package/lib/md049-md050.js +22 -12
  73. package/lib/md051.js +20 -28
  74. package/lib/md052.js +2 -2
  75. package/lib/md053.js +7 -6
  76. package/lib/md054.js +16 -23
  77. package/lib/md055.js +41 -49
  78. package/lib/md056.js +32 -33
  79. package/lib/md058.js +58 -0
  80. package/lib/rules.js +11 -4
  81. package/package.json +23 -20
  82. package/schema/.markdownlint.jsonc +58 -52
  83. package/schema/.markdownlint.yaml +56 -51
  84. package/schema/ValidatingConfiguration.md +6 -8
  85. package/schema/markdownlint-config-schema-strict.json +1841 -0
  86. package/schema/markdownlint-config-schema.json +141 -109
  87. package/style/prettier.json +1 -0
  88. package/helpers/micromark.cjs +0 -445
  89. package/lib/md019.js +0 -42
  90. package/lib/md021.js +0 -65
package/lib/md005.js CHANGED
@@ -3,7 +3,7 @@
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
8
  // eslint-disable-next-line jsdoc/valid-types
9
9
  /** @type import("./markdownlint").Rule */
@@ -13,16 +13,7 @@ module.exports = {
13
13
  "tags": [ "bullet", "ul", "indentation" ],
14
14
  "parser": "micromark",
15
15
  "function": function MD005(params, onError) {
16
- // eslint-disable-next-line jsdoc/valid-types
17
- /** @type import("../helpers/micromark.cjs").Token[] */
18
- const micromarkTokens =
19
- // @ts-ignore
20
- params.parsers.micromark.tokens;
21
- const lists = filterByTypes(
22
- micromarkTokens,
23
- [ "listOrdered", "listUnordered" ]
24
- ).filter((list) => !inHtmlFlow(list));
25
- for (const list of lists) {
16
+ for (const list of filterByTypesCached([ "listOrdered", "listUnordered" ])) {
26
17
  const expectedIndent = list.startColumn - 1;
27
18
  let expectedEnd = 0;
28
19
  let endMatching = false;
@@ -38,8 +29,8 @@ module.exports = {
38
29
  lineNumber,
39
30
  expectedIndent,
40
31
  actualIndent,
41
- null,
42
- null,
32
+ undefined,
33
+ undefined,
43
34
  range
44
35
  // No fixInfo; MD007 handles this scenario better
45
36
  );
package/lib/md007.js CHANGED
@@ -3,8 +3,8 @@
3
3
  "use strict";
4
4
 
5
5
  const { addErrorDetailIf } = require("../helpers");
6
- const { filterByTypes, getTokenParentOfType, inHtmlFlow } =
7
- require("../helpers/micromark.cjs");
6
+ const { getParentOfType } = 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-micromark").TokenType[] */
@@ -26,24 +26,20 @@ module.exports = {
26
26
  const indent = Number(params.config.indent || 2);
27
27
  const startIndented = !!params.config.start_indented;
28
28
  const startIndent = Number(params.config.start_indent || indent);
29
- // eslint-disable-next-line jsdoc/valid-types
30
- /** @type import("../helpers/micromark.cjs").Token[] */
31
- const micromarkTokens =
32
- // @ts-ignore
33
- params.parsers.micromark.tokens;
34
29
  const unorderedListNesting = new Map();
35
30
  let lastBlockQuotePrefix = null;
36
- const tokens = filterByTypes(micromarkTokens, unorderedListTypes);
31
+ const tokens = filterByTypesCached(unorderedListTypes);
37
32
  for (const token of tokens) {
38
33
  const { endColumn, parent, startColumn, startLine, type } = token;
39
34
  if (type === "blockQuotePrefix") {
40
35
  lastBlockQuotePrefix = token;
41
36
  } else if (type === "listUnordered") {
42
37
  let nesting = 0;
43
- /** @type {import("../helpers/micromark.cjs").Token | null} */
38
+ /** @type {import("../helpers/micromark-helpers.cjs").Token | null} */
44
39
  let current = token;
45
40
  while (
46
- (current = getTokenParentOfType(current, unorderedParentTypes))
41
+ // @ts-ignore
42
+ (current = getParentOfType(current, unorderedParentTypes))
47
43
  ) {
48
44
  if (current.type === "listUnordered") {
49
45
  nesting++;
@@ -57,7 +53,7 @@ module.exports = {
57
53
  if (nesting >= 0) {
58
54
  unorderedListNesting.set(token, nesting);
59
55
  }
60
- } else if (!inHtmlFlow(token)) {
56
+ } else {
61
57
  // listItemPrefix
62
58
  const nesting = unorderedListNesting.get(parent);
63
59
  if (nesting !== undefined) {
package/lib/md009.js CHANGED
@@ -2,9 +2,9 @@
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-helpers.cjs");
7
+ const { filterByTypesCached } = require("./cache");
8
8
 
9
9
  // eslint-disable-next-line jsdoc/valid-types
10
10
  /** @type import("./markdownlint").Rule */
@@ -12,63 +12,68 @@ module.exports = {
12
12
  "names": [ "MD009", "no-trailing-spaces" ],
13
13
  "description": "Trailing spaces",
14
14
  "tags": [ "whitespace" ],
15
- "parser": "markdownit",
15
+ "parser": "micromark",
16
16
  "function": function MD009(params, onError) {
17
17
  let brSpaces = params.config.br_spaces;
18
18
  brSpaces = Number((brSpaces === undefined) ? 2 : brSpaces);
19
19
  const listItemEmptyLines = !!params.config.list_item_empty_lines;
20
20
  const strict = !!params.config.strict;
21
- 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();
22
29
  if (listItemEmptyLines) {
23
- filterTokens(params, "list_item_open", (token) => {
24
- for (let i = token.map[0]; i < token.map[1]; i++) {
25
- 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
+ }
26
50
  }
27
- });
28
- listItemLineNumbers.sort(numericSortAscending);
51
+ }
29
52
  }
30
- const paragraphLineNumbers = [];
31
- const codeInlineLineNumbers = [];
53
+ const paragraphLineNumbers = new Set();
54
+ const codeInlineLineNumbers = new Set();
32
55
  if (strict) {
33
- filterTokens(params, "paragraph_open", (token) => {
34
- for (let i = token.map[0]; i < token.map[1] - 1; i++) {
35
- paragraphLineNumbers.push(i + 1);
36
- }
37
- });
38
- const addLineNumberRange = (start, end) => {
39
- for (let i = start; i < end; i++) {
40
- codeInlineLineNumbers.push(i);
41
- }
42
- };
43
- filterTokens(params, "inline", (token) => {
44
- let start = 0;
45
- for (const child of token.children) {
46
- if (start > 0) {
47
- addLineNumberRange(start, child.lineNumber);
48
- start = 0;
49
- }
50
- if (child.type === "code_inline") {
51
- start = child.lineNumber;
52
- }
53
- }
54
- if (start > 0) {
55
- addLineNumberRange(start, token.map[1]);
56
- }
57
- });
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
+ }
58
62
  }
59
63
  const expected = (brSpaces < 2) ? 0 : brSpaces;
60
- forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
64
+ for (let lineIndex = 0; lineIndex < params.lines.length; lineIndex++) {
65
+ const line = params.lines[lineIndex];
61
66
  const lineNumber = lineIndex + 1;
62
67
  const trailingSpaces = line.length - line.trimEnd().length;
63
68
  if (
64
69
  trailingSpaces &&
65
- !inCode &&
66
- !includesSorted(listItemLineNumbers, lineNumber) &&
70
+ !codeBlockLineNumbers.has(lineNumber) &&
71
+ !listItemLineNumbers.has(lineNumber) &&
67
72
  (
68
73
  (expected !== trailingSpaces) ||
69
74
  (strict &&
70
- (!includesSorted(paragraphLineNumbers, lineNumber) ||
71
- includesSorted(codeInlineLineNumbers, lineNumber)))
75
+ (!paragraphLineNumbers.has(lineNumber) ||
76
+ codeInlineLineNumbers.has(lineNumber)))
72
77
  )
73
78
  ) {
74
79
  const column = line.length - trailingSpaces + 1;
@@ -82,8 +87,9 @@ module.exports = {
82
87
  {
83
88
  "editColumn": column,
84
89
  "deleteCount": trailingSpaces
85
- });
90
+ }
91
+ );
86
92
  }
87
- });
93
+ }
88
94
  }
89
95
  };
package/lib/md010.js CHANGED
@@ -2,9 +2,9 @@
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, hasOverlap } = require("../helpers");
6
+ const { getDescendantsByType } = require("../helpers/micromark-helpers.cjs");
7
+ const { filterByTypesCached } = require("./cache");
8
8
 
9
9
  const tabRe = /\t+/g;
10
10
 
@@ -14,7 +14,7 @@ module.exports = {
14
14
  "names": [ "MD010", "no-hard-tabs" ],
15
15
  "description": "Hard tabs",
16
16
  "tags": [ "whitespace", "hard_tab" ],
17
- "parser": "markdownit",
17
+ "parser": "micromark",
18
18
  "function": function MD010(params, onError) {
19
19
  const codeBlocks = params.config.code_blocks;
20
20
  const includeCode = (codeBlocks === undefined) ? true : !!codeBlocks;
@@ -26,38 +26,57 @@ module.exports = {
26
26
  const spaceMultiplier = (spacesPerTab === undefined) ?
27
27
  1 :
28
28
  Math.max(0, Number(spacesPerTab));
29
- const exclusions = includeCode ? [] : codeBlockAndSpanRanges();
30
- filterTokens(params, "fence", (token) => {
31
- const language = token.info.trim().toLowerCase();
32
- if (ignoreCodeLanguages.has(language)) {
33
- for (let i = token.map[0] + 1; i < token.map[1] - 1; i++) {
34
- exclusions.push([ i, 0, params.lines[i].length ]);
35
- }
29
+ // eslint-disable-next-line jsdoc/valid-types
30
+ /** @type import("../helpers/micromark-helpers.cjs").TokenType[] */
31
+ const exclusionTypes = [];
32
+ if (includeCode) {
33
+ if (ignoreCodeLanguages.size > 0) {
34
+ exclusionTypes.push("codeFenced");
35
+ }
36
+ } else {
37
+ exclusionTypes.push("codeFenced", "codeIndented", "codeText");
38
+ }
39
+ const codeTokens = filterByTypesCached(exclusionTypes).filter((token) => {
40
+ if ((token.type === "codeFenced") && (ignoreCodeLanguages.size > 0)) {
41
+ const fenceInfos = getDescendantsByType(token, [ "codeFencedFence", "codeFencedFenceInfo" ]);
42
+ return fenceInfos.every((fenceInfo) => ignoreCodeLanguages.has(fenceInfo.text.toLowerCase()));
36
43
  }
44
+ return true;
37
45
  });
38
- forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
39
- if (includeCode || !inCode) {
40
- let match = null;
41
- while ((match = tabRe.exec(line)) !== null) {
42
- const { index } = match;
43
- const column = index + 1;
44
- const length = match[0].length;
45
- if (!withinAnyRange(exclusions, lineIndex, index, length)) {
46
- addError(
47
- onError,
48
- lineIndex + 1,
49
- "Column: " + column,
50
- undefined,
51
- [ column, length ],
52
- {
53
- "editColumn": column,
54
- "deleteCount": length,
55
- "insertText": "".padEnd(length * spaceMultiplier)
56
- }
57
- );
58
- }
46
+ const codeRanges = codeTokens.map((token) => {
47
+ const { type, startLine, startColumn, endLine, endColumn } = token;
48
+ const codeFenced = (type === "codeFenced");
49
+ return {
50
+ "startLine": startLine + (codeFenced ? 1 : 0),
51
+ "startColumn": codeFenced ? 0 : startColumn,
52
+ "endLine": endLine - (codeFenced ? 1 : 0),
53
+ "endColumn": codeFenced ? Number.MAX_SAFE_INTEGER : endColumn
54
+ };
55
+ });
56
+ for (let lineIndex = 0; lineIndex < params.lines.length; lineIndex++) {
57
+ const line = params.lines[lineIndex];
58
+ let match = null;
59
+ while ((match = tabRe.exec(line)) !== null) {
60
+ const lineNumber = lineIndex + 1;
61
+ const column = match.index + 1;
62
+ const length = match[0].length;
63
+ /** @type {import("../helpers").FileRange} */
64
+ const range = { "startLine": lineNumber, "startColumn": column, "endLine": lineNumber, "endColumn": column + length - 1 };
65
+ if (!codeRanges.some((codeRange) => hasOverlap(codeRange, range))) {
66
+ addError(
67
+ onError,
68
+ lineNumber,
69
+ "Column: " + column,
70
+ undefined,
71
+ [ column, length ],
72
+ {
73
+ "editColumn": column,
74
+ "deleteCount": length,
75
+ "insertText": "".padEnd(length * spaceMultiplier)
76
+ }
77
+ );
59
78
  }
60
79
  }
61
- });
80
+ }
62
81
  }
63
82
  };
package/lib/md011.js CHANGED
@@ -2,8 +2,9 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addError, forEachLine, withinAnyRange } = require("../helpers");
6
- const { codeBlockAndSpanRanges, lineMetadata } = require("./cache");
5
+ const { addError, hasOverlap } = require("../helpers");
6
+ const { addRangeToSet } = require("../helpers/micromark-helpers.cjs");
7
+ const { filterByTypesCached } = require("./cache");
7
8
 
8
9
  const reversedLinkRe =
9
10
  /(^|[^\\])\(([^()]+)\)\[([^\]^][^\]]*)\](?!\()/g;
@@ -14,36 +15,44 @@ module.exports = {
14
15
  "names": [ "MD011", "no-reversed-links" ],
15
16
  "description": "Reversed link syntax",
16
17
  "tags": [ "links" ],
17
- "parser": "none",
18
+ "parser": "micromark",
18
19
  "function": function MD011(params, onError) {
19
- const exclusions = codeBlockAndSpanRanges();
20
- forEachLine(lineMetadata(), (line, lineIndex, inCode, onFence) => {
21
- 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 codeTexts = filterByTypesCached([ "codeText" ]);
25
+ for (const [ lineIndex, line ] of params.lines.entries()) {
26
+ const lineNumber = lineIndex + 1;
27
+ if (!codeBlockLineNumbers.has(lineNumber)) {
22
28
  let match = null;
23
29
  while ((match = reversedLinkRe.exec(line)) !== null) {
24
30
  const [ reversedLink, preChar, linkText, linkDestination ] = match;
25
- const index = match.index + preChar.length;
26
- const length = match[0].length - preChar.length;
27
31
  if (
28
32
  !linkText.endsWith("\\") &&
29
- !linkDestination.endsWith("\\") &&
30
- !withinAnyRange(exclusions, lineIndex, index, length)
33
+ !linkDestination.endsWith("\\")
31
34
  ) {
32
- addError(
33
- onError,
34
- lineIndex + 1,
35
- reversedLink.slice(preChar.length),
36
- undefined,
37
- [ index + 1, length ],
38
- {
39
- "editColumn": index + 1,
40
- "deleteCount": length,
41
- "insertText": `[${linkText}](${linkDestination})`
42
- }
43
- );
35
+ const column = match.index + preChar.length + 1;
36
+ const length = match[0].length - preChar.length;
37
+ /** @type {import("../helpers").FileRange} */
38
+ const range = { "startLine": lineNumber, "startColumn": column, "endLine": lineNumber, "endColumn": column + length - 1 };
39
+ if (!codeTexts.some((codeText) => hasOverlap(codeText, range))) {
40
+ addError(
41
+ onError,
42
+ lineNumber,
43
+ reversedLink.slice(preChar.length),
44
+ undefined,
45
+ [ column, length ],
46
+ {
47
+ "editColumn": column,
48
+ "deleteCount": length,
49
+ "insertText": `[${linkText}](${linkDestination})`
50
+ }
51
+ );
52
+ }
44
53
  }
45
54
  }
46
55
  }
47
- });
56
+ }
48
57
  }
49
58
  };
package/lib/md012.js CHANGED
@@ -2,8 +2,9 @@
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-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,11 +12,17 @@ module.exports = {
11
12
  "names": [ "MD012", "no-multiple-blanks" ],
12
13
  "description": "Multiple consecutive blank lines",
13
14
  "tags": [ "whitespace", "blank_lines" ],
14
- "parser": "none",
15
+ "parser": "micromark",
15
16
  "function": function MD012(params, onError) {
16
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
+ }
17
23
  let count = 0;
18
- forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
24
+ for (const [ lineIndex, line ] of lines.entries()) {
25
+ const inCode = codeBlockLineNumbers.has(lineIndex + 1);
19
26
  count = (inCode || (line.trim().length > 0)) ? 0 : count + 1;
20
27
  if (maximum < count) {
21
28
  addErrorDetailIf(
@@ -23,13 +30,14 @@ module.exports = {
23
30
  lineIndex + 1,
24
31
  maximum,
25
32
  count,
26
- null,
27
- null,
28
- null,
33
+ undefined,
34
+ undefined,
35
+ undefined,
29
36
  {
30
37
  "deleteCount": -1
31
- });
38
+ }
39
+ );
32
40
  }
33
- });
41
+ }
34
42
  }
35
43
  };
package/lib/md013.js CHANGED
@@ -2,25 +2,15 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorDetailIf, filterTokens, forEachHeading, forEachLine,
6
- includesSorted } = require("../helpers");
7
- const { lineMetadata, referenceLinkImageData } = require("./cache");
5
+ const { addErrorDetailIf } = require("../helpers");
6
+ const { getReferenceLinkImageData } = require("./cache");
7
+ const { addRangeToSet, getDescendantsByType } = require("../helpers/micromark-helpers.cjs");
8
+ const { filterByTypesCached } = require("./cache");
8
9
 
9
10
  const longLineRePrefix = "^.{";
10
11
  const longLineRePostfixRelaxed = "}.*\\s.*$";
11
12
  const longLineRePostfixStrict = "}.+$";
12
- const linkOrImageOnlyLineRe = /^[es]*(?:lT?L|I)[ES]*$/;
13
13
  const sternModeRe = /^(?:[#>\s]*\s)?\S*$/;
14
- const tokenTypeMap = {
15
- "em_open": "e",
16
- "em_close": "E",
17
- "image": "I",
18
- "link_open": "l",
19
- "link_close": "L",
20
- "strong_open": "s",
21
- "strong_close": "S",
22
- "text": "T"
23
- };
24
14
 
25
15
  // eslint-disable-next-line jsdoc/valid-types
26
16
  /** @type import("./markdownlint").Rule */
@@ -28,7 +18,7 @@ module.exports = {
28
18
  "names": [ "MD013", "line-length" ],
29
19
  "description": "Line length",
30
20
  "tags": [ "line_length" ],
31
- "parser": "markdownit",
21
+ "parser": "micromark",
32
22
  "function": function MD013(params, onError) {
33
23
  const lineLength = Number(params.config.line_length || 80);
34
24
  const headingLineLength =
@@ -51,26 +41,41 @@ module.exports = {
51
41
  const includeTables = (tables === undefined) ? true : !!tables;
52
42
  const headings = params.config.headings;
53
43
  const includeHeadings = (headings === undefined) ? true : !!headings;
54
- const headingLineNumbers = [];
55
- forEachHeading(params, (heading) => {
56
- headingLineNumbers.push(heading.lineNumber);
57
- });
58
- const linkOnlyLineNumbers = [];
59
- filterTokens(params, "inline", (token) => {
60
- let childTokenTypes = "";
61
- for (const child of token.children) {
62
- if (child.type !== "text" || child.content !== "") {
63
- childTokenTypes += tokenTypeMap[child.type] || "x";
64
- }
44
+ const headingLineNumbers = new Set();
45
+ for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
46
+ addRangeToSet(headingLineNumbers, heading.startLine, heading.endLine);
47
+ }
48
+ const codeBlockLineNumbers = new Set();
49
+ for (const codeBlock of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
50
+ addRangeToSet(codeBlockLineNumbers, codeBlock.startLine, codeBlock.endLine);
51
+ }
52
+ const tableLineNumbers = new Set();
53
+ for (const table of filterByTypesCached([ "table" ])) {
54
+ addRangeToSet(tableLineNumbers, table.startLine, table.endLine);
55
+ }
56
+ const linkLineNumbers = new Set();
57
+ for (const link of filterByTypesCached([ "autolink", "image", "link", "literalAutolink" ])) {
58
+ addRangeToSet(linkLineNumbers, link.startLine, link.endLine);
59
+ }
60
+ const paragraphDataLineNumbers = new Set();
61
+ for (const paragraph of filterByTypesCached([ "paragraph" ])) {
62
+ for (const data of getDescendantsByType(paragraph, [ "data" ])) {
63
+ addRangeToSet(paragraphDataLineNumbers, data.startLine, data.endLine);
65
64
  }
66
- if (linkOrImageOnlyLineRe.test(childTokenTypes)) {
67
- linkOnlyLineNumbers.push(token.lineNumber);
65
+ }
66
+ const linkOnlyLineNumbers = new Set();
67
+ for (const lineNumber of linkLineNumbers) {
68
+ if (!paragraphDataLineNumbers.has(lineNumber)) {
69
+ linkOnlyLineNumbers.add(lineNumber);
68
70
  }
69
- });
70
- const { definitionLineIndices } = referenceLinkImageData();
71
- forEachLine(lineMetadata(), (line, lineIndex, inCode, onFence, inTable) => {
71
+ }
72
+ const definitionLineIndices = new Set(getReferenceLinkImageData().definitionLineIndices);
73
+ for (let lineIndex = 0; lineIndex < params.lines.length; lineIndex++) {
74
+ const line = params.lines[lineIndex];
72
75
  const lineNumber = lineIndex + 1;
73
- const isHeading = includesSorted(headingLineNumbers, lineNumber);
76
+ const isHeading = headingLineNumbers.has(lineNumber);
77
+ const inCode = codeBlockLineNumbers.has(lineNumber);
78
+ const inTable = tableLineNumbers.has(lineNumber);
74
79
  const length = inCode ?
75
80
  codeLineLength :
76
81
  (isHeading ? headingLineLength : lineLength);
@@ -80,20 +85,21 @@ module.exports = {
80
85
  if ((includeCodeBlocks || !inCode) &&
81
86
  (includeTables || !inTable) &&
82
87
  (includeHeadings || !isHeading) &&
83
- !includesSorted(definitionLineIndices, lineIndex) &&
88
+ !definitionLineIndices.has(lineIndex) &&
84
89
  (strict ||
85
90
  (!(stern && sternModeRe.test(line)) &&
86
- !includesSorted(linkOnlyLineNumbers, lineNumber))) &&
91
+ !linkOnlyLineNumbers.has(lineNumber))) &&
87
92
  lengthRe.test(line)) {
88
93
  addErrorDetailIf(
89
94
  onError,
90
95
  lineNumber,
91
96
  length,
92
97
  line.length,
93
- null,
94
- null,
95
- [ length + 1, line.length - length ]);
98
+ undefined,
99
+ undefined,
100
+ [ length + 1, line.length - length ]
101
+ );
96
102
  }
97
- });
103
+ }
98
104
  }
99
105
  };