markdownlint 0.22.0 → 0.25.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.
package/lib/md002.js CHANGED
@@ -6,7 +6,7 @@ const { addErrorDetailIf } = require("../helpers");
6
6
 
7
7
  module.exports = {
8
8
  "names": [ "MD002", "first-heading-h1", "first-header-h1" ],
9
- "description": "First heading should be a top level heading",
9
+ "description": "First heading should be a top-level heading",
10
10
  "tags": [ "headings", "headers" ],
11
11
  "function": function MD002(params, onError) {
12
12
  const level = Number(params.config.level || 1);
package/lib/md004.js CHANGED
@@ -2,10 +2,22 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorDetailIf, listItemMarkerRe,
6
- rangeFromRegExp, unorderedListStyleFor } = require("../helpers");
5
+ const { addErrorDetailIf, listItemMarkerRe, unorderedListStyleFor } =
6
+ require("../helpers");
7
7
  const { flattenedLists } = require("./cache");
8
8
 
9
+ const expectedStyleToMarker = {
10
+ "dash": "-",
11
+ "plus": "+",
12
+ "asterisk": "*"
13
+ };
14
+ const differentItemStyle = {
15
+ "dash": "plus",
16
+ "plus": "asterisk",
17
+ "asterisk": "dash"
18
+ };
19
+ const validStyles = Object.keys(expectedStyleToMarker);
20
+
9
21
  module.exports = {
10
22
  "names": [ "MD004", "ul-style" ],
11
23
  "description": "Unordered list style",
@@ -23,19 +35,40 @@ module.exports = {
23
35
  const itemStyle = unorderedListStyleFor(item);
24
36
  if (style === "sublist") {
25
37
  const nesting = list.nesting;
26
- if (!nestingStyles[nesting] &&
27
- (itemStyle !== nestingStyles[nesting - 1])) {
28
- nestingStyles[nesting] = itemStyle;
29
- } else {
30
- addErrorDetailIf(onError, item.lineNumber,
31
- nestingStyles[nesting], itemStyle, null, null,
32
- rangeFromRegExp(item.line, listItemMarkerRe));
38
+ if (!nestingStyles[nesting]) {
39
+ nestingStyles[nesting] =
40
+ (itemStyle === nestingStyles[nesting - 1]) ?
41
+ differentItemStyle[itemStyle] :
42
+ itemStyle;
33
43
  }
34
- } else {
35
- addErrorDetailIf(onError, item.lineNumber,
36
- expectedStyle, itemStyle, null, null,
37
- rangeFromRegExp(item.line, listItemMarkerRe));
44
+ expectedStyle = nestingStyles[nesting];
45
+ }
46
+ if (!validStyles.includes(expectedStyle)) {
47
+ expectedStyle = validStyles[0];
48
+ }
49
+ let range = null;
50
+ let fixInfo = null;
51
+ const match = item.line.match(listItemMarkerRe);
52
+ if (match) {
53
+ const column = match.index + 1;
54
+ const length = match[0].length;
55
+ range = [ column, length ];
56
+ fixInfo = {
57
+ "editColumn": match[1].length + 1,
58
+ "deleteCount": 1,
59
+ "insertText": expectedStyleToMarker[expectedStyle]
60
+ };
38
61
  }
62
+ addErrorDetailIf(
63
+ onError,
64
+ item.lineNumber,
65
+ expectedStyle,
66
+ itemStyle,
67
+ null,
68
+ null,
69
+ range,
70
+ fixInfo
71
+ );
39
72
  });
40
73
  }
41
74
  });
package/lib/md006.js CHANGED
@@ -25,7 +25,7 @@ module.exports = {
25
25
  null,
26
26
  rangeFromRegExp(line, listItemMarkerRe),
27
27
  {
28
- "deleteCount": line.length - line.trimLeft().length
28
+ "deleteCount": line.length - line.trimStart().length
29
29
  });
30
30
  });
31
31
  }
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/md009.js CHANGED
@@ -49,26 +49,30 @@ module.exports = {
49
49
  const expected = (brSpaces < 2) ? 0 : brSpaces;
50
50
  forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
51
51
  const lineNumber = lineIndex + 1;
52
- const trailingSpaces = line.length - line.trimRight().length;
53
- if (trailingSpaces && !inCode &&
54
- !includesSorted(listItemLineNumbers, lineNumber)) {
55
- if ((expected !== trailingSpaces) ||
56
- (strict &&
57
- (!includesSorted(paragraphLineNumbers, lineNumber) ||
58
- includesSorted(codeInlineLineNumbers, lineNumber)))) {
59
- const column = line.length - trailingSpaces + 1;
60
- addError(
61
- onError,
62
- lineNumber,
63
- "Expected: " + (expected === 0 ? "" : "0 or ") +
64
- expected + "; Actual: " + trailingSpaces,
65
- null,
66
- [ column, trailingSpaces ],
67
- {
68
- "editColumn": column,
69
- "deleteCount": trailingSpaces
70
- });
71
- }
52
+ const trailingSpaces = line.length - line.trimEnd().length;
53
+ if (
54
+ trailingSpaces &&
55
+ !inCode &&
56
+ !includesSorted(listItemLineNumbers, lineNumber) &&
57
+ (
58
+ (expected !== trailingSpaces) ||
59
+ (strict &&
60
+ (!includesSorted(paragraphLineNumbers, lineNumber) ||
61
+ includesSorted(codeInlineLineNumbers, lineNumber)))
62
+ )
63
+ ) {
64
+ const column = line.length - trailingSpaces + 1;
65
+ addError(
66
+ onError,
67
+ lineNumber,
68
+ "Expected: " + (expected === 0 ? "" : "0 or ") +
69
+ expected + "; Actual: " + trailingSpaces,
70
+ null,
71
+ [ column, trailingSpaces ],
72
+ {
73
+ "editColumn": column,
74
+ "deleteCount": trailingSpaces
75
+ });
72
76
  }
73
77
  });
74
78
  }
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/md018.js CHANGED
@@ -12,7 +12,7 @@ module.exports = {
12
12
  "function": function MD018(params, onError) {
13
13
  forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
14
14
  if (!inCode &&
15
- /^#+[^#\s]/.test(line) &&
15
+ /^#+[^# \t]/.test(line) &&
16
16
  !/#\s*$/.test(line) &&
17
17
  !line.startsWith("#️⃣")) {
18
18
  const hashCount = /^#+/.exec(line)[0].length;
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 = /^(#+)(\s{2,})(?:\S)/.exec(line);
16
+ const match = /^(#+)([ \t]{2,})(?:\S)/.exec(line);
17
17
  if (match) {
18
18
  const [
19
19
  ,
package/lib/md020.js CHANGED
@@ -13,7 +13,7 @@ module.exports = {
13
13
  forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
14
14
  if (!inCode) {
15
15
  const match =
16
- /^(#+)(\s*)([^#]*?[^#\\])(\s*)((?:\\#)?)(#+)(\s*)$/.exec(line);
16
+ /^(#+)([ \t]*)([^#]*?[^#\\])([ \t]*)((?:\\#)?)(#+)(\s*)$/.exec(line);
17
17
  if (match) {
18
18
  const [
19
19
  ,
package/lib/md021.js CHANGED
@@ -13,7 +13,7 @@ module.exports = {
13
13
  filterTokens(params, "heading_open", (token) => {
14
14
  if (headingStyleFor(token) === "atx_closed") {
15
15
  const { line, lineNumber } = token;
16
- const match = /^(#+)(\s+)([^#]+?)(\s+)(#+)(\s*)$/.exec(line);
16
+ const match = /^(#+)([ \t]+)([^#]+?)([ \t]+)(#+)(\s*)$/.exec(line);
17
17
  if (match) {
18
18
  const [
19
19
  ,
package/lib/md023.js CHANGED
@@ -17,7 +17,7 @@ module.exports = {
17
17
  if (match) {
18
18
  const [ prefixAndFirstChar, prefix ] = match;
19
19
  let deleteCount = prefix.length;
20
- const prefixLengthNoSpace = prefix.trimRight().length;
20
+ const prefixLengthNoSpace = prefix.trimEnd().length;
21
21
  if (prefixLengthNoSpace) {
22
22
  deleteCount -= prefixLengthNoSpace - 1;
23
23
  }
package/lib/md025.js CHANGED
@@ -7,7 +7,7 @@ const { addErrorContext, filterTokens, frontMatterHasTitle } =
7
7
 
8
8
  module.exports = {
9
9
  "names": [ "MD025", "single-title", "single-h1" ],
10
- "description": "Multiple top level headings in the same document",
10
+ "description": "Multiple top-level headings in the same document",
11
11
  "tags": [ "headings", "headers" ],
12
12
  "function": function MD025(params, onError) {
13
13
  const level = Number(params.config.level || 1);
package/lib/md032.js CHANGED
@@ -17,7 +17,7 @@ module.exports = {
17
17
  const firstIndex = list.open.map[0];
18
18
  if (!isBlankLine(lines[firstIndex - 1])) {
19
19
  const line = lines[firstIndex];
20
- const quotePrefix = line.match(quotePrefixRe)[0].trimRight();
20
+ const quotePrefix = line.match(quotePrefixRe)[0].trimEnd();
21
21
  addErrorContext(
22
22
  onError,
23
23
  firstIndex + 1,
@@ -32,7 +32,7 @@ module.exports = {
32
32
  const lastIndex = list.lastLineIndex - 1;
33
33
  if (!isBlankLine(lines[lastIndex + 1])) {
34
34
  const line = lines[lastIndex];
35
- const quotePrefix = line.match(quotePrefixRe)[0].trimRight();
35
+ const quotePrefix = line.match(quotePrefixRe)[0].trimEnd();
36
36
  addErrorContext(
37
37
  onError,
38
38
  lastIndex + 1,
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,9 +7,11 @@ 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+$/;
14
+ const tablePipeRe = /\|/;
13
15
 
14
16
  module.exports = {
15
17
  "names": [ "MD037", "no-space-in-emphasis" ],
@@ -28,18 +30,23 @@ module.exports = {
28
30
  pendingError = null;
29
31
  }
30
32
  // eslint-disable-next-line jsdoc/require-jsdoc
31
- function handleRunEnd(line, lineIndex, contextLength, match, matchIndex) {
33
+ function handleRunEnd(
34
+ line, lineIndex, contextLength, match, matchIndex, inTable
35
+ ) {
32
36
  // Close current run
33
37
  let content = line.substring(emphasisIndex, matchIndex);
34
38
  if (!emphasisLength) {
35
- content = content.trimLeft();
39
+ content = content.trimStart();
36
40
  }
37
41
  if (!match) {
38
- content = content.trimRight();
42
+ content = content.trimEnd();
39
43
  }
40
44
  const leftSpace = leftSpaceRe.test(content);
41
45
  const rightSpace = rightSpaceRe.test(content);
42
- if (leftSpace || rightSpace) {
46
+ if (
47
+ (leftSpace || rightSpace) &&
48
+ (!inTable || !tablePipeRe.test(content))
49
+ ) {
43
50
  // Report the violation
44
51
  const contextStart = emphasisIndex - emphasisLength;
45
52
  const contextEnd = matchIndex + contextLength;
@@ -70,24 +77,37 @@ module.exports = {
70
77
  resetRunTracking();
71
78
  forEachLine(
72
79
  lineMetadata(),
73
- (line, lineIndex, inCode, onFence, inTable, inItem, onBreak) => {
80
+ (line, lineIndex, inCode, onFence, inTable, inItem, onBreak, inMath) => {
74
81
  const onItemStart = (inItem === 1);
75
- if (inCode || inTable || onBreak || onItemStart || isBlankLine(line)) {
82
+ if (
83
+ inCode ||
84
+ onFence ||
85
+ inTable ||
86
+ onBreak ||
87
+ onItemStart ||
88
+ isBlankLine(line)
89
+ ) {
76
90
  // Emphasis resets when leaving a block
77
91
  resetRunTracking();
78
92
  }
79
- if (inCode || onBreak) {
93
+ if (
94
+ inCode ||
95
+ onFence ||
96
+ onBreak ||
97
+ inMath
98
+ ) {
80
99
  // Emphasis has no meaning here
81
100
  return;
82
101
  }
102
+ let patchedLine = line.replace(embeddedUnderscoreRe, "$1 $2");
83
103
  if (onItemStart) {
84
104
  // Trim overlapping '*' list item marker
85
- line = line.replace(asteriskListItemMarkerRe, "$1 $2");
105
+ patchedLine = patchedLine.replace(asteriskListItemMarkerRe, "$1 $2");
86
106
  }
87
107
  let match = null;
88
108
  // Match all emphasis-looking runs in the line...
89
- while ((match = emphasisRe.exec(line))) {
90
- const ignoreMarkersForLine = ignoreMarkersByLine[lineIndex] || [];
109
+ while ((match = emphasisRe.exec(patchedLine))) {
110
+ const ignoreMarkersForLine = ignoreMarkersByLine[lineIndex];
91
111
  const matchIndex = match.index + match[1].length;
92
112
  if (ignoreMarkersForLine.includes(matchIndex)) {
93
113
  // Ignore emphasis markers inside code spans and links
@@ -106,12 +126,20 @@ module.exports = {
106
126
  if (matchLength === effectiveEmphasisLength) {
107
127
  // Ending an existing run, report any pending error
108
128
  if (pendingError) {
129
+ // @ts-ignore
109
130
  addErrorContext(...pendingError);
110
131
  pendingError = null;
111
132
  }
112
133
  const error = handleRunEnd(
113
- line, lineIndex, effectiveEmphasisLength, match, matchIndex);
134
+ line,
135
+ lineIndex,
136
+ effectiveEmphasisLength,
137
+ match,
138
+ matchIndex,
139
+ inTable
140
+ );
114
141
  if (error) {
142
+ // @ts-ignore
115
143
  addErrorContext(...error);
116
144
  }
117
145
  // Reset
@@ -139,7 +167,7 @@ module.exports = {
139
167
  }
140
168
  if (emphasisIndex !== -1) {
141
169
  pendingError = pendingError ||
142
- handleRunEnd(line, lineIndex, 0, null, line.length);
170
+ handleRunEnd(line, lineIndex, 0, null, line.length, inTable);
143
171
  // Adjust for pending run on new line
144
172
  emphasisIndex = 0;
145
173
  emphasisLength = 0;
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" ],
@@ -24,8 +25,8 @@ module.exports = {
24
25
  linkText = "";
25
26
  } else if (type === "link_close") {
26
27
  inLink = false;
27
- const left = linkText.trimLeft().length !== linkText.length;
28
- const right = linkText.trimRight().length !== linkText.length;
28
+ const left = linkText.trimStart().length !== linkText.length;
29
+ const right = linkText.trimEnd().length !== linkText.length;
29
30
  if (left || right) {
30
31
  const line = params.lines[lineNumber - 1];
31
32
  let range = null;
package/lib/md041.js CHANGED
@@ -6,7 +6,7 @@ const { addErrorContext, frontMatterHasTitle } = require("../helpers");
6
6
 
7
7
  module.exports = {
8
8
  "names": [ "MD041", "first-line-heading", "first-line-h1" ],
9
- "description": "First line in file should be a top level heading",
9
+ "description": "First line in a file should be a top-level heading",
10
10
  "tags": [ "headings", "headers" ],
11
11
  "function": function MD041(params, onError) {
12
12
  const level = Number(params.config.level || 1);
@@ -17,11 +17,22 @@ module.exports = {
17
17
  params.config.front_matter_title
18
18
  );
19
19
  if (!foundFrontMatterTitle) {
20
+ const htmlHeadingRe = new RegExp(`^<h${level}[ />]`, "i");
20
21
  params.tokens.every((token) => {
22
+ let isError = false;
21
23
  if (token.type === "html_block") {
22
- return true;
24
+ if (token.content.startsWith("<!--")) {
25
+ // Ignore leading HTML comments
26
+ return true;
27
+ } else if (!htmlHeadingRe.test(token.content)) {
28
+ // Something other than an HTML heading
29
+ isError = true;
30
+ }
31
+ } else if ((token.type !== "heading_open") || (token.tag !== tag)) {
32
+ // Something other than a Markdown heading
33
+ isError = true;
23
34
  }
24
- if ((token.type !== "heading_open") || (token.tag !== tag)) {
35
+ if (isError) {
25
36
  addErrorContext(onError, token.lineNumber, token.line);
26
37
  }
27
38
  return false;
package/lib/md043.js CHANGED
@@ -20,16 +20,18 @@ 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) {
27
26
  anyHeadings = true;
28
27
  const actual = levels[heading.tag] + " " + content;
29
- let expected = getExpected();
28
+ const expected = getExpected();
30
29
  if (expected === "*") {
31
- matchAny = true;
32
- expected = 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,