markdownlint 0.23.0 → 0.25.1

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.
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
+ const startIndent = Number(params.config.start_indent || indent);
16
17
  flattenedLists().forEach((list) => {
17
18
  if (list.unordered && list.parentsUnordered) {
18
19
  list.items.forEach((item) => {
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;
package/lib/md010.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addError, forEachLine } = require("../helpers");
6
- const { lineMetadata } = require("./cache");
5
+ const { addError, forEachLine, overlapsAnyRange } = require("../helpers");
6
+ const { codeBlockAndSpanRanges, lineMetadata } = require("./cache");
7
7
 
8
8
  const tabRe = /\t+/g;
9
9
 
@@ -13,24 +13,33 @@ module.exports = {
13
13
  "tags": [ "whitespace", "hard_tab" ],
14
14
  "function": function MD010(params, onError) {
15
15
  const codeBlocks = params.config.code_blocks;
16
- const includeCodeBlocks = (codeBlocks === undefined) ? true : !!codeBlocks;
16
+ const includeCode = (codeBlocks === undefined) ? true : !!codeBlocks;
17
+ const spacesPerTab = params.config.spaces_per_tab;
18
+ const spaceMultiplier = (spacesPerTab === undefined) ?
19
+ 1 :
20
+ Math.max(0, Number(spacesPerTab));
21
+ const exclusions = includeCode ? [] : codeBlockAndSpanRanges();
17
22
  forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
18
- if (!inCode || includeCodeBlocks) {
23
+ if (includeCode || !inCode) {
19
24
  let match = null;
20
25
  while ((match = tabRe.exec(line)) !== null) {
21
- const column = match.index + 1;
26
+ const { index } = match;
27
+ const column = index + 1;
22
28
  const length = match[0].length;
23
- addError(
24
- onError,
25
- lineIndex + 1,
26
- "Column: " + column,
27
- null,
28
- [ column, length ],
29
- {
30
- "editColumn": column,
31
- "deleteCount": length,
32
- "insertText": "".padEnd(length)
33
- });
29
+ if (!overlapsAnyRange(exclusions, lineIndex, index, length)) {
30
+ addError(
31
+ onError,
32
+ lineIndex + 1,
33
+ "Column: " + column,
34
+ null,
35
+ [ column, length ],
36
+ {
37
+ "editColumn": column,
38
+ "deleteCount": length,
39
+ "insertText": "".padEnd(length * spaceMultiplier)
40
+ }
41
+ );
42
+ }
34
43
  }
35
44
  }
36
45
  });
package/lib/md011.js CHANGED
@@ -2,40 +2,44 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addError, forEachInlineChild, unescapeMarkdown } =
6
- require("../helpers");
5
+ const { addError, forEachLine, overlapsAnyRange } = require("../helpers");
6
+ const { codeBlockAndSpanRanges, lineMetadata } = require("./cache");
7
7
 
8
- const reversedLinkRe = /\(([^)]+)\)\[([^\]^][^\]]*)]/g;
8
+ const reversedLinkRe =
9
+ /(^|[^\\])\(([^)]+)\)\[([^\]^][^\]]*)](?!\()/g;
9
10
 
10
11
  module.exports = {
11
12
  "names": [ "MD011", "no-reversed-links" ],
12
13
  "description": "Reversed link syntax",
13
14
  "tags": [ "links" ],
14
15
  "function": function MD011(params, onError) {
15
- forEachInlineChild(params, "text", (token) => {
16
- const { lineNumber, content } = token;
17
- let match = null;
18
- while ((match = reversedLinkRe.exec(content)) !== null) {
19
- const [ reversedLink, linkText, linkDestination ] = match;
20
- const line = params.lines[lineNumber - 1];
21
- const column = unescapeMarkdown(line).indexOf(reversedLink) + 1;
22
- const length = reversedLink.length;
23
- const range = column ? [ column, length ] : null;
24
- const fixInfo = column ?
25
- {
26
- "editColumn": column,
27
- "deleteCount": length,
28
- "insertText": `[${linkText}](${linkDestination})`
29
- } :
30
- null;
31
- addError(
32
- onError,
33
- lineNumber,
34
- reversedLink,
35
- null,
36
- range,
37
- fixInfo
38
- );
16
+ const exclusions = codeBlockAndSpanRanges();
17
+ forEachLine(lineMetadata(), (line, lineIndex, inCode, onFence) => {
18
+ if (!inCode && !onFence) {
19
+ let match = null;
20
+ while ((match = reversedLinkRe.exec(line)) !== null) {
21
+ const [ reversedLink, preChar, linkText, linkDestination ] = match;
22
+ const index = match.index + preChar.length;
23
+ const length = match[0].length - preChar.length;
24
+ if (
25
+ !linkText.endsWith("\\") &&
26
+ !linkDestination.endsWith("\\") &&
27
+ !overlapsAnyRange(exclusions, lineIndex, index, length)
28
+ ) {
29
+ addError(
30
+ onError,
31
+ lineIndex + 1,
32
+ reversedLink.slice(preChar.length),
33
+ null,
34
+ [ index + 1, length ],
35
+ {
36
+ "editColumn": index + 1,
37
+ "deleteCount": length,
38
+ "insertText": `[${linkText}](${linkDestination})`
39
+ }
40
+ );
41
+ }
42
+ }
39
43
  }
40
44
  });
41
45
  }
package/lib/md033.js CHANGED
@@ -2,12 +2,13 @@
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, overlapsAnyRange, unescapeMarkdown
7
+ } = require("../helpers");
8
+ const { codeBlockAndSpanRanges, lineMetadata } = require("./cache");
7
9
 
8
10
  const htmlElementRe = /<(([A-Za-z][A-Za-z0-9-]*)(?:\s[^>]*)?)\/?>/g;
9
11
  const linkDestinationRe = /]\(\s*$/;
10
- const inlineCodeRe = /^[^`]*(`+[^`]+`+[^`]+)*`+[^`]*$/;
11
12
  // See https://spec.commonmark.org/0.29/#autolinks
12
13
  const emailAddressRe =
13
14
  // eslint-disable-next-line max-len
@@ -21,19 +22,22 @@ module.exports = {
21
22
  let allowedElements = params.config.allowed_elements;
22
23
  allowedElements = Array.isArray(allowedElements) ? allowedElements : [];
23
24
  allowedElements = allowedElements.map((element) => element.toLowerCase());
25
+ const exclusions = codeBlockAndSpanRanges();
24
26
  forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
25
27
  let match = null;
26
28
  // eslint-disable-next-line no-unmodified-loop-condition
27
29
  while (!inCode && ((match = htmlElementRe.exec(line)) !== null)) {
28
30
  const [ tag, content, element ] = match;
29
- if (!allowedElements.includes(element.toLowerCase()) &&
31
+ if (
32
+ !allowedElements.includes(element.toLowerCase()) &&
30
33
  !tag.endsWith("\\>") &&
31
- !emailAddressRe.test(content)) {
34
+ !emailAddressRe.test(content) &&
35
+ !overlapsAnyRange(exclusions, lineIndex, match.index, match[0].length)
36
+ ) {
32
37
  const prefix = line.substring(0, match.index);
33
- if (!linkDestinationRe.test(prefix) && !inlineCodeRe.test(prefix)) {
38
+ if (!linkDestinationRe.test(prefix)) {
34
39
  const unescaped = unescapeMarkdown(prefix + "<", "_");
35
- if (!unescaped.endsWith("_") &&
36
- ((unescaped + "`").match(/`/g).length % 2)) {
40
+ if (!unescaped.endsWith("_")) {
37
41
  addError(onError, lineIndex + 1, "Element: " + element,
38
42
  null, [ match.index + 1, tag.length ]);
39
43
  }
package/lib/md035.js CHANGED
@@ -10,12 +10,12 @@ module.exports = {
10
10
  "tags": [ "hr" ],
11
11
  "function": function MD035(params, onError) {
12
12
  let style = String(params.config.style || "consistent");
13
- filterTokens(params, "hr", function forToken(token) {
14
- const lineTrim = token.line.trim();
13
+ filterTokens(params, "hr", (token) => {
14
+ const { lineNumber, markup } = token;
15
15
  if (style === "consistent") {
16
- style = lineTrim;
16
+ style = markup;
17
17
  }
18
- addErrorDetailIf(onError, token.lineNumber, style, lineTrim);
18
+ addErrorDetailIf(onError, lineNumber, style, markup);
19
19
  });
20
20
  }
21
21
  };
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+$/;
@@ -78,22 +79,35 @@ module.exports = {
78
79
  lineMetadata(),
79
80
  (line, lineIndex, inCode, onFence, inTable, inItem, onBreak, inMath) => {
80
81
  const onItemStart = (inItem === 1);
81
- if (inCode || inTable || onBreak || onItemStart || isBlankLine(line)) {
82
+ if (
83
+ inCode ||
84
+ onFence ||
85
+ inTable ||
86
+ onBreak ||
87
+ onItemStart ||
88
+ isBlankLine(line)
89
+ ) {
82
90
  // Emphasis resets when leaving a block
83
91
  resetRunTracking();
84
92
  }
85
- if (inCode || onBreak || inMath) {
93
+ if (
94
+ inCode ||
95
+ onFence ||
96
+ onBreak ||
97
+ inMath
98
+ ) {
86
99
  // Emphasis has no meaning here
87
100
  return;
88
101
  }
102
+ let patchedLine = line.replace(embeddedUnderscoreRe, "$1 $2");
89
103
  if (onItemStart) {
90
104
  // Trim overlapping '*' list item marker
91
- line = line.replace(asteriskListItemMarkerRe, "$1 $2");
105
+ patchedLine = patchedLine.replace(asteriskListItemMarkerRe, "$1 $2");
92
106
  }
93
107
  let match = null;
94
108
  // Match all emphasis-looking runs in the line...
95
- while ((match = emphasisRe.exec(line))) {
96
- const ignoreMarkersForLine = ignoreMarkersByLine[lineIndex] || [];
109
+ while ((match = emphasisRe.exec(patchedLine))) {
110
+ const ignoreMarkersForLine = ignoreMarkersByLine[lineIndex];
97
111
  const matchIndex = match.index + match[1].length;
98
112
  if (ignoreMarkersForLine.includes(matchIndex)) {
99
113
  // Ignore emphasis markers inside code spans and links
package/lib/md039.js CHANGED
@@ -4,7 +4,8 @@
4
4
 
5
5
  const { addErrorContext, filterTokens } = require("../helpers");
6
6
 
7
- const spaceInLinkRe = /\[(?:\s+(?:[^\]]*?)\s*|(?:[^\]]*?)\s+)](?=\(\S*\))/;
7
+ const spaceInLinkRe =
8
+ /\[(?:\s+(?:[^\]]*?)\s*|(?:[^\]]*?)\s+)](?=((?:\([^)]*\))|(?:\[[^\]]*\])))/;
8
9
 
9
10
  module.exports = {
10
11
  "names": [ "MD039", "no-space-in-links" ],
package/lib/md043.js CHANGED
@@ -20,7 +20,6 @@ module.exports = {
20
20
  let matchAny = false;
21
21
  let hasError = false;
22
22
  let anyHeadings = false;
23
- // eslint-disable-next-line func-style
24
23
  const getExpected = () => requiredHeadings[i++] || "[None]";
25
24
  forEachHeading(params, (heading, content) => {
26
25
  if (!hasError) {
@@ -28,8 +27,11 @@ module.exports = {
28
27
  const actual = levels[heading.tag] + " " + content;
29
28
  const expected = getExpected();
30
29
  if (expected === "*") {
31
- matchAny = true;
32
- getExpected();
30
+ const nextExpected = getExpected();
31
+ if (nextExpected.toLowerCase() !== actual.toLowerCase()) {
32
+ matchAny = true;
33
+ i--;
34
+ }
33
35
  } else if (expected === "+") {
34
36
  matchAny = true;
35
37
  } else if (expected.toLowerCase() === actual.toLowerCase()) {
@@ -43,9 +45,11 @@ module.exports = {
43
45
  }
44
46
  }
45
47
  });
48
+ const extraHeadings = requiredHeadings.length - i;
46
49
  if (
47
50
  !hasError &&
48
- (i < requiredHeadings.length) &&
51
+ ((extraHeadings > 1) ||
52
+ ((extraHeadings === 1) && (requiredHeadings[i] !== "*"))) &&
49
53
  (anyHeadings || !requiredHeadings.every((heading) => heading === "*"))
50
54
  ) {
51
55
  addErrorContext(onError, params.lines.length,
package/lib/md044.js CHANGED
@@ -2,11 +2,9 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorDetailIf, bareUrlRe, escapeForRegExp, filterTokens,
6
- forEachInlineChild, newLineRe } = require("../helpers");
7
-
8
- const startNonWordRe = /^\W/;
9
- const endNonWordRe = /\W$/;
5
+ const { addErrorDetailIf, bareUrlRe, escapeForRegExp, forEachLine,
6
+ overlapsAnyRange, linkRe, linkReferenceRe } = require("../helpers");
7
+ const { codeBlockAndSpanRanges, lineMetadata } = require("./cache");
10
8
 
11
9
  module.exports = {
12
10
  "names": [ "MD044", "proper-names" ],
@@ -15,80 +13,65 @@ module.exports = {
15
13
  "function": function MD044(params, onError) {
16
14
  let names = params.config.names;
17
15
  names = Array.isArray(names) ? names : [];
16
+ names.sort((a, b) => (b.length - a.length) || a.localeCompare(b));
18
17
  const codeBlocks = params.config.code_blocks;
19
18
  const includeCodeBlocks = (codeBlocks === undefined) ? true : !!codeBlocks;
20
- // Text of automatic hyperlinks is implicitly a URL
21
- const autolinkText = new Set();
22
- filterTokens(params, "inline", (token) => {
23
- let inAutoLink = false;
24
- token.children.forEach((child) => {
25
- const { info, type } = child;
26
- if ((type === "link_open") && (info === "auto")) {
27
- inAutoLink = true;
28
- } else if (type === "link_close") {
29
- inAutoLink = false;
30
- } else if ((type === "text") && inAutoLink) {
31
- autolinkText.add(child);
19
+ const exclusions = [];
20
+ forEachLine(lineMetadata(), (line, lineIndex) => {
21
+ if (linkReferenceRe.test(line)) {
22
+ exclusions.push([ lineIndex, 0, line.length ]);
23
+ } else {
24
+ let match = null;
25
+ while ((match = bareUrlRe.exec(line)) !== null) {
26
+ exclusions.push([ lineIndex, match.index, match[0].length ]);
32
27
  }
33
- });
28
+ while ((match = linkRe.exec(line)) !== null) {
29
+ const [ , text, destination ] = match;
30
+ if (destination) {
31
+ exclusions.push(
32
+ [ lineIndex, match.index + text.length, destination.length ]
33
+ );
34
+ }
35
+ }
36
+ }
34
37
  });
35
- // For each proper name...
36
- names.forEach((name) => {
38
+ if (!includeCodeBlocks) {
39
+ exclusions.push(...codeBlockAndSpanRanges());
40
+ }
41
+ for (const name of names) {
37
42
  const escapedName = escapeForRegExp(name);
38
- const startNamePattern = startNonWordRe.test(name) ? "" : "\\S*\\b";
39
- const endNamePattern = endNonWordRe.test(name) ? "" : "\\b\\S*";
43
+ const startNamePattern = /^\W/.test(name) ? "" : "\\b_*";
44
+ const endNamePattern = /\W$/.test(name) ? "" : "_*\\b";
40
45
  const namePattern =
41
- `(${startNamePattern})(${escapedName})(${endNamePattern})`;
42
- const anyNameRe = new RegExp(namePattern, "gi");
43
- // eslint-disable-next-line jsdoc/require-jsdoc
44
- function forToken(token) {
45
- if (!autolinkText.has(token)) {
46
- const fenceOffset = (token.type === "fence") ? 1 : 0;
47
- token.content.split(newLineRe).forEach((line, index) => {
48
- let match = null;
49
- while ((match = anyNameRe.exec(line)) !== null) {
50
- const [ fullMatch, leftMatch, nameMatch, rightMatch ] = match;
51
- if (fullMatch.search(bareUrlRe) === -1) {
52
- const wordMatch = fullMatch
53
- .replace(new RegExp(`^\\W{0,${leftMatch.length}}`), "")
54
- .replace(new RegExp(`\\W{0,${rightMatch.length}}$`), "");
55
- if (!names.includes(wordMatch)) {
56
- const lineNumber = token.lineNumber + index + fenceOffset;
57
- const fullLine = params.lines[lineNumber - 1];
58
- const matchLength = wordMatch.length;
59
- const matchIndex = fullLine.indexOf(wordMatch);
60
- const range = (matchIndex === -1) ?
61
- null :
62
- [ matchIndex + 1, matchLength ];
63
- const fixInfo = (matchIndex === -1) ?
64
- null :
65
- {
66
- "editColumn": matchIndex + 1,
67
- "deleteCount": matchLength,
68
- "insertText": name
69
- };
70
- addErrorDetailIf(
71
- onError,
72
- lineNumber,
73
- name,
74
- nameMatch,
75
- null,
76
- null,
77
- range,
78
- fixInfo
79
- );
46
+ `(${startNamePattern})(${escapedName})${endNamePattern}`;
47
+ const nameRe = new RegExp(namePattern, "gi");
48
+ forEachLine(lineMetadata(), (line, lineIndex, inCode, onFence) => {
49
+ if (includeCodeBlocks || (!inCode && !onFence)) {
50
+ let match = null;
51
+ while ((match = nameRe.exec(line)) !== null) {
52
+ const [ , leftMatch, nameMatch ] = match;
53
+ const index = match.index + leftMatch.length;
54
+ const length = nameMatch.length;
55
+ if (!overlapsAnyRange(exclusions, lineIndex, index, length)) {
56
+ addErrorDetailIf(
57
+ onError,
58
+ lineIndex + 1,
59
+ name,
60
+ nameMatch,
61
+ null,
62
+ null,
63
+ [ index + 1, length ],
64
+ {
65
+ "editColumn": index + 1,
66
+ "deleteCount": length,
67
+ "insertText": name
80
68
  }
81
- }
69
+ );
82
70
  }
83
- });
71
+ exclusions.push([ lineIndex, index, length ]);
72
+ }
84
73
  }
85
- }
86
- forEachInlineChild(params, "text", forToken);
87
- if (includeCodeBlocks) {
88
- forEachInlineChild(params, "code_inline", forToken);
89
- filterTokens(params, "code_block", forToken);
90
- filterTokens(params, "fence", forToken);
91
- }
92
- });
74
+ });
75
+ }
93
76
  }
94
77
  };
package/lib/md049.js ADDED
@@ -0,0 +1,45 @@
1
+ // @ts-check
2
+
3
+ "use strict";
4
+
5
+ const { addError, emphasisOrStrongStyleFor, forEachInlineChild,
6
+ getNextChildToken, getRangeAndFixInfoIfFound } = require("../helpers");
7
+
8
+ module.exports = {
9
+ "names": [ "MD049", "emphasis-style" ],
10
+ "description": "Emphasis style should be consistent",
11
+ "tags": [ "emphasis" ],
12
+ "function": function MD049(params, onError) {
13
+ let expectedStyle = String(params.config.style || "consistent");
14
+ forEachInlineChild(params, "em_open", (token, parent) => {
15
+ const { lineNumber, markup } = token;
16
+ const markupStyle = emphasisOrStrongStyleFor(markup);
17
+ if (expectedStyle === "consistent") {
18
+ expectedStyle = markupStyle;
19
+ }
20
+ if (expectedStyle !== markupStyle) {
21
+ let rangeAndFixInfo = {};
22
+ const contentToken = getNextChildToken(
23
+ parent, token, "text", "em_close"
24
+ );
25
+ if (contentToken) {
26
+ const { content } = contentToken;
27
+ const actual = `${markup}${content}${markup}`;
28
+ const expectedMarkup = (expectedStyle === "asterisk") ? "*" : "_";
29
+ const expected = `${expectedMarkup}${content}${expectedMarkup}`;
30
+ rangeAndFixInfo = getRangeAndFixInfoIfFound(
31
+ params.lines, lineNumber - 1, actual, expected
32
+ );
33
+ }
34
+ addError(
35
+ onError,
36
+ lineNumber,
37
+ `Expected: ${expectedStyle}; Actual: ${markupStyle}`,
38
+ null,
39
+ rangeAndFixInfo.range,
40
+ rangeAndFixInfo.fixInfo
41
+ );
42
+ }
43
+ });
44
+ }
45
+ };
package/lib/md050.js ADDED
@@ -0,0 +1,45 @@
1
+ // @ts-check
2
+
3
+ "use strict";
4
+
5
+ const { addError, emphasisOrStrongStyleFor, forEachInlineChild,
6
+ getNextChildToken, getRangeAndFixInfoIfFound } = require("../helpers");
7
+
8
+ module.exports = {
9
+ "names": [ "MD050", "strong-style" ],
10
+ "description": "Strong style should be consistent",
11
+ "tags": [ "emphasis" ],
12
+ "function": function MD050(params, onError) {
13
+ let expectedStyle = String(params.config.style || "consistent");
14
+ forEachInlineChild(params, "strong_open", (token, parent) => {
15
+ const { lineNumber, markup } = token;
16
+ const markupStyle = emphasisOrStrongStyleFor(markup);
17
+ if (expectedStyle === "consistent") {
18
+ expectedStyle = markupStyle;
19
+ }
20
+ if (expectedStyle !== markupStyle) {
21
+ let rangeAndFixInfo = {};
22
+ const contentToken = getNextChildToken(
23
+ parent, token, "text", "strong_close"
24
+ );
25
+ if (contentToken) {
26
+ const { content } = contentToken;
27
+ const actual = `${markup}${content}${markup}`;
28
+ const expectedMarkup = (expectedStyle === "asterisk") ? "**" : "__";
29
+ const expected = `${expectedMarkup}${content}${expectedMarkup}`;
30
+ rangeAndFixInfo = getRangeAndFixInfoIfFound(
31
+ params.lines, lineNumber - 1, actual, expected
32
+ );
33
+ }
34
+ addError(
35
+ onError,
36
+ lineNumber,
37
+ `Expected: ${expectedStyle}; Actual: ${markupStyle}`,
38
+ null,
39
+ rangeAndFixInfo.range,
40
+ rangeAndFixInfo.fixInfo
41
+ );
42
+ }
43
+ });
44
+ }
45
+ };
package/lib/rules.js CHANGED
@@ -2,9 +2,7 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const packageJson = require("../package.json");
6
- const homepage = packageJson.homepage;
7
- const version = packageJson.version;
5
+ const { homepage, version } = require("./constants");
8
6
 
9
7
  const rules = [
10
8
  require("./md001"),
@@ -50,7 +48,9 @@ const rules = [
50
48
  require("./md045"),
51
49
  require("./md046"),
52
50
  require("./md047"),
53
- require("./md048")
51
+ require("./md048"),
52
+ require("./md049"),
53
+ require("./md050")
54
54
  ];
55
55
  rules.forEach((rule) => {
56
56
  const name = rule.names[0].toLowerCase();