markdownlint 0.30.0 → 0.31.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.
@@ -520,7 +520,7 @@ function getEnabledRulesPerLineNumber(
520
520
  * @param {boolean} handleRuleFailures Whether to handle exceptions in rules.
521
521
  * @param {boolean} noInlineConfig Whether to allow inline configuration.
522
522
  * @param {number} resultVersion Version of the LintResults object to return.
523
- * @param {Function} callback Callback (err, result) function.
523
+ * @param {LintContentCallback} callback Callback (err, result) function.
524
524
  * @returns {void}
525
525
  */
526
526
  function lintContent(
@@ -809,7 +809,7 @@ function lintContent(
809
809
  * @param {number} resultVersion Version of the LintResults object to return.
810
810
  * @param {Object} fs File system implementation.
811
811
  * @param {boolean} synchronous Whether to execute synchronously.
812
- * @param {Function} callback Callback (err, result) function.
812
+ * @param {LintContentCallback} callback Callback (err, result) function.
813
813
  * @returns {void}
814
814
  */
815
815
  function lintFile(
@@ -859,7 +859,7 @@ function lintFile(
859
859
  *
860
860
  * @param {Options | null} options Options object.
861
861
  * @param {boolean} synchronous Whether to execute synchronously.
862
- * @param {Function} callback Callback (err, result) function.
862
+ * @param {LintCallback} callback Callback (err, result) function.
863
863
  * @returns {void}
864
864
  */
865
865
  function lintInput(options, synchronous, callback) {
@@ -1022,7 +1022,7 @@ function markdownlintPromise(options) {
1022
1022
  * @returns {LintResults} Results object.
1023
1023
  */
1024
1024
  function markdownlintSync(options) {
1025
- let results = {};
1025
+ let results = null;
1026
1026
  lintInput(options, true, function callback(error, res) {
1027
1027
  if (error) {
1028
1028
  throw error;
@@ -1176,6 +1176,7 @@ function readConfig(file, parsers, fs, callback) {
1176
1176
  }
1177
1177
  // Read file
1178
1178
  file = helpers.expandTildePath(file, require("node:os"));
1179
+ // eslint-disable-next-line n/prefer-promises/fs
1179
1180
  fs.readFile(file, "utf8", (err, content) => {
1180
1181
  if (err) {
1181
1182
  // @ts-ignore
@@ -1416,12 +1417,21 @@ module.exports = markdownlint;
1416
1417
  * @property {string} [insertText] Text to insert (after deleting).
1417
1418
  */
1418
1419
 
1420
+ /**
1421
+ * Called with the result of linting a string or document.
1422
+ *
1423
+ * @callback LintContentCallback
1424
+ * @param {Error | null} error Error iff failed.
1425
+ * @param {LintError[]} [result] Result iff successful.
1426
+ * @returns {void}
1427
+ */
1428
+
1419
1429
  /**
1420
1430
  * Called with the result of the lint function.
1421
1431
  *
1422
1432
  * @callback LintCallback
1423
- * @param {Error | null} err Error object or null.
1424
- * @param {LintResults} [results] Lint results.
1433
+ * @param {Error | null} error Error object iff failed.
1434
+ * @param {LintResults} [results] Lint results iff succeeded.
1425
1435
  * @returns {void}
1426
1436
  */
1427
1437
 
package/lib/md032.js CHANGED
@@ -4,8 +4,7 @@
4
4
 
5
5
  const { addErrorContext, blockquotePrefixRe, isBlankLine } =
6
6
  require("../helpers");
7
- const { filterByPredicate, flattenedChildren } =
8
- require("../helpers/micromark.cjs");
7
+ const { filterByPredicate } = require("../helpers/micromark.cjs");
9
8
 
10
9
  const nonContentTokens = new Set([
11
10
  "blockQuoteMarker",
@@ -47,7 +46,9 @@ module.exports = {
47
46
  const topLevelLists = filterByPredicate(
48
47
  parsers.micromark.tokens,
49
48
  isList,
50
- (token) => (isList(token) ? [] : token.children)
49
+ (token) => (
50
+ (isList(token) || (token.type === "htmlFlow")) ? [] : token.children
51
+ )
51
52
  );
52
53
  for (const list of topLevelLists) {
53
54
 
@@ -59,7 +60,11 @@ module.exports = {
59
60
 
60
61
  // Find the "visual" end of the list
61
62
  let endLine = list.endLine;
62
- for (const child of flattenedChildren(list).reverse()) {
63
+ const flattenedChildren = filterByPredicate(
64
+ list.children,
65
+ () => true
66
+ );
67
+ for (const child of flattenedChildren.reverse()) {
63
68
  if (!nonContentTokens.has(child.type)) {
64
69
  endLine = child.endLine;
65
70
  break;
package/lib/md033.js CHANGED
@@ -3,7 +3,7 @@
3
3
  "use strict";
4
4
 
5
5
  const { addError } = require("../helpers");
6
- const { filterByHtmlTokens, getHtmlTagInfo } =
6
+ const { filterByTypes, getHtmlTagInfo } =
7
7
  require("../helpers/micromark.cjs");
8
8
 
9
9
  const nextLinesRe = /[\r\n][\s\S]*$/;
@@ -16,7 +16,8 @@ module.exports = {
16
16
  let allowedElements = params.config.allowed_elements;
17
17
  allowedElements = Array.isArray(allowedElements) ? allowedElements : [];
18
18
  allowedElements = allowedElements.map((element) => element.toLowerCase());
19
- for (const token of filterByHtmlTokens(params.parsers.micromark.tokens)) {
19
+ const { tokens } = params.parsers.micromark;
20
+ for (const token of filterByTypes(tokens, [ "htmlText" ])) {
20
21
  const htmlTagInfo = getHtmlTagInfo(token);
21
22
  if (
22
23
  htmlTagInfo &&
package/lib/md034.js CHANGED
@@ -3,7 +3,7 @@
3
3
  "use strict";
4
4
 
5
5
  const { addErrorContext } = require("../helpers");
6
- const { filterByPredicate, getHtmlTagInfo, parse } =
6
+ const { filterByPredicate, filterByTypes, getHtmlTagInfo, parse } =
7
7
  require("../helpers/micromark.cjs");
8
8
 
9
9
  module.exports = {
@@ -11,42 +11,40 @@ module.exports = {
11
11
  "description": "Bare URL used",
12
12
  "tags": [ "links", "url" ],
13
13
  "function": function MD034(params, onError) {
14
- const literalAutolinks = (tokens) => (
15
- filterByPredicate(
16
- tokens,
17
- (token) => token.type === "literalAutolink",
18
- (token) => {
19
- const { children } = token;
20
- const result = [];
21
- for (let i = 0; i < children.length; i++) {
22
- const openToken = children[i];
23
- const openTagInfo = getHtmlTagInfo(openToken);
24
- if (openTagInfo && !openTagInfo.close) {
25
- let count = 1;
26
- for (let j = i + 1; j < children.length; j++) {
27
- const closeToken = children[j];
28
- const closeTagInfo = getHtmlTagInfo(closeToken);
29
- if (closeTagInfo && (openTagInfo.name === closeTagInfo.name)) {
30
- if (closeTagInfo.close) {
31
- count--;
32
- if (count === 0) {
33
- i = j;
34
- break;
35
- }
36
- } else {
37
- count++;
38
- }
14
+ const literalAutolinks = (tokens) => {
15
+ const flattened = filterByPredicate(tokens, () => true);
16
+ const result = [];
17
+ for (let i = 0; i < flattened.length; i++) {
18
+ const current = flattened[i];
19
+ const openTagInfo = getHtmlTagInfo(current);
20
+ if (openTagInfo && !openTagInfo.close) {
21
+ let count = 1;
22
+ for (let j = i + 1; j < flattened.length; j++) {
23
+ const candidate = flattened[j];
24
+ const closeTagInfo = getHtmlTagInfo(candidate);
25
+ if (closeTagInfo && (openTagInfo.name === closeTagInfo.name)) {
26
+ if (closeTagInfo.close) {
27
+ count--;
28
+ if (count === 0) {
29
+ i = j;
30
+ break;
39
31
  }
32
+ } else {
33
+ count++;
40
34
  }
41
- } else {
42
- result.push(openToken);
43
35
  }
44
36
  }
45
- return result;
37
+ } else {
38
+ result.push(current);
46
39
  }
47
- )
40
+ }
41
+ return result.filter((token) => token.type === "literalAutolink");
42
+ };
43
+ const autoLinks = filterByTypes(
44
+ params.parsers.micromark.tokens,
45
+ [ "literalAutolink" ]
48
46
  );
49
- if (literalAutolinks(params.parsers.micromark.tokens).length > 0) {
47
+ if (autoLinks.length > 0) {
50
48
  // Re-parse with correct link/image reference definition handling
51
49
  const document = params.lines.join("\n");
52
50
  const tokens = parse(document, undefined, false);
package/lib/md037.js CHANGED
@@ -3,9 +3,7 @@
3
3
  "use strict";
4
4
 
5
5
  const { addError } = require("../helpers");
6
-
7
- const emphasisStartTextRe = /^(\S{1,3})(\s+)\S/;
8
- const emphasisEndTextRe = /\S(\s+)(\S{1,3})$/;
6
+ const { filterByPredicate } = require("../helpers/micromark.cjs");
9
7
 
10
8
  module.exports = {
11
9
  "names": [ "MD037", "no-space-in-emphasis" ],
@@ -19,16 +17,11 @@ module.exports = {
19
17
  for (const marker of [ "_", "__", "___", "*", "**", "***" ]) {
20
18
  emphasisTokensByMarker.set(marker, []);
21
19
  }
22
- const pending = [ ...parsers.micromark.tokens ];
23
- let token = null;
24
- while ((token = pending.shift())) {
25
-
26
- // Use reparsed children of htmlFlow tokens
27
- if (token.type === "htmlFlow") {
28
- pending.unshift(...token.htmlFlowChildren);
29
- continue;
30
- }
31
- pending.push(...token.children);
20
+ const tokens = filterByPredicate(
21
+ parsers.micromark.tokens,
22
+ (token) => token.children.some((child) => child.type === "data")
23
+ );
24
+ for (const token of tokens) {
32
25
 
33
26
  // Build lists of bare tokens for each emphasis marker type
34
27
  for (const emphasisTokens of emphasisTokensByMarker.values()) {
@@ -45,51 +38,51 @@ module.exports = {
45
38
  }
46
39
 
47
40
  // Process bare tokens for each emphasis marker type
48
- for (const emphasisTokens of emphasisTokensByMarker.values()) {
41
+ for (const entry of emphasisTokensByMarker.entries()) {
42
+ const [ marker, emphasisTokens ] = entry;
49
43
  for (let i = 0; i + 1 < emphasisTokens.length; i += 2) {
50
44
 
51
45
  // Process start token of start/end pair
52
46
  const startToken = emphasisTokens[i];
53
- const startText =
54
- lines[startToken.startLine - 1].slice(startToken.startColumn - 1);
55
- const startMatch = startText.match(emphasisStartTextRe);
47
+ const startLine = lines[startToken.startLine - 1];
48
+ const startSlice = startLine.slice(startToken.endColumn - 1);
49
+ const startMatch = startSlice.match(/^\s+\S/);
56
50
  if (startMatch) {
57
- const [ startContext, startMarker, startSpaces ] = startMatch;
58
- if ((startMarker === startToken.text) && (startSpaces.length > 0)) {
59
- addError(
60
- onError,
61
- startToken.startLine,
62
- undefined,
63
- startContext,
64
- [ startToken.startColumn, startContext.length ],
65
- {
66
- "editColumn": startToken.endColumn,
67
- "deleteCount": startSpaces.length
68
- }
69
- );
70
- }
51
+ const [ startSpaceCharacter ] = startMatch;
52
+ const startContext = `${marker}${startSpaceCharacter}`;
53
+ addError(
54
+ onError,
55
+ startToken.startLine,
56
+ undefined,
57
+ startContext,
58
+ [ startToken.startColumn, startContext.length ],
59
+ {
60
+ "editColumn": startToken.endColumn,
61
+ "deleteCount": startSpaceCharacter.length - 1
62
+ }
63
+ );
71
64
  }
72
65
 
73
66
  // Process end token of start/end pair
74
67
  const endToken = emphasisTokens[i + 1];
75
- const endText =
76
- lines[endToken.startLine - 1].slice(0, endToken.endColumn - 1);
77
- const endMatch = endText.match(emphasisEndTextRe);
68
+ const endLine = lines[endToken.startLine - 1];
69
+ const endSlice = endLine.slice(0, endToken.startColumn - 1);
70
+ const endMatch = endSlice.match(/\S\s+$/);
78
71
  if (endMatch) {
79
- const [ endContext, endSpace, endMarker ] = endMatch;
80
- if ((endMarker === endToken.text) && (endSpace.length > 0)) {
81
- addError(
82
- onError,
83
- endToken.startLine,
84
- undefined,
85
- endContext,
86
- [ endToken.endColumn - endContext.length, endContext.length ],
87
- {
88
- "editColumn": endToken.startColumn - endSpace.length,
89
- "deleteCount": endSpace.length
90
- }
91
- );
92
- }
72
+ const [ endSpaceCharacter ] = endMatch;
73
+ const endContext = `${endSpaceCharacter}${marker}`;
74
+ addError(
75
+ onError,
76
+ endToken.startLine,
77
+ undefined,
78
+ endContext,
79
+ [ endToken.endColumn - endContext.length, endContext.length ],
80
+ {
81
+ "editColumn":
82
+ endToken.startColumn - (endSpaceCharacter.length - 1),
83
+ "deleteCount": endSpaceCharacter.length - 1
84
+ }
85
+ );
93
86
  }
94
87
  }
95
88
  }
package/lib/md043.js CHANGED
@@ -11,52 +11,54 @@ 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
+ if (!Array.isArray(requiredHeadings)) {
15
+ // Nothing to check; avoid doing any work
16
+ return;
17
+ }
14
18
  const matchCase = params.config.match_case || false;
15
- if (Array.isArray(requiredHeadings)) {
16
- const levels = {};
17
- for (const level of [ 1, 2, 3, 4, 5, 6 ]) {
18
- levels["h" + level] = "######".substr(-level);
19
- }
20
- let i = 0;
21
- let matchAny = false;
22
- let hasError = false;
23
- let anyHeadings = false;
24
- const getExpected = () => requiredHeadings[i++] || "[None]";
25
- const handleCase = (str) => (matchCase ? str : str.toLowerCase());
26
- forEachHeading(params, (heading, content) => {
27
- if (!hasError) {
28
- anyHeadings = true;
29
- const actual = levels[heading.tag] + " " + content;
30
- const expected = getExpected();
31
- if (expected === "*") {
32
- const nextExpected = getExpected();
33
- if (handleCase(nextExpected) !== handleCase(actual)) {
34
- matchAny = true;
35
- i--;
36
- }
37
- } else if (expected === "+") {
19
+ const levels = {};
20
+ for (const level of [ 1, 2, 3, 4, 5, 6 ]) {
21
+ levels["h" + level] = "######".substr(-level);
22
+ }
23
+ let i = 0;
24
+ let matchAny = false;
25
+ let hasError = false;
26
+ let anyHeadings = false;
27
+ const getExpected = () => requiredHeadings[i++] || "[None]";
28
+ const handleCase = (str) => (matchCase ? str : str.toLowerCase());
29
+ forEachHeading(params, (heading, content) => {
30
+ if (!hasError) {
31
+ anyHeadings = true;
32
+ const actual = levels[heading.tag] + " " + content;
33
+ const expected = getExpected();
34
+ if (expected === "*") {
35
+ const nextExpected = getExpected();
36
+ if (handleCase(nextExpected) !== handleCase(actual)) {
38
37
  matchAny = true;
39
- } else if (handleCase(expected) === handleCase(actual)) {
40
- matchAny = false;
41
- } else if (matchAny) {
42
38
  i--;
43
- } else {
44
- addErrorDetailIf(onError, heading.lineNumber,
45
- expected, actual);
46
- hasError = true;
47
39
  }
40
+ } else if (expected === "+") {
41
+ matchAny = true;
42
+ } else if (handleCase(expected) === handleCase(actual)) {
43
+ matchAny = false;
44
+ } else if (matchAny) {
45
+ i--;
46
+ } else {
47
+ addErrorDetailIf(onError, heading.lineNumber,
48
+ expected, actual);
49
+ hasError = true;
48
50
  }
49
- });
50
- const extraHeadings = requiredHeadings.length - i;
51
- if (
52
- !hasError &&
53
- ((extraHeadings > 1) ||
54
- ((extraHeadings === 1) && (requiredHeadings[i] !== "*"))) &&
55
- (anyHeadings || !requiredHeadings.every((heading) => heading === "*"))
56
- ) {
57
- addErrorContext(onError, params.lines.length,
58
- requiredHeadings[i]);
59
51
  }
52
+ });
53
+ const extraHeadings = requiredHeadings.length - i;
54
+ if (
55
+ !hasError &&
56
+ ((extraHeadings > 1) ||
57
+ ((extraHeadings === 1) && (requiredHeadings[i] !== "*"))) &&
58
+ (anyHeadings || !requiredHeadings.every((heading) => heading === "*"))
59
+ ) {
60
+ addErrorContext(onError, params.lines.length,
61
+ requiredHeadings[i]);
60
62
  }
61
63
  }
62
64
  };
package/lib/md044.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorDetailIf, escapeForRegExp, newLineRe, withinAnyRange } =
5
+ const { addErrorDetailIf, escapeForRegExp, withinAnyRange } =
6
6
  require("../helpers");
7
7
  const { filterByPredicate, filterByTypes, parse } =
8
8
  require("../helpers/micromark.cjs");
@@ -19,46 +19,32 @@ module.exports = {
19
19
  let names = params.config.names;
20
20
  names = Array.isArray(names) ? names : [];
21
21
  names.sort((a, b) => (b.length - a.length) || a.localeCompare(b));
22
+ if (names.length === 0) {
23
+ // Nothing to check; avoid doing any work
24
+ return;
25
+ }
22
26
  const codeBlocks = params.config.code_blocks;
23
27
  const includeCodeBlocks =
24
28
  (codeBlocks === undefined) ? true : !!codeBlocks;
25
29
  const htmlElements = params.config.html_elements;
26
30
  const includeHtmlElements =
27
31
  (htmlElements === undefined) ? true : !!htmlElements;
28
- const scannedTypes = new Set([ "data", "htmlFlowData" ]);
32
+ const scannedTypes = new Set([ "data" ]);
29
33
  if (includeCodeBlocks) {
30
34
  scannedTypes.add("codeFlowValue");
31
35
  scannedTypes.add("codeTextData");
32
36
  }
33
- const tokenAdjustments = new Map();
37
+ if (includeHtmlElements) {
38
+ scannedTypes.add("htmlFlowData");
39
+ scannedTypes.add("htmlTextData");
40
+ }
34
41
  const contentTokens =
35
42
  filterByPredicate(
36
43
  params.parsers.micromark.tokens,
37
44
  (token) => scannedTypes.has(token.type),
38
- (token) => {
39
- let { children } = token;
40
- const { startLine, text } = token;
41
- if (!includeHtmlElements && (token.type === "htmlFlow")) {
42
- if (text.startsWith("<!--")) {
43
- // Remove comment content
44
- children = [];
45
- } else {
46
- // Re-parse to get htmlText elements for detailed tokenization
47
- const htmlTextLines =
48
- `<md044>\n${text}\n</md044>`.split(newLineRe);
49
- children = parse(htmlTextLines.join(""));
50
- const reTokens = [ ...children ];
51
- for (const reToken of reTokens) {
52
- tokenAdjustments.set(reToken, {
53
- htmlTextLines,
54
- startLine
55
- });
56
- reTokens.push(...reToken.children);
57
- }
58
- }
59
- }
60
- return children.filter((t) => !ignoredChildTypes.has(t.type));
61
- }
45
+ (token) => (
46
+ token.children.filter((t) => !ignoredChildTypes.has(t.type))
47
+ )
62
48
  );
63
49
  const exclusions = [];
64
50
  const autoLinked = new Set();
@@ -96,22 +82,10 @@ module.exports = {
96
82
  autoLinked.add(token);
97
83
  }
98
84
  if (!withinAnyRange(urlRanges, lineIndex, index, length)) {
99
- let lineNumber = token.startLine;
100
- let column = index;
101
- if (tokenAdjustments.has(token)) {
102
- const { htmlTextLines, startLine } =
103
- tokenAdjustments.get(token);
104
- let lineDelta = 0;
105
- while (htmlTextLines[lineDelta].length <= column) {
106
- column -= htmlTextLines[lineDelta].length;
107
- lineDelta++;
108
- }
109
- lineNumber = startLine + lineDelta - 1;
110
- }
111
- column++;
85
+ const column = index + 1;
112
86
  addErrorDetailIf(
113
87
  onError,
114
- lineNumber,
88
+ token.startLine,
115
89
  name,
116
90
  nameMatch,
117
91
  null,
@@ -3,15 +3,18 @@
3
3
  "use strict";
4
4
 
5
5
  const { addError, emphasisOrStrongStyleFor } = require("../helpers");
6
- const { filterByTypes, tokenIfType } = require("../helpers/micromark.cjs");
6
+ const { filterByPredicate, tokenIfType } = require("../helpers/micromark.cjs");
7
7
 
8
8
  const intrawordRe = /\w/;
9
9
 
10
10
  const impl =
11
11
  (params, onError, type, asterisk, underline, style = "consistent") => {
12
12
  const { lines, parsers } = params;
13
- const emphasisTokens =
14
- filterByTypes(parsers.micromark.tokens, [ type ]);
13
+ const emphasisTokens = filterByPredicate(
14
+ parsers.micromark.tokens,
15
+ (token) => token.type === type,
16
+ (token) => ((token.type === "htmlFlow") ? [] : token.children)
17
+ );
15
18
  for (const token of emphasisTokens) {
16
19
  const { children } = token;
17
20
  const childType = `${type}Sequence`;