markdownlint 0.26.1 → 0.27.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 (85) hide show
  1. package/CONTRIBUTING.md +81 -59
  2. package/README.md +424 -301
  3. package/demo/markdownlint-browser.js +445 -210
  4. package/doc/CustomRules.md +101 -50
  5. package/doc/Prettier.md +14 -10
  6. package/doc/ReleaseProcess.md +20 -0
  7. package/doc/Rules.md +464 -296
  8. package/doc/md001.md +37 -0
  9. package/doc/md002.md +37 -0
  10. package/doc/md003.md +59 -0
  11. package/doc/md004.md +49 -0
  12. package/doc/md005.md +53 -0
  13. package/doc/md006.md +46 -0
  14. package/doc/md007.md +55 -0
  15. package/doc/md009.md +51 -0
  16. package/doc/md010.md +56 -0
  17. package/doc/md011.md +30 -0
  18. package/doc/md012.md +38 -0
  19. package/doc/md013.md +60 -0
  20. package/doc/md014.md +54 -0
  21. package/doc/md018.md +27 -0
  22. package/doc/md019.md +28 -0
  23. package/doc/md020.md +29 -0
  24. package/doc/md021.md +31 -0
  25. package/doc/md022.md +46 -0
  26. package/doc/md023.md +26 -0
  27. package/doc/md024.md +47 -0
  28. package/doc/md025.md +49 -0
  29. package/doc/md026.md +41 -0
  30. package/doc/md027.md +24 -0
  31. package/doc/md028.md +40 -0
  32. package/doc/md029.md +98 -0
  33. package/doc/md030.md +82 -0
  34. package/doc/md031.md +50 -0
  35. package/doc/md032.md +38 -0
  36. package/doc/md033.md +27 -0
  37. package/doc/md034.md +44 -0
  38. package/doc/md035.md +42 -0
  39. package/doc/md036.md +45 -0
  40. package/doc/md037.md +37 -0
  41. package/doc/md038.md +40 -0
  42. package/doc/md039.md +21 -0
  43. package/doc/md040.md +52 -0
  44. package/doc/md041.md +49 -0
  45. package/doc/md042.md +32 -0
  46. package/doc/md043.md +72 -0
  47. package/doc/md044.md +35 -0
  48. package/doc/md045.md +34 -0
  49. package/doc/md046.md +40 -0
  50. package/doc/md047.md +34 -0
  51. package/doc/md048.md +41 -0
  52. package/doc/md049.md +32 -0
  53. package/doc/md050.md +32 -0
  54. package/doc/md051.md +50 -0
  55. package/doc/md052.md +33 -0
  56. package/doc/md053.md +38 -0
  57. package/helpers/README.md +35 -15
  58. package/helpers/helpers.js +234 -70
  59. package/helpers/package.json +2 -2
  60. package/lib/constants.js +8 -1
  61. package/lib/markdownlint.js +7 -12
  62. package/lib/md011.js +2 -2
  63. package/lib/md013.js +7 -6
  64. package/lib/md019.js +1 -1
  65. package/lib/md022.js +15 -3
  66. package/lib/md023.js +1 -1
  67. package/lib/md026.js +1 -1
  68. package/lib/md032.js +4 -5
  69. package/lib/md033.js +16 -5
  70. package/lib/md034.js +70 -45
  71. package/lib/md035.js +1 -1
  72. package/lib/md037.js +25 -22
  73. package/lib/md038.js +1 -1
  74. package/lib/md039.js +1 -1
  75. package/lib/md040.js +21 -2
  76. package/lib/md043.js +4 -2
  77. package/lib/md044.js +4 -3
  78. package/lib/md051.js +43 -11
  79. package/lib/md053.js +18 -11
  80. package/lib/rules.js +1 -1
  81. package/package.json +31 -21
  82. package/schema/.markdownlint.jsonc +16 -4
  83. package/schema/.markdownlint.yaml +13 -3
  84. package/schema/markdownlint-config-schema.json +45 -5
  85. package/style/prettier.json +26 -0
@@ -2,8 +2,8 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const path = require("path");
6
- const { promisify } = require("util");
5
+ const path = require("node:path");
6
+ const { promisify } = require("node:util");
7
7
  const markdownIt = require("markdown-it");
8
8
  const { deprecatedRuleNames } = require("./constants");
9
9
  const rules = require("./rules");
@@ -233,11 +233,6 @@ function annotateAndFreezeTokens(tokens, lines) {
233
233
  if (!token.map && trMap) {
234
234
  token.map = [ ...trMap ];
235
235
  }
236
- // Adjust maps for math blocks
237
- if (helpers.isMathBlock(token) && token.map[1]) {
238
- // markdown-it-texmath plugin does not account for math_block_end
239
- token.map[1]++;
240
- }
241
236
  // Update token metadata
242
237
  if (token.map) {
243
238
  token.line = lines[token.map[0]];
@@ -879,7 +874,7 @@ function lintInput(options, synchronous, callback) {
879
874
  // @ts-ignore
880
875
  md.use(...plugin);
881
876
  }
882
- const fs = options.fs || require("fs");
877
+ const fs = options.fs || require("node:fs");
883
878
  const results = newResults(ruleList);
884
879
  let done = false;
885
880
  let concurrency = 0;
@@ -1085,10 +1080,10 @@ function readConfig(file, parsers, fs, callback) {
1085
1080
  }
1086
1081
  }
1087
1082
  if (!fs) {
1088
- fs = require("fs");
1083
+ fs = require("node:fs");
1089
1084
  }
1090
1085
  // Read file
1091
- const os = require("os");
1086
+ const os = require("node:os");
1092
1087
  file = helpers.expandTildePath(file, os);
1093
1088
  fs.readFile(file, "utf8", (err, content) => {
1094
1089
  if (err) {
@@ -1160,10 +1155,10 @@ function readConfigPromise(file, parsers, fs) {
1160
1155
  */
1161
1156
  function readConfigSync(file, parsers, fs) {
1162
1157
  if (!fs) {
1163
- fs = require("fs");
1158
+ fs = require("node:fs");
1164
1159
  }
1165
1160
  // Read file
1166
- const os = require("os");
1161
+ const os = require("node:os");
1167
1162
  file = helpers.expandTildePath(file, os);
1168
1163
  const content = fs.readFileSync(file, "utf8");
1169
1164
  // Try to parse file
package/lib/md011.js CHANGED
@@ -6,7 +6,7 @@ const { addError, forEachLine, withinAnyRange } = require("../helpers");
6
6
  const { codeBlockAndSpanRanges, lineMetadata } = require("./cache");
7
7
 
8
8
  const reversedLinkRe =
9
- /(^|[^\\])\(([^)]+)\)\[([^\]^][^\]]*)](?!\()/g;
9
+ /(^|[^\\])\(([^()]+)\)\[([^\]^][^\]]*)\](?!\()/g;
10
10
 
11
11
  module.exports = {
12
12
  "names": [ "MD011", "no-reversed-links" ],
@@ -30,7 +30,7 @@ module.exports = {
30
30
  onError,
31
31
  lineIndex + 1,
32
32
  reversedLink.slice(preChar.length),
33
- null,
33
+ undefined,
34
34
  [ index + 1, length ],
35
35
  {
36
36
  "editColumn": index + 1,
package/lib/md013.js CHANGED
@@ -3,14 +3,14 @@
3
3
  "use strict";
4
4
 
5
5
  const { addErrorDetailIf, filterTokens, forEachHeading, forEachLine,
6
- includesSorted, linkReferenceDefinitionRe } = require("../helpers");
7
- const { lineMetadata } = require("./cache");
6
+ includesSorted } = require("../helpers");
7
+ const { lineMetadata, referenceLinkImageData } = require("./cache");
8
8
 
9
9
  const longLineRePrefix = "^.{";
10
10
  const longLineRePostfixRelaxed = "}.*\\s.*$";
11
11
  const longLineRePostfixStrict = "}.+$";
12
- const linkOrImageOnlyLineRe = /^[es]*(lT?L|I)[ES]*$/;
13
- const sternModeRe = /^([#>\s]*\s)?\S*$/;
12
+ const linkOrImageOnlyLineRe = /^[es]*(?:lT?L|I)[ES]*$/;
13
+ const sternModeRe = /^(?:[#>\s]*\s)?\S*$/;
14
14
  const tokenTypeMap = {
15
15
  "em_open": "e",
16
16
  "em_close": "E",
@@ -67,6 +67,7 @@ module.exports = {
67
67
  linkOnlyLineNumbers.push(token.lineNumber);
68
68
  }
69
69
  });
70
+ const { definitionLineIndices } = referenceLinkImageData();
70
71
  forEachLine(lineMetadata(), (line, lineIndex, inCode, onFence, inTable) => {
71
72
  const lineNumber = lineIndex + 1;
72
73
  const isHeading = includesSorted(headingLineNumbers, lineNumber);
@@ -79,10 +80,10 @@ module.exports = {
79
80
  if ((includeCodeBlocks || !inCode) &&
80
81
  (includeTables || !inTable) &&
81
82
  (includeHeadings || !isHeading) &&
83
+ !includesSorted(definitionLineIndices, lineIndex) &&
82
84
  (strict ||
83
85
  (!(stern && sternModeRe.test(line)) &&
84
- !includesSorted(linkOnlyLineNumbers, lineNumber) &&
85
- !linkReferenceDefinitionRe.test(line))) &&
86
+ !includesSorted(linkOnlyLineNumbers, lineNumber))) &&
86
87
  lengthRe.test(line)) {
87
88
  addErrorDetailIf(
88
89
  onError,
package/lib/md019.js CHANGED
@@ -13,7 +13,7 @@ module.exports = {
13
13
  filterTokens(params, "heading_open", (token) => {
14
14
  if (headingStyleFor(token) === "atx") {
15
15
  const { line, lineNumber } = token;
16
- const match = /^(#+)([ \t]{2,})(?:\S)/.exec(line);
16
+ const match = /^(#+)([ \t]{2,})\S/.exec(line);
17
17
  if (match) {
18
18
  const [
19
19
  ,
package/lib/md022.js CHANGED
@@ -2,7 +2,17 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorDetailIf, filterTokens, isBlankLine } = require("../helpers");
5
+ const { addErrorDetailIf, blockquotePrefixRe, filterTokens, isBlankLine } =
6
+ require("../helpers");
7
+
8
+ const getBlockQuote = (str, count) => (
9
+ (str || "")
10
+ .match(blockquotePrefixRe)[0]
11
+ .trimEnd()
12
+ // eslint-disable-next-line unicorn/prefer-spread
13
+ .concat("\n")
14
+ .repeat(count)
15
+ );
6
16
 
7
17
  module.exports = {
8
18
  "names": [ "MD022", "blanks-around-headings", "blanks-around-headers" ],
@@ -31,7 +41,8 @@ module.exports = {
31
41
  lines[topIndex].trim(),
32
42
  null,
33
43
  {
34
- "insertText": "".padEnd(linesAbove - actualAbove, "\n")
44
+ "insertText":
45
+ getBlockQuote(lines[topIndex - 1], linesAbove - actualAbove)
35
46
  });
36
47
  let actualBelow = 0;
37
48
  for (let i = 0; i < linesBelow; i++) {
@@ -49,7 +60,8 @@ module.exports = {
49
60
  null,
50
61
  {
51
62
  "lineNumber": nextIndex + 1,
52
- "insertText": "".padEnd(linesBelow - actualBelow, "\n")
63
+ "insertText":
64
+ getBlockQuote(lines[nextIndex], linesBelow - actualBelow)
53
65
  });
54
66
  });
55
67
  }
package/lib/md023.js CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  const { addErrorContext, filterTokens } = require("../helpers");
6
6
 
7
- const spaceBeforeHeadingRe = /^((?:\s+)|(?:[>\s]+\s\s))[^>\s]/;
7
+ const spaceBeforeHeadingRe = /^(\s+|[>\s]+\s\s)[^>\s]/;
8
8
 
9
9
  module.exports = {
10
10
  "names": [ "MD023", "heading-start-left", "header-start-left" ],
package/lib/md026.js CHANGED
@@ -5,7 +5,7 @@
5
5
  const { addError, allPunctuationNoQuestion, escapeForRegExp, forEachHeading } =
6
6
  require("../helpers");
7
7
 
8
- const endOfLineHtmlEntityRe = /&#?[0-9a-zA-Z]+;$/;
8
+ const endOfLineHtmlEntityRe = /&#?[\da-zA-Z]+;$/;
9
9
 
10
10
  module.exports = {
11
11
  "names": [ "MD026", "no-trailing-punctuation" ],
package/lib/md032.js CHANGED
@@ -2,11 +2,10 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorContext, isBlankLine } = require("../helpers");
5
+ const { addErrorContext, blockquotePrefixRe, isBlankLine } =
6
+ require("../helpers");
6
7
  const { flattenedLists } = require("./cache");
7
8
 
8
- const quotePrefixRe = /^[>\s]*/;
9
-
10
9
  module.exports = {
11
10
  "names": [ "MD032", "blanks-around-lists" ],
12
11
  "description": "Lists should be surrounded by blank lines",
@@ -18,7 +17,7 @@ module.exports = {
18
17
  const firstIndex = list.open.map[0];
19
18
  if (!isBlankLine(lines[firstIndex - 1])) {
20
19
  const line = lines[firstIndex];
21
- const quotePrefix = line.match(quotePrefixRe)[0].trimEnd();
20
+ const quotePrefix = line.match(blockquotePrefixRe)[0].trimEnd();
22
21
  addErrorContext(
23
22
  onError,
24
23
  firstIndex + 1,
@@ -33,7 +32,7 @@ module.exports = {
33
32
  const lastIndex = list.lastLineIndex - 1;
34
33
  if (!isBlankLine(lines[lastIndex + 1])) {
35
34
  const line = lines[lastIndex];
36
- const quotePrefix = line.match(quotePrefixRe)[0].trimEnd();
35
+ const quotePrefix = line.match(blockquotePrefixRe)[0].trimEnd();
37
36
  addErrorContext(
38
37
  onError,
39
38
  lastIndex + 1,
package/lib/md033.js CHANGED
@@ -5,13 +5,14 @@
5
5
  const {
6
6
  addError, forEachLine, htmlElementRe, withinAnyRange, unescapeMarkdown
7
7
  } = require("../helpers");
8
- const { codeBlockAndSpanRanges, lineMetadata } = require("./cache");
8
+ const { codeBlockAndSpanRanges, lineMetadata, referenceLinkImageData } =
9
+ require("./cache");
9
10
 
10
- const linkDestinationRe = /]\(\s*$/;
11
+ const linkDestinationRe = /\]\(\s*$/;
11
12
  // See https://spec.commonmark.org/0.29/#autolinks
12
13
  const emailAddressRe =
13
14
  // eslint-disable-next-line max-len
14
- /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
15
+ /^[\w.!#$%&'*+/=?^`{|}~-]+@[a-zA-Z\d](?:[a-zA-Z\d-]{0,61}[a-zA-Z\d])?(?:\.[a-zA-Z\d](?:[a-zA-Z\d-]{0,61}[a-zA-Z\d])?)*$/;
15
16
 
16
17
  module.exports = {
17
18
  "names": [ "MD033", "no-inline-html" ],
@@ -22,6 +23,15 @@ module.exports = {
22
23
  allowedElements = Array.isArray(allowedElements) ? allowedElements : [];
23
24
  allowedElements = allowedElements.map((element) => element.toLowerCase());
24
25
  const exclusions = codeBlockAndSpanRanges();
26
+ const { references, definitionLineIndices } = referenceLinkImageData();
27
+ for (const datas of references.values()) {
28
+ for (const data of datas) {
29
+ const [ lineIndex, index, , textLength, labelLength ] = data;
30
+ if (labelLength > 0) {
31
+ exclusions.push([ lineIndex, index + 3 + textLength, labelLength ]);
32
+ }
33
+ }
34
+ }
25
35
  forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
26
36
  let match = null;
27
37
  // eslint-disable-next-line no-unmodified-loop-condition
@@ -31,14 +41,15 @@ module.exports = {
31
41
  !allowedElements.includes(element.toLowerCase()) &&
32
42
  !tag.endsWith("\\>") &&
33
43
  !emailAddressRe.test(content) &&
34
- !withinAnyRange(exclusions, lineIndex, match.index, match[0].length)
44
+ !withinAnyRange(exclusions, lineIndex, match.index, tag.length) &&
45
+ !definitionLineIndices.includes(lineIndex)
35
46
  ) {
36
47
  const prefix = line.substring(0, match.index);
37
48
  if (!linkDestinationRe.test(prefix)) {
38
49
  const unescaped = unescapeMarkdown(prefix + "<", "_");
39
50
  if (!unescaped.endsWith("_")) {
40
51
  addError(onError, lineIndex + 1, "Element: " + element,
41
- null, [ match.index + 1, tag.length ]);
52
+ undefined, [ match.index + 1, tag.length ]);
42
53
  }
43
54
  }
44
55
  }
package/lib/md034.js CHANGED
@@ -2,60 +2,85 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorContext, bareUrlRe, filterTokens } = require("../helpers");
5
+ const { addErrorContext, filterTokens, funcExpExec, urlFe, withinAnyRange } =
6
+ require("../helpers");
7
+ const { codeBlockAndSpanRanges, htmlElementRanges, referenceLinkImageData } =
8
+ require("./cache");
9
+
10
+ const htmlLinkRe = /<a(?:\s[^>]*)?>[^<>]*<\/a\s*>/gi;
6
11
 
7
12
  module.exports = {
8
13
  "names": [ "MD034", "no-bare-urls" ],
9
14
  "description": "Bare URL used",
10
15
  "tags": [ "links", "url" ],
11
16
  "function": function MD034(params, onError) {
12
- filterTokens(params, "inline", (token) => {
13
- let inLink = false;
14
- for (const child of token.children) {
15
- const { content, line, lineNumber, type } = child;
17
+ const { lines } = params;
18
+ const codeExclusions = [
19
+ ...codeBlockAndSpanRanges(),
20
+ ...htmlElementRanges()
21
+ ];
22
+ filterTokens(params, "html_block", (token) => {
23
+ for (let i = token.map[0]; i < token.map[1]; i++) {
24
+ codeExclusions.push([ i, 0, lines[i].length ]);
25
+ }
26
+ });
27
+ const { definitionLineIndices } = referenceLinkImageData();
28
+ for (const [ lineIndex, line ] of lines.entries()) {
29
+ if (definitionLineIndices[0] === lineIndex) {
30
+ definitionLineIndices.shift();
31
+ } else {
16
32
  let match = null;
17
- if (type === "link_open") {
18
- inLink = true;
19
- } else if (type === "link_close") {
20
- inLink = false;
21
- } else if ((type === "text") && !inLink) {
22
- while ((match = bareUrlRe.exec(content)) !== null) {
23
- const [ bareUrl ] = match;
24
- const matchIndex = match.index;
25
- const bareUrlLength = bareUrl.length;
26
- // Allow "[https://example.com]" to avoid conflicts with
27
- // MD011/no-reversed-links; allow quoting as another way
28
- // of deliberately including a bare URL
29
- const leftChar = content[matchIndex - 1];
30
- const rightChar = content[matchIndex + bareUrlLength];
31
- if (
32
- !((leftChar === "[") && (rightChar === "]")) &&
33
- !((leftChar === "\"") && (rightChar === "\"")) &&
34
- !((leftChar === "'") && (rightChar === "'"))
35
- ) {
36
- const index = line.indexOf(content);
37
- const range = (index === -1) ? null : [
38
- index + matchIndex + 1,
39
- bareUrlLength
40
- ];
41
- const fixInfo = range ? {
42
- "editColumn": range[0],
43
- "deleteCount": range[1],
44
- "insertText": `<${bareUrl}>`
45
- } : null;
46
- addErrorContext(
47
- onError,
48
- lineNumber,
49
- bareUrl,
50
- null,
51
- null,
52
- range,
53
- fixInfo
54
- );
55
- }
33
+ const lineExclusions = [];
34
+ while ((match = htmlLinkRe.exec(line)) !== null) {
35
+ lineExclusions.push([ lineIndex, match.index, match[0].length ]);
36
+ }
37
+ while ((match = funcExpExec(urlFe, line)) !== null) {
38
+ const [ bareUrl ] = match;
39
+ // @ts-ignore
40
+ const matchIndex = match.index;
41
+ const bareUrlLength = bareUrl.length;
42
+ const prefix = line.slice(0, matchIndex);
43
+ const postfix = line.slice(matchIndex + bareUrlLength);
44
+ if (
45
+ // Allow <...> to avoid reporting non-bare links
46
+ !(prefix.endsWith("<") && postfix.startsWith(">")) &&
47
+ // Allow >...</ to avoid reporting <code>...</code>
48
+ !(prefix.endsWith(">") && postfix.startsWith("</")) &&
49
+ // Allow "..." and '...' to allow quoting a bare link
50
+ !(prefix.endsWith("\"") && postfix.startsWith("\"")) &&
51
+ !(prefix.endsWith("'") && postfix.startsWith("'")) &&
52
+ // Allow ](... to avoid reporting Markdown-style links
53
+ !(/\]\(\s*$/.test(prefix)) &&
54
+ // Allow [...] to avoid MD011/no-reversed-links and nested links
55
+ !(/\[[^\]]*$/.test(prefix) && /^[^[]*\]/.test(postfix)) &&
56
+ !withinAnyRange(
57
+ lineExclusions, lineIndex, matchIndex, bareUrlLength
58
+ ) &&
59
+ !withinAnyRange(
60
+ codeExclusions, lineIndex, matchIndex, bareUrlLength
61
+ )
62
+ ) {
63
+ const range = [
64
+ matchIndex + 1,
65
+ bareUrlLength
66
+ ];
67
+ const fixInfo = {
68
+ "editColumn": range[0],
69
+ "deleteCount": range[1],
70
+ "insertText": `<${bareUrl}>`
71
+ };
72
+ addErrorContext(
73
+ onError,
74
+ lineIndex + 1,
75
+ bareUrl,
76
+ null,
77
+ null,
78
+ range,
79
+ fixInfo
80
+ );
56
81
  }
57
82
  }
58
83
  }
59
- });
84
+ }
60
85
  }
61
86
  };
package/lib/md035.js CHANGED
@@ -13,7 +13,7 @@ module.exports = {
13
13
  filterTokens(params, "hr", (token) => {
14
14
  const { line, lineNumber } = token;
15
15
  let { markup } = token;
16
- const match = line.match(/[_*\-\s\t]+$/);
16
+ const match = line.match(/[_*\-\s]+$/);
17
17
  if (match) {
18
18
  markup = match[0].trim();
19
19
  }
package/lib/md037.js CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorContext, emphasisMarkersInContent, forEachLine, isBlankLine } =
6
- require("../helpers");
7
- const { lineMetadata } = require("./cache");
5
+ const { addErrorContext, emphasisMarkersInContent, forEachLine, isBlankLine,
6
+ withinAnyRange } = require("../helpers");
7
+ const { htmlElementRanges, lineMetadata } = require("./cache");
8
8
 
9
- const emphasisRe = /(^|[^\\]|\\\\)(?:(\*\*?\*?)|(__?_?))/g;
10
- const embeddedUnderscoreRe = /([A-Za-z0-9])_([A-Za-z0-9])/g;
9
+ const emphasisRe = /(^|[^\\]|\\\\)(?:(\*{1,3})|(_{1,3}))/g;
10
+ const embeddedUnderscoreRe = /([A-Za-z\d])_([A-Za-z\d])/g;
11
11
  const asteriskListItemMarkerRe = /^([\s>]*)\*(\s+)/;
12
12
  const leftSpaceRe = /^\s+/;
13
13
  const rightSpaceRe = /\s+$/;
@@ -18,6 +18,7 @@ module.exports = {
18
18
  "description": "Spaces inside emphasis markers",
19
19
  "tags": [ "whitespace", "emphasis" ],
20
20
  "function": function MD037(params, onError) {
21
+ const exclusions = htmlElementRanges();
21
22
  // eslint-disable-next-line init-declarations
22
23
  let effectiveEmphasisLength, emphasisIndex, emphasisKind, emphasisLength,
23
24
  pendingError = null;
@@ -50,25 +51,27 @@ module.exports = {
50
51
  // Report the violation
51
52
  const contextStart = emphasisIndex - emphasisLength;
52
53
  const contextEnd = matchIndex + contextLength;
53
- const context = line.substring(contextStart, contextEnd);
54
54
  const column = contextStart + 1;
55
55
  const length = contextEnd - contextStart;
56
- const leftMarker = line.substring(contextStart, emphasisIndex);
57
- const rightMarker = match ? (match[2] || match[3]) : "";
58
- const fixedText = `${leftMarker}${content.trim()}${rightMarker}`;
59
- return [
60
- onError,
61
- lineIndex + 1,
62
- context,
63
- leftSpace,
64
- rightSpace,
65
- [ column, length ],
66
- {
67
- "editColumn": column,
68
- "deleteCount": length,
69
- "insertText": fixedText
70
- }
71
- ];
56
+ if (!withinAnyRange(exclusions, lineIndex, column, length)) {
57
+ const context = line.substring(contextStart, contextEnd);
58
+ const leftMarker = line.substring(contextStart, emphasisIndex);
59
+ const rightMarker = match ? (match[2] || match[3]) : "";
60
+ const fixedText = `${leftMarker}${content.trim()}${rightMarker}`;
61
+ return [
62
+ onError,
63
+ lineIndex + 1,
64
+ context,
65
+ leftSpace,
66
+ rightSpace,
67
+ [ column, length ],
68
+ {
69
+ "editColumn": column,
70
+ "deleteCount": length,
71
+ "insertText": fixedText
72
+ }
73
+ ];
74
+ }
72
75
  }
73
76
  return null;
74
77
  }
package/lib/md038.js CHANGED
@@ -5,7 +5,7 @@
5
5
  const { addErrorContext, filterTokens, forEachInlineCodeSpan, newLineRe } =
6
6
  require("../helpers");
7
7
 
8
- const leftSpaceRe = /^\s([^`]|$)/;
8
+ const leftSpaceRe = /^\s(?:[^`]|$)/;
9
9
  const rightSpaceRe = /[^`]\s$/;
10
10
 
11
11
  const spaceInsideCodeInline = (token) => (
package/lib/md039.js CHANGED
@@ -5,7 +5,7 @@
5
5
  const { addErrorContext, filterTokens } = require("../helpers");
6
6
 
7
7
  const spaceInLinkRe =
8
- /\[(?:\s+(?:[^\]]*?)\s*|(?:[^\]]*?)\s+)](?=((?:\([^)]*\))|(?:\[[^\]]*\])))/;
8
+ /\[(?:\s[^\]]*|[^\]]*?\s)\](?=(\([^)]*\)|\[[^\]]*\]))/;
9
9
 
10
10
  module.exports = {
11
11
  "names": [ "MD039", "no-space-in-links" ],
package/lib/md040.js CHANGED
@@ -2,16 +2,35 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorContext, filterTokens } = require("../helpers");
5
+ const { addError, addErrorContext, filterTokens } = require("../helpers");
6
6
 
7
7
  module.exports = {
8
8
  "names": [ "MD040", "fenced-code-language" ],
9
9
  "description": "Fenced code blocks should have a language specified",
10
10
  "tags": [ "code", "language" ],
11
11
  "function": function MD040(params, onError) {
12
+ let allowed = params.config.allowed_languages;
13
+ allowed = Array.isArray(allowed) ? allowed : [];
14
+ const languageOnly = !!params.config.language_only;
15
+
12
16
  filterTokens(params, "fence", function forToken(token) {
13
- if (!token.info.trim()) {
17
+ const lang = token.info.trim().split(/\s+/u).shift();
18
+ if (lang === "") {
14
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
+ );
15
34
  }
16
35
  });
17
36
  }
package/lib/md043.js CHANGED
@@ -11,6 +11,7 @@ module.exports = {
11
11
  "tags": [ "headings", "headers" ],
12
12
  "function": function MD043(params, onError) {
13
13
  const requiredHeadings = params.config.headings || params.config.headers;
14
+ const matchCase = params.config.match_case || false;
14
15
  if (Array.isArray(requiredHeadings)) {
15
16
  const levels = {};
16
17
  for (const level of [ 1, 2, 3, 4, 5, 6 ]) {
@@ -21,6 +22,7 @@ module.exports = {
21
22
  let hasError = false;
22
23
  let anyHeadings = false;
23
24
  const getExpected = () => requiredHeadings[i++] || "[None]";
25
+ const handleCase = (str) => (matchCase ? str : str.toLowerCase());
24
26
  forEachHeading(params, (heading, content) => {
25
27
  if (!hasError) {
26
28
  anyHeadings = true;
@@ -28,13 +30,13 @@ module.exports = {
28
30
  const expected = getExpected();
29
31
  if (expected === "*") {
30
32
  const nextExpected = getExpected();
31
- if (nextExpected.toLowerCase() !== actual.toLowerCase()) {
33
+ if (handleCase(nextExpected) !== handleCase(actual)) {
32
34
  matchAny = true;
33
35
  i--;
34
36
  }
35
37
  } else if (expected === "+") {
36
38
  matchAny = true;
37
- } else if (expected.toLowerCase() === actual.toLowerCase()) {
39
+ } else if (handleCase(expected) === handleCase(actual)) {
38
40
  matchAny = false;
39
41
  } else if (matchAny) {
40
42
  i--;
package/lib/md044.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorDetailIf, bareUrlRe, escapeForRegExp, forEachLine,
6
- forEachLink, withinAnyRange, linkReferenceDefinitionRe } =
5
+ const { addErrorDetailIf, escapeForRegExp, forEachLine, forEachLink,
6
+ funcExpExec, linkReferenceDefinitionRe, urlFe, withinAnyRange } =
7
7
  require("../helpers");
8
8
  const { codeBlockAndSpanRanges, htmlElementRanges, lineMetadata } =
9
9
  require("./cache");
@@ -28,7 +28,8 @@ module.exports = {
28
28
  exclusions.push([ lineIndex, 0, line.length ]);
29
29
  } else {
30
30
  let match = null;
31
- while ((match = bareUrlRe.exec(line)) !== null) {
31
+ while ((match = funcExpExec(urlFe, line)) !== null) {
32
+ // @ts-ignore
32
33
  exclusions.push([ lineIndex, match.index, match[0].length ]);
33
34
  }
34
35
  forEachLink(line, (index, _, text, destination) => {