markdownlint 0.26.2 → 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 (83) hide show
  1. package/CONTRIBUTING.md +81 -59
  2. package/README.md +424 -303
  3. package/demo/markdownlint-browser.js +374 -144
  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 +453 -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 +204 -42
  59. package/helpers/package.json +2 -2
  60. package/lib/constants.js +8 -1
  61. package/lib/markdownlint.js +7 -7
  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/md033.js +15 -4
  69. package/lib/md034.js +70 -45
  70. package/lib/md035.js +1 -1
  71. package/lib/md037.js +2 -2
  72. package/lib/md038.js +1 -1
  73. package/lib/md039.js +1 -1
  74. package/lib/md040.js +21 -2
  75. package/lib/md043.js +4 -2
  76. package/lib/md044.js +4 -3
  77. package/lib/md051.js +37 -5
  78. package/lib/rules.js +1 -1
  79. package/package.json +31 -21
  80. package/schema/.markdownlint.jsonc +10 -3
  81. package/schema/.markdownlint.yaml +8 -2
  82. package/schema/markdownlint-config-schema.json +27 -3
  83. package/style/prettier.json +26 -0
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/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,7 +41,8 @@ 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)) {
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
@@ -6,8 +6,8 @@ const { addErrorContext, emphasisMarkersInContent, forEachLine, isBlankLine,
6
6
  withinAnyRange } = require("../helpers");
7
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+$/;
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) => {
package/lib/md051.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addError, escapeForRegExp, filterTokens, forEachInlineChild,
6
- forEachHeading, htmlElementRe } = require("../helpers");
5
+ const { addError, addErrorDetailIf, escapeForRegExp, filterTokens,
6
+ forEachInlineChild, forEachHeading, htmlElementRe } = require("../helpers");
7
7
 
8
8
  // Regular expression for identifying HTML anchor names
9
9
  const idRe = /\sid\s*=\s*['"]?([^'"\s>]+)/iu;
@@ -73,14 +73,46 @@ module.exports = {
73
73
  if (id && (id.length > 1) && (id[0] === "#") && !fragments.has(id)) {
74
74
  let context = id;
75
75
  let range = null;
76
+ let fixInfo = null;
76
77
  const match = line.match(
77
78
  new RegExp(`\\[.*?\\]\\(${escapeForRegExp(context)}\\)`)
78
79
  );
79
80
  if (match) {
80
- context = match[0];
81
- range = [ match.index + 1, match[0].length ];
81
+ [ context ] = match;
82
+ const index = match.index;
83
+ const length = context.length;
84
+ range = [ index + 1, length ];
85
+ fixInfo = {
86
+ "editColumn": index + (length - id.length),
87
+ "deleteCount": id.length,
88
+ "insertText": null
89
+ };
90
+ }
91
+ const idLower = id.toLowerCase();
92
+ const mixedCaseKey = [ ...fragments.keys() ]
93
+ .find((key) => idLower === key.toLowerCase());
94
+ if (mixedCaseKey) {
95
+ (fixInfo || {}).insertText = mixedCaseKey;
96
+ addErrorDetailIf(
97
+ onError,
98
+ lineNumber,
99
+ mixedCaseKey,
100
+ id,
101
+ undefined,
102
+ context,
103
+ range,
104
+ fixInfo
105
+ );
106
+ } else {
107
+ addError(
108
+ onError,
109
+ lineNumber,
110
+ undefined,
111
+ context,
112
+ // @ts-ignore
113
+ range
114
+ );
82
115
  }
83
- addError(onError, lineNumber, undefined, context, range);
84
116
  }
85
117
  });
86
118
  }
package/lib/rules.js CHANGED
@@ -58,6 +58,6 @@ for (const rule of rules) {
58
58
  const name = rule.names[0].toLowerCase();
59
59
  // eslint-disable-next-line dot-notation
60
60
  rule["information"] =
61
- new URL(`${homepage}/blob/v${version}/doc/Rules.md#${name}`);
61
+ new URL(`${homepage}/blob/v${version}/doc/${name}.md`);
62
62
  }
63
63
  module.exports = rules;
package/package.json CHANGED
@@ -1,12 +1,16 @@
1
1
  {
2
2
  "name": "markdownlint",
3
- "version": "0.26.2",
3
+ "version": "0.27.0",
4
4
  "description": "A Node.js style checker and lint tool for Markdown/CommonMark files.",
5
5
  "type": "commonjs",
6
6
  "main": "./lib/markdownlint.js",
7
7
  "exports": {
8
8
  ".": "./lib/markdownlint.js",
9
- "./helpers": "./helpers/helpers.js"
9
+ "./helpers": "./helpers/helpers.js",
10
+ "./style/all": "./style/all.json",
11
+ "./style/cirosantilli": "./style/cirosantilli.json",
12
+ "./style/prettier": "./style/prettier.json",
13
+ "./style/relaxed": "./style/relaxed.json"
10
14
  },
11
15
  "types": "./lib/markdownlint.d.ts",
12
16
  "author": "David Anson (https://dlaa.me/)",
@@ -23,24 +27,28 @@
23
27
  "build-config-schema": "node schema/build-config-schema.js",
24
28
  "build-declaration": "tsc --allowJs --declaration --emitDeclarationOnly --module commonjs --resolveJsonModule --target es2015 lib/markdownlint.js && node scripts delete 'lib/{c,md,r}*.d.ts' 'helpers/*.d.ts'",
25
29
  "build-demo": "node scripts copy node_modules/markdown-it/dist/markdown-it.min.js demo/markdown-it.min.js && cd demo && webpack --no-stats",
30
+ "build-docs": "node doc-build/build-rules.mjs",
26
31
  "build-example": "npm install --no-save --ignore-scripts grunt grunt-cli gulp through2",
27
- "ci": "npm-run-all --continue-on-error --parallel build-config lint serial-declaration-demo test-cover && git diff --exit-code",
32
+ "ci": "npm-run-all --continue-on-error --parallel lint serial-config-docs serial-declaration-demo test-cover && git diff --exit-code",
33
+ "clone-test-repos-apache-airflow": "cd test-repos && git clone https://github.com/apache/airflow apache-airflow --depth 1 --no-tags --quiet",
28
34
  "clone-test-repos-dotnet-docs": "cd test-repos && git clone https://github.com/dotnet/docs dotnet-docs --depth 1 --no-tags --quiet",
35
+ "clone-test-repos-electron-electron": "cd test-repos && git clone https://github.com/electron/electron electron-electron --depth 1 --no-tags --quiet",
29
36
  "clone-test-repos-eslint-eslint": "cd test-repos && git clone https://github.com/eslint/eslint eslint-eslint --depth 1 --no-tags --quiet",
37
+ "clone-test-repos-mdn-content": "cd test-repos && git clone https://github.com/mdn/content mdn-content --depth 1 --no-tags --quiet",
30
38
  "clone-test-repos-mkdocs-mkdocs": "cd test-repos && git clone https://github.com/mkdocs/mkdocs mkdocs-mkdocs --depth 1 --no-tags --quiet",
31
39
  "clone-test-repos-mochajs-mocha": "cd test-repos && git clone https://github.com/mochajs/mocha mochajs-mocha --depth 1 --no-tags --quiet",
32
40
  "clone-test-repos-pi-hole-docs": "cd test-repos && git clone https://github.com/pi-hole/docs pi-hole-docs --depth 1 --no-tags --quiet",
33
41
  "clone-test-repos-v8-v8-dev": "cd test-repos && git clone https://github.com/v8/v8.dev v8-v8-dev --depth 1 --no-tags --quiet",
34
42
  "clone-test-repos-webhintio-hint": "cd test-repos && git clone https://github.com/webhintio/hint webhintio-hint --depth 1 --no-tags --quiet",
35
43
  "clone-test-repos-webpack-webpack-js-org": "cd test-repos && git clone https://github.com/webpack/webpack.js.org webpack-webpack-js-org --depth 1 --no-tags --quiet",
36
- "clone-test-repos": "mkdir test-repos && cd test-repos && npm run clone-test-repos-eslint-eslint && npm run clone-test-repos-mkdocs-mkdocs && npm run clone-test-repos-mochajs-mocha && npm run clone-test-repos-pi-hole-docs && npm run clone-test-repos-webhintio-hint && npm run clone-test-repos-webpack-webpack-js-org",
37
- "clone-test-repos-large": "npm run clone-test-repos && cd test-repos && npm run clone-test-repos-dotnet-docs && npm run clone-test-repos-v8-v8-dev",
44
+ "clone-test-repos": "mkdir test-repos && cd test-repos && npm run clone-test-repos-apache-airflow && npm run clone-test-repos-dotnet-docs && npm run clone-test-repos-electron-electron && npm run clone-test-repos-eslint-eslint && npm run clone-test-repos-mdn-content && npm run clone-test-repos-mkdocs-mkdocs && npm run clone-test-repos-mochajs-mocha && npm run clone-test-repos-pi-hole-docs && npm run clone-test-repos-v8-v8-dev && npm run clone-test-repos-webhintio-hint && npm run clone-test-repos-webpack-webpack-js-org",
38
45
  "declaration": "npm run build-declaration && npm run test-declaration",
39
46
  "example": "cd example && node standalone.js && grunt markdownlint --force && gulp markdownlint",
40
47
  "docker-npm-install": "docker run --rm --tty --name npm-install --volume $PWD:/home/workdir --workdir /home/workdir --user node node:16 npm install",
41
48
  "docker-npm-run-upgrade": "docker run --rm --tty --name npm-run-upgrade --volume $PWD:/home/workdir --workdir /home/workdir --user node node:16 npm run upgrade",
42
- "lint": "eslint --max-warnings 0 .",
49
+ "lint": "eslint --ext .js,.mjs --max-warnings 0 .",
43
50
  "lint-test-repos": "ava --timeout=5m test/markdownlint-test-repos.js",
51
+ "serial-config-docs": "npm run build-config && npm run build-docs",
44
52
  "serial-declaration-demo": "npm run build-declaration && npm-run-all --continue-on-error --parallel build-demo test-declaration",
45
53
  "test": "ava test/markdownlint-test.js test/markdownlint-test-config.js test/markdownlint-test-custom-rules.js test/markdownlint-test-helpers.js test/markdownlint-test-result-object.js test/markdownlint-test-scenarios.js",
46
54
  "test-cover": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 npm test",
@@ -50,35 +58,37 @@
50
58
  "upgrade": "npx --yes npm-check-updates --upgrade"
51
59
  },
52
60
  "engines": {
53
- "node": ">=14"
61
+ "node": ">=14.18.0"
54
62
  },
55
63
  "dependencies": {
56
64
  "markdown-it": "13.0.1"
57
65
  },
58
66
  "devDependencies": {
59
- "ava": "4.3.0",
60
- "c8": "7.11.3",
61
- "eslint": "8.18.0",
67
+ "ava": "5.1.0",
68
+ "c8": "7.12.0",
69
+ "eslint": "8.30.0",
62
70
  "eslint-plugin-es": "4.1.0",
63
- "eslint-plugin-jsdoc": "39.3.3",
64
- "eslint-plugin-node": "11.1.0",
65
- "eslint-plugin-unicorn": "42.0.0",
66
- "globby": "13.1.2",
71
+ "eslint-plugin-jsdoc": "39.6.4",
72
+ "eslint-plugin-n": "15.6.0",
73
+ "eslint-plugin-regexp": "1.11.0",
74
+ "eslint-plugin-unicorn": "45.0.2",
75
+ "globby": "13.1.3",
67
76
  "js-yaml": "4.1.0",
77
+ "markdown-it-footnote": "3.0.3",
68
78
  "markdown-it-for-inline": "0.1.1",
69
79
  "markdown-it-sub": "1.0.0",
70
80
  "markdown-it-sup": "1.0.0",
71
81
  "markdown-it-texmath": "1.0.0",
72
- "markdownlint-rule-github-internal-links": "0.1.0",
73
- "markdownlint-rule-helpers": "0.16.0",
82
+ "markdownlint-rule-helpers": "0.17.2",
74
83
  "npm-run-all": "4.1.5",
75
- "strip-json-comments": "4.0.0",
84
+ "strip-json-comments": "5.0.0",
85
+ "terser-webpack-plugin": "5.3.6",
76
86
  "toml": "3.0.0",
77
- "ts-loader": "9.3.0",
87
+ "ts-loader": "9.4.2",
78
88
  "tv4": "1.3.0",
79
- "typescript": "4.7.4",
80
- "webpack": "5.73.0",
81
- "webpack-cli": "4.10.0"
89
+ "typescript": "4.9.4",
90
+ "webpack": "5.75.0",
91
+ "webpack-cli": "5.0.1"
82
92
  },
83
93
  "keywords": [
84
94
  "markdown",
@@ -139,7 +139,7 @@
139
139
 
140
140
  // MD026/no-trailing-punctuation - Trailing punctuation in heading
141
141
  "MD026": {
142
- // Punctuation characters
142
+ // Punctuation characters not allowed at end of headings
143
143
  "punctuation": ".,;:!。,;:!"
144
144
  },
145
145
 
@@ -207,7 +207,12 @@
207
207
  "MD039": true,
208
208
 
209
209
  // MD040/fenced-code-language - Fenced code blocks should have a language specified
210
- "MD040": true,
210
+ "MD040": {
211
+ // List of languages
212
+ "allowed_languages": [],
213
+ // Require language only
214
+ "language_only": false
215
+ },
211
216
 
212
217
  // MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading
213
218
  "MD041": {
@@ -225,7 +230,9 @@
225
230
  // List of headings
226
231
  "headings": [],
227
232
  // List of headings
228
- "headers": []
233
+ "headers": [],
234
+ // Match case of headings
235
+ "match_case": false
229
236
  },
230
237
 
231
238
  // MD044/proper-names - Proper names should have the correct capitalization