markdownlint 0.24.0 → 0.26.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 (51) hide show
  1. package/CONTRIBUTING.md +15 -3
  2. package/LICENSE +1 -1
  3. package/README.md +138 -87
  4. package/demo/markdownlint-browser.js +2028 -1364
  5. package/doc/CustomRules.md +15 -4
  6. package/doc/Rules.md +217 -26
  7. package/helpers/LICENSE +1 -1
  8. package/helpers/helpers.js +541 -188
  9. package/helpers/package.json +4 -1
  10. package/lib/cache.js +15 -25
  11. package/lib/constants.js +7 -0
  12. package/lib/markdownlint.d.ts +27 -19
  13. package/lib/markdownlint.js +438 -344
  14. package/lib/md004.js +4 -4
  15. package/lib/md005.js +4 -4
  16. package/lib/md006.js +4 -4
  17. package/lib/md007.js +8 -6
  18. package/lib/md009.js +20 -13
  19. package/lib/md010.js +34 -16
  20. package/lib/md011.js +4 -4
  21. package/lib/md013.js +4 -5
  22. package/lib/md014.js +4 -4
  23. package/lib/md021.js +3 -1
  24. package/lib/md026.js +1 -1
  25. package/lib/md027.js +2 -2
  26. package/lib/md028.js +2 -2
  27. package/lib/md029.js +5 -4
  28. package/lib/md030.js +4 -4
  29. package/lib/md031.js +2 -2
  30. package/lib/md032.js +3 -2
  31. package/lib/md033.js +12 -9
  32. package/lib/md034.js +2 -2
  33. package/lib/md035.js +10 -5
  34. package/lib/md036.js +2 -2
  35. package/lib/md037.js +5 -3
  36. package/lib/md038.js +7 -4
  37. package/lib/md039.js +8 -5
  38. package/lib/md042.js +17 -10
  39. package/lib/md043.js +2 -3
  40. package/lib/md044.js +21 -10
  41. package/lib/md046.js +14 -13
  42. package/lib/md048.js +13 -14
  43. package/lib/md049-md050.js +73 -0
  44. package/lib/md051.js +84 -0
  45. package/lib/md052.js +35 -0
  46. package/lib/md053.js +52 -0
  47. package/lib/rules.js +8 -6
  48. package/package.json +37 -32
  49. package/schema/.markdownlint.jsonc +30 -3
  50. package/schema/.markdownlint.yaml +26 -1
  51. package/schema/markdownlint-config-schema.json +159 -683
package/lib/md004.js CHANGED
@@ -26,12 +26,12 @@ module.exports = {
26
26
  const style = String(params.config.style || "consistent");
27
27
  let expectedStyle = style;
28
28
  const nestingStyles = [];
29
- flattenedLists().forEach((list) => {
29
+ for (const list of flattenedLists()) {
30
30
  if (list.unordered) {
31
31
  if (expectedStyle === "consistent") {
32
32
  expectedStyle = unorderedListStyleFor(list.items[0]);
33
33
  }
34
- list.items.forEach((item) => {
34
+ for (const item of list.items) {
35
35
  const itemStyle = unorderedListStyleFor(item);
36
36
  if (style === "sublist") {
37
37
  const nesting = list.nesting;
@@ -69,8 +69,8 @@ module.exports = {
69
69
  range,
70
70
  fixInfo
71
71
  );
72
- });
72
+ }
73
73
  }
74
- });
74
+ }
75
75
  }
76
76
  };
package/lib/md005.js CHANGED
@@ -11,12 +11,12 @@ module.exports = {
11
11
  "description": "Inconsistent indentation for list items at the same level",
12
12
  "tags": [ "bullet", "ul", "indentation" ],
13
13
  "function": function MD005(params, onError) {
14
- flattenedLists().forEach((list) => {
14
+ for (const list of flattenedLists()) {
15
15
  const expectedIndent = list.indent;
16
16
  let expectedEnd = 0;
17
17
  let actualEnd = -1;
18
18
  let endMatching = false;
19
- list.items.forEach((item) => {
19
+ for (const item of list.items) {
20
20
  const { line, lineNumber } = item;
21
21
  const actualIndent = indentFor(item);
22
22
  let match = null;
@@ -63,7 +63,7 @@ module.exports = {
63
63
  }
64
64
  }
65
65
  }
66
- });
67
- });
66
+ }
67
+ }
68
68
  }
69
69
  };
package/lib/md006.js CHANGED
@@ -12,9 +12,9 @@ module.exports = {
12
12
  "Consider starting bulleted lists at the beginning of the line",
13
13
  "tags": [ "bullet", "ul", "indentation" ],
14
14
  "function": function MD006(params, onError) {
15
- flattenedLists().forEach((list) => {
15
+ for (const list of flattenedLists()) {
16
16
  if (list.unordered && !list.nesting && (list.indent !== 0)) {
17
- list.items.forEach((item) => {
17
+ for (const item of list.items) {
18
18
  const { lineNumber, line } = item;
19
19
  addErrorDetailIf(
20
20
  onError,
@@ -27,8 +27,8 @@ module.exports = {
27
27
  {
28
28
  "deleteCount": line.length - line.trimStart().length
29
29
  });
30
- });
30
+ }
31
31
  }
32
- });
32
+ }
33
33
  }
34
34
  };
package/lib/md007.js CHANGED
@@ -13,12 +13,14 @@ module.exports = {
13
13
  "function": function MD007(params, onError) {
14
14
  const indent = Number(params.config.indent || 2);
15
15
  const startIndented = !!params.config.start_indented;
16
- flattenedLists().forEach((list) => {
16
+ const startIndent = Number(params.config.start_indent || indent);
17
+ for (const list of flattenedLists()) {
17
18
  if (list.unordered && list.parentsUnordered) {
18
- list.items.forEach((item) => {
19
+ for (const item of list.items) {
19
20
  const { lineNumber, line } = item;
20
- const expectedNesting = list.nesting + (startIndented ? 1 : 0);
21
- const expectedIndent = expectedNesting * indent;
21
+ const expectedIndent =
22
+ (startIndented ? startIndent : 0) +
23
+ (list.nesting * indent);
22
24
  const actualIndent = indentFor(item);
23
25
  let range = null;
24
26
  let editColumn = 1;
@@ -40,8 +42,8 @@ module.exports = {
40
42
  "deleteCount": actualIndent,
41
43
  "insertText": "".padEnd(expectedIndent)
42
44
  });
43
- });
45
+ }
44
46
  }
45
- });
47
+ }
46
48
  }
47
49
  };
package/lib/md009.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addError, filterTokens, forEachInlineCodeSpan, forEachLine,
6
- includesSorted, newLineRe, numericSortAscending } = require("../helpers");
5
+ const { addError, filterTokens, forEachLine, includesSorted,
6
+ numericSortAscending } = require("../helpers");
7
7
  const { lineMetadata } = require("./cache");
8
8
 
9
9
  module.exports = {
@@ -32,19 +32,26 @@ module.exports = {
32
32
  paragraphLineNumbers.push(i + 1);
33
33
  }
34
34
  });
35
- paragraphLineNumbers.sort(numericSortAscending);
35
+ const addLineNumberRange = (start, end) => {
36
+ for (let i = start; i < end; i++) {
37
+ codeInlineLineNumbers.push(i);
38
+ }
39
+ };
36
40
  filterTokens(params, "inline", (token) => {
37
- if (token.children.some((child) => child.type === "code_inline")) {
38
- const tokenLines = params.lines.slice(token.map[0], token.map[1]);
39
- forEachInlineCodeSpan(tokenLines.join("\n"), (code, lineIndex) => {
40
- const codeLineCount = code.split(newLineRe).length;
41
- for (let i = 0; i < codeLineCount; i++) {
42
- codeInlineLineNumbers.push(token.lineNumber + lineIndex + i);
43
- }
44
- });
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]);
45
53
  }
46
54
  });
47
- codeInlineLineNumbers.sort(numericSortAscending);
48
55
  }
49
56
  const expected = (brSpaces < 2) ? 0 : brSpaces;
50
57
  forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
@@ -67,7 +74,7 @@ module.exports = {
67
74
  lineNumber,
68
75
  "Expected: " + (expected === 0 ? "" : "0 or ") +
69
76
  expected + "; Actual: " + trailingSpaces,
70
- null,
77
+ undefined,
71
78
  [ column, trailingSpaces ],
72
79
  {
73
80
  "editColumn": column,
package/lib/md010.js CHANGED
@@ -2,8 +2,9 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addError, forEachLine } = require("../helpers");
6
- const { lineMetadata } = require("./cache");
5
+ const { addError, filterTokens, forEachLine, withinAnyRange } =
6
+ require("../helpers");
7
+ const { codeBlockAndSpanRanges, lineMetadata } = require("./cache");
7
8
 
8
9
  const tabRe = /\t+/g;
9
10
 
@@ -13,28 +14,45 @@ module.exports = {
13
14
  "tags": [ "whitespace", "hard_tab" ],
14
15
  "function": function MD010(params, onError) {
15
16
  const codeBlocks = params.config.code_blocks;
16
- const includeCodeBlocks = (codeBlocks === undefined) ? true : !!codeBlocks;
17
+ const includeCode = (codeBlocks === undefined) ? true : !!codeBlocks;
18
+ const ignoreCodeLanguages = new Set(
19
+ (params.config.ignore_code_languages || [])
20
+ .map((language) => language.toLowerCase())
21
+ );
17
22
  const spacesPerTab = params.config.spaces_per_tab;
18
23
  const spaceMultiplier = (spacesPerTab === undefined) ?
19
24
  1 :
20
25
  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
+ }
33
+ }
34
+ });
21
35
  forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
22
- if (!inCode || includeCodeBlocks) {
36
+ if (includeCode || !inCode) {
23
37
  let match = null;
24
38
  while ((match = tabRe.exec(line)) !== null) {
25
- const column = match.index + 1;
39
+ const { index } = match;
40
+ const column = index + 1;
26
41
  const length = match[0].length;
27
- addError(
28
- onError,
29
- lineIndex + 1,
30
- "Column: " + column,
31
- null,
32
- [ column, length ],
33
- {
34
- "editColumn": column,
35
- "deleteCount": length,
36
- "insertText": "".padEnd(length * spaceMultiplier)
37
- });
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
+ }
38
56
  }
39
57
  }
40
58
  });
package/lib/md011.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addError, forEachLine, overlapsAnyRange } = require("../helpers");
6
- const { inlineCodeSpanRanges, lineMetadata } = require("./cache");
5
+ const { addError, forEachLine, withinAnyRange } = require("../helpers");
6
+ const { codeBlockAndSpanRanges, lineMetadata } = require("./cache");
7
7
 
8
8
  const reversedLinkRe =
9
9
  /(^|[^\\])\(([^)]+)\)\[([^\]^][^\]]*)](?!\()/g;
@@ -13,7 +13,7 @@ module.exports = {
13
13
  "description": "Reversed link syntax",
14
14
  "tags": [ "links" ],
15
15
  "function": function MD011(params, onError) {
16
- const exclusions = inlineCodeSpanRanges();
16
+ const exclusions = codeBlockAndSpanRanges();
17
17
  forEachLine(lineMetadata(), (line, lineIndex, inCode, onFence) => {
18
18
  if (!inCode && !onFence) {
19
19
  let match = null;
@@ -24,7 +24,7 @@ module.exports = {
24
24
  if (
25
25
  !linkText.endsWith("\\") &&
26
26
  !linkDestination.endsWith("\\") &&
27
- !overlapsAnyRange(exclusions, lineIndex, index, length)
27
+ !withinAnyRange(exclusions, lineIndex, index, length)
28
28
  ) {
29
29
  addError(
30
30
  onError,
package/lib/md013.js CHANGED
@@ -3,13 +3,12 @@
3
3
  "use strict";
4
4
 
5
5
  const { addErrorDetailIf, filterTokens, forEachHeading, forEachLine,
6
- includesSorted } = require("../helpers");
6
+ includesSorted, linkReferenceDefinitionRe } = require("../helpers");
7
7
  const { lineMetadata } = require("./cache");
8
8
 
9
9
  const longLineRePrefix = "^.{";
10
10
  const longLineRePostfixRelaxed = "}.*\\s.*$";
11
11
  const longLineRePostfixStrict = "}.+$";
12
- const labelRe = /^\s*\[.*[^\\]]:/;
13
12
  const linkOrImageOnlyLineRe = /^[es]*(lT?L|I)[ES]*$/;
14
13
  const sternModeRe = /^([#>\s]*\s)?\S*$/;
15
14
  const tokenTypeMap = {
@@ -59,11 +58,11 @@ module.exports = {
59
58
  const linkOnlyLineNumbers = [];
60
59
  filterTokens(params, "inline", (token) => {
61
60
  let childTokenTypes = "";
62
- token.children.forEach((child) => {
61
+ for (const child of token.children) {
63
62
  if (child.type !== "text" || child.content !== "") {
64
63
  childTokenTypes += tokenTypeMap[child.type] || "x";
65
64
  }
66
- });
65
+ }
67
66
  if (linkOrImageOnlyLineRe.test(childTokenTypes)) {
68
67
  linkOnlyLineNumbers.push(token.lineNumber);
69
68
  }
@@ -83,7 +82,7 @@ module.exports = {
83
82
  (strict ||
84
83
  (!(stern && sternModeRe.test(line)) &&
85
84
  !includesSorted(linkOnlyLineNumbers, lineNumber) &&
86
- !labelRe.test(line))) &&
85
+ !linkReferenceDefinitionRe.test(line))) &&
87
86
  lengthRe.test(line)) {
88
87
  addErrorDetailIf(
89
88
  onError,
package/lib/md014.js CHANGED
@@ -11,7 +11,7 @@ module.exports = {
11
11
  "description": "Dollar signs used before commands without showing output",
12
12
  "tags": [ "code" ],
13
13
  "function": function MD014(params, onError) {
14
- [ "code_block", "fence" ].forEach((type) => {
14
+ for (const type of [ "code_block", "fence" ]) {
15
15
  filterTokens(params, type, (token) => {
16
16
  const margin = (token.type === "fence") ? 1 : 0;
17
17
  const dollarInstances = [];
@@ -31,7 +31,7 @@ module.exports = {
31
31
  }
32
32
  }
33
33
  if (allDollars) {
34
- dollarInstances.forEach((instance) => {
34
+ for (const instance of dollarInstances) {
35
35
  const [ i, lineTrim, column, length ] = instance;
36
36
  addErrorContext(
37
37
  onError,
@@ -45,9 +45,9 @@ module.exports = {
45
45
  "deleteCount": length
46
46
  }
47
47
  );
48
- });
48
+ }
49
49
  }
50
50
  });
51
- });
51
+ }
52
52
  }
53
53
  };
package/lib/md021.js CHANGED
@@ -5,6 +5,8 @@
5
5
  const { addErrorContext, filterTokens, headingStyleFor } =
6
6
  require("../helpers");
7
7
 
8
+ const closedAtxRe = /^(#+)([ \t]+)([^ \t]|[^ \t].*[^ \t])([ \t]+)(#+)(\s*)$/;
9
+
8
10
  module.exports = {
9
11
  "names": [ "MD021", "no-multiple-space-closed-atx" ],
10
12
  "description": "Multiple spaces inside hashes on closed atx style heading",
@@ -13,7 +15,7 @@ module.exports = {
13
15
  filterTokens(params, "heading_open", (token) => {
14
16
  if (headingStyleFor(token) === "atx_closed") {
15
17
  const { line, lineNumber } = token;
16
- const match = /^(#+)([ \t]+)([^#]+?)([ \t]+)(#+)(\s*)$/.exec(line);
18
+ const match = closedAtxRe.exec(line);
17
19
  if (match) {
18
20
  const [
19
21
  ,
package/lib/md026.js CHANGED
@@ -20,7 +20,7 @@ module.exports = {
20
20
  new RegExp("\\s*[" + escapeForRegExp(punctuation) + "]+$");
21
21
  forEachHeading(params, (heading) => {
22
22
  const { line, lineNumber } = heading;
23
- const trimmedLine = line.replace(/[\s#]*$/, "");
23
+ const trimmedLine = line.replace(/([^\s#])[\s#]+$/, "$1");
24
24
  const match = trailingPunctuationRe.exec(trimmedLine);
25
25
  if (match && !endOfLineHtmlEntityRe.test(trimmedLine)) {
26
26
  const fullMatch = match[0];
package/lib/md027.js CHANGED
@@ -13,7 +13,7 @@ module.exports = {
13
13
  "function": function MD027(params, onError) {
14
14
  let blockquoteNesting = 0;
15
15
  let listItemNesting = 0;
16
- params.tokens.forEach((token) => {
16
+ for (const token of params.tokens) {
17
17
  const { content, lineNumber, type } = token;
18
18
  if (type === "blockquote_open") {
19
19
  blockquoteNesting++;
@@ -51,6 +51,6 @@ module.exports = {
51
51
  }
52
52
  }
53
53
  }
54
- });
54
+ }
55
55
  }
56
56
  };
package/lib/md028.js CHANGED
@@ -11,7 +11,7 @@ module.exports = {
11
11
  "function": function MD028(params, onError) {
12
12
  let prevToken = {};
13
13
  let prevLineNumber = null;
14
- params.tokens.forEach(function forToken(token) {
14
+ for (const token of params.tokens) {
15
15
  if ((token.type === "blockquote_open") &&
16
16
  (prevToken.type === "blockquote_close")) {
17
17
  for (
@@ -25,6 +25,6 @@ module.exports = {
25
25
  if (token.type === "blockquote_open") {
26
26
  prevLineNumber = token.map[1] + 1;
27
27
  }
28
- });
28
+ }
29
29
  }
30
30
  };
package/lib/md029.js CHANGED
@@ -18,7 +18,8 @@ module.exports = {
18
18
  "tags": [ "ol" ],
19
19
  "function": function MD029(params, onError) {
20
20
  const style = String(params.config.style || "one_or_ordered");
21
- flattenedLists().filter((list) => !list.unordered).forEach((list) => {
21
+ const filteredLists = flattenedLists().filter((list) => !list.unordered);
22
+ for (const list of filteredLists) {
22
23
  const { items } = list;
23
24
  let current = 1;
24
25
  let incrementing = false;
@@ -49,7 +50,7 @@ module.exports = {
49
50
  current = 1;
50
51
  }
51
52
  // Validate each list item marker
52
- items.forEach((item) => {
53
+ for (const item of items) {
53
54
  const match = orderedListItemMarkerRe.exec(item.line);
54
55
  if (match) {
55
56
  addErrorDetailIf(onError, item.lineNumber,
@@ -60,7 +61,7 @@ module.exports = {
60
61
  current++;
61
62
  }
62
63
  }
63
- });
64
- });
64
+ }
65
+ }
65
66
  }
66
67
  };
package/lib/md030.js CHANGED
@@ -14,13 +14,13 @@ module.exports = {
14
14
  const olSingle = Number(params.config.ol_single || 1);
15
15
  const ulMulti = Number(params.config.ul_multi || 1);
16
16
  const olMulti = Number(params.config.ol_multi || 1);
17
- flattenedLists().forEach((list) => {
17
+ for (const list of flattenedLists()) {
18
18
  const lineCount = list.lastLineIndex - list.open.map[0];
19
19
  const allSingle = lineCount === list.items.length;
20
20
  const expectedSpaces = list.unordered ?
21
21
  (allSingle ? ulSingle : ulMulti) :
22
22
  (allSingle ? olSingle : olMulti);
23
- list.items.forEach((item) => {
23
+ for (const item of list.items) {
24
24
  const { line, lineNumber } = item;
25
25
  const match = /^[\s>]*\S+(\s*)/.exec(line);
26
26
  const [ { "length": matchLength }, { "length": actualSpaces } ] = match;
@@ -44,7 +44,7 @@ module.exports = {
44
44
  fixInfo
45
45
  );
46
46
  }
47
- });
48
- });
47
+ }
48
+ }
49
49
  }
50
50
  };
package/lib/md031.js CHANGED
@@ -5,7 +5,7 @@
5
5
  const { addErrorContext, forEachLine, isBlankLine } = require("../helpers");
6
6
  const { lineMetadata } = require("./cache");
7
7
 
8
- const codeFencePrefixRe = /^(.*?)\s*[`~]/;
8
+ const codeFencePrefixRe = /^(.*?)[`~]/;
9
9
 
10
10
  module.exports = {
11
11
  "names": [ "MD031", "blanks-around-fences" ],
@@ -24,7 +24,7 @@ module.exports = {
24
24
  const [ , prefix ] = line.match(codeFencePrefixRe) || [];
25
25
  const fixInfo = (prefix === undefined) ? null : {
26
26
  "lineNumber": i + (onTopFence ? 1 : 2),
27
- "insertText": `${prefix}\n`
27
+ "insertText": `${prefix.replace(/[^>]/g, " ").trim()}\n`
28
28
  };
29
29
  addErrorContext(
30
30
  onError,
package/lib/md032.js CHANGED
@@ -13,7 +13,8 @@ module.exports = {
13
13
  "tags": [ "bullet", "ul", "ol", "blank_lines" ],
14
14
  "function": function MD032(params, onError) {
15
15
  const { lines } = params;
16
- flattenedLists().filter((list) => !list.nesting).forEach((list) => {
16
+ const filteredLists = flattenedLists().filter((list) => !list.nesting);
17
+ for (const list of filteredLists) {
17
18
  const firstIndex = list.open.map[0];
18
19
  if (!isBlankLine(lines[firstIndex - 1])) {
19
20
  const line = lines[firstIndex];
@@ -45,6 +46,6 @@ module.exports = {
45
46
  "insertText": `${quotePrefix}\n`
46
47
  });
47
48
  }
48
- });
49
+ }
49
50
  }
50
51
  };
package/lib/md033.js CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addError, forEachLine, unescapeMarkdown } = require("../helpers");
6
- const { lineMetadata } = require("./cache");
5
+ const {
6
+ addError, forEachLine, htmlElementRe, withinAnyRange, unescapeMarkdown
7
+ } = require("../helpers");
8
+ const { codeBlockAndSpanRanges, lineMetadata } = require("./cache");
7
9
 
8
- const htmlElementRe = /<(([A-Za-z][A-Za-z0-9-]*)(?:\s[^>]*)?)\/?>/g;
9
10
  const linkDestinationRe = /]\(\s*$/;
10
- const inlineCodeRe = /^[^`]*(`+[^`]+`+[^`]+)*`+[^`]*$/;
11
11
  // See https://spec.commonmark.org/0.29/#autolinks
12
12
  const emailAddressRe =
13
13
  // eslint-disable-next-line max-len
@@ -21,19 +21,22 @@ module.exports = {
21
21
  let allowedElements = params.config.allowed_elements;
22
22
  allowedElements = Array.isArray(allowedElements) ? allowedElements : [];
23
23
  allowedElements = allowedElements.map((element) => element.toLowerCase());
24
+ const exclusions = codeBlockAndSpanRanges();
24
25
  forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
25
26
  let match = null;
26
27
  // eslint-disable-next-line no-unmodified-loop-condition
27
28
  while (!inCode && ((match = htmlElementRe.exec(line)) !== null)) {
28
29
  const [ tag, content, element ] = match;
29
- if (!allowedElements.includes(element.toLowerCase()) &&
30
+ if (
31
+ !allowedElements.includes(element.toLowerCase()) &&
30
32
  !tag.endsWith("\\>") &&
31
- !emailAddressRe.test(content)) {
33
+ !emailAddressRe.test(content) &&
34
+ !withinAnyRange(exclusions, lineIndex, match.index, match[0].length)
35
+ ) {
32
36
  const prefix = line.substring(0, match.index);
33
- if (!linkDestinationRe.test(prefix) && !inlineCodeRe.test(prefix)) {
37
+ if (!linkDestinationRe.test(prefix)) {
34
38
  const unescaped = unescapeMarkdown(prefix + "<", "_");
35
- if (!unescaped.endsWith("_") &&
36
- ((unescaped + "`").match(/`/g).length % 2)) {
39
+ if (!unescaped.endsWith("_")) {
37
40
  addError(onError, lineIndex + 1, "Element: " + element,
38
41
  null, [ match.index + 1, tag.length ]);
39
42
  }
package/lib/md034.js CHANGED
@@ -11,7 +11,7 @@ module.exports = {
11
11
  "function": function MD034(params, onError) {
12
12
  filterTokens(params, "inline", (token) => {
13
13
  let inLink = false;
14
- token.children.forEach((child) => {
14
+ for (const child of token.children) {
15
15
  const { content, line, lineNumber, type } = child;
16
16
  let match = null;
17
17
  if (type === "link_open") {
@@ -55,7 +55,7 @@ module.exports = {
55
55
  }
56
56
  }
57
57
  }
58
- });
58
+ }
59
59
  });
60
60
  }
61
61
  };
package/lib/md035.js CHANGED
@@ -9,13 +9,18 @@ module.exports = {
9
9
  "description": "Horizontal rule style",
10
10
  "tags": [ "hr" ],
11
11
  "function": function MD035(params, onError) {
12
- let style = String(params.config.style || "consistent");
13
- filterTokens(params, "hr", function forToken(token) {
14
- const lineTrim = token.line.trim();
12
+ let style = String(params.config.style || "consistent").trim();
13
+ filterTokens(params, "hr", (token) => {
14
+ const { line, lineNumber } = token;
15
+ let { markup } = token;
16
+ const match = line.match(/[_*\-\s\t]+$/);
17
+ if (match) {
18
+ markup = match[0].trim();
19
+ }
15
20
  if (style === "consistent") {
16
- style = lineTrim;
21
+ style = markup;
17
22
  }
18
- addErrorDetailIf(onError, token.lineNumber, style, lineTrim);
23
+ addErrorDetailIf(onError, lineNumber, style, markup);
19
24
  });
20
25
  }
21
26
  };
package/lib/md036.js CHANGED
@@ -49,8 +49,8 @@ module.exports = {
49
49
  return base;
50
50
  }
51
51
  let state = base;
52
- params.tokens.forEach(function forToken(token) {
52
+ for (const token of params.tokens) {
53
53
  state = state(token);
54
- });
54
+ }
55
55
  }
56
56
  };
package/lib/md037.js CHANGED
@@ -7,6 +7,7 @@ const { addErrorContext, emphasisMarkersInContent, forEachLine, isBlankLine } =
7
7
  const { lineMetadata } = require("./cache");
8
8
 
9
9
  const emphasisRe = /(^|[^\\]|\\\\)(?:(\*\*?\*?)|(__?_?))/g;
10
+ const embeddedUnderscoreRe = /([A-Za-z0-9])_([A-Za-z0-9])/g;
10
11
  const asteriskListItemMarkerRe = /^([\s>]*)\*(\s+)/;
11
12
  const leftSpaceRe = /^\s+/;
12
13
  const rightSpaceRe = /\s+$/;
@@ -98,14 +99,15 @@ module.exports = {
98
99
  // Emphasis has no meaning here
99
100
  return;
100
101
  }
102
+ let patchedLine = line.replace(embeddedUnderscoreRe, "$1 $2");
101
103
  if (onItemStart) {
102
104
  // Trim overlapping '*' list item marker
103
- line = line.replace(asteriskListItemMarkerRe, "$1 $2");
105
+ patchedLine = patchedLine.replace(asteriskListItemMarkerRe, "$1 $2");
104
106
  }
105
107
  let match = null;
106
108
  // Match all emphasis-looking runs in the line...
107
- while ((match = emphasisRe.exec(line))) {
108
- const ignoreMarkersForLine = ignoreMarkersByLine[lineIndex] || [];
109
+ while ((match = emphasisRe.exec(patchedLine))) {
110
+ const ignoreMarkersForLine = ignoreMarkersByLine[lineIndex];
109
111
  const matchIndex = match.index + match[1].length;
110
112
  if (ignoreMarkersForLine.includes(matchIndex)) {
111
113
  // Ignore emphasis markers inside code spans and links
package/lib/md038.js CHANGED
@@ -7,7 +7,11 @@ const { addErrorContext, filterTokens, forEachInlineCodeSpan, newLineRe } =
7
7
 
8
8
  const leftSpaceRe = /^\s([^`]|$)/;
9
9
  const rightSpaceRe = /[^`]\s$/;
10
- const singleLeftRightSpaceRe = /^\s(?:\S.*\S|\S)\s$/;
10
+
11
+ const spaceInsideCodeInline = (token) => (
12
+ (token.type === "code_inline") &&
13
+ (leftSpaceRe.test(token.content) || rightSpaceRe.test(token.content))
14
+ );
11
15
 
12
16
  module.exports = {
13
17
  "names": [ "MD038", "no-space-in-code" ],
@@ -15,7 +19,7 @@ module.exports = {
15
19
  "tags": [ "whitespace", "code" ],
16
20
  "function": function MD038(params, onError) {
17
21
  filterTokens(params, "inline", (token) => {
18
- if (token.children.some((child) => child.type === "code_inline")) {
22
+ if (token.children.some(spaceInsideCodeInline)) {
19
23
  const tokenLines = params.lines.slice(token.map[0], token.map[1]);
20
24
  forEachInlineCodeSpan(
21
25
  tokenLines.join("\n"),
@@ -33,8 +37,7 @@ module.exports = {
33
37
  rangeLineOffset = codeLines.length - 1;
34
38
  fixIndex = 0;
35
39
  }
36
- const allowed = singleLeftRightSpaceRe.test(code);
37
- if ((left || right) && !allowed) {
40
+ if (left || right) {
38
41
  const codeLinesRange = codeLines[rangeLineOffset];
39
42
  if (codeLines.length > 1) {
40
43
  rangeLength = codeLinesRange.length + tickCount;